package OOXMLconvert;

import java.util.HashMap;
import java.util.Iterator;
import java.util.LinkedHashMap;
import java.util.List;
import org.jdom.Element;

/**
 *
 * @author ferou
 */
public class ElementTable {
	
	private boolean mergedCells[][]; // [col][row]
	private int cRows = 0;
	private int cCols = 0;
	private int row = -1;
	private int col = -1;
	private Element elTbl;

	/**
	 * Konštruktor triedy na prácu s tabuľkovým navigátorom.
	 * @param elTbl vstupný element s tabuľkou
	 */
	public ElementTable(Element elTbl) {
		setCounts(elTbl);
		this.elTbl = elTbl;
	}
	
	/**
	 * Funkcia nastaví počet riadkov a stĺpcov a tiež pripraví pole mergenutých buniek
	 * @param elTbl vstupný element s tabuľkou
	 */
	private void setCounts(Element elTbl) {
		try {
			// zisti pocet riadkov
			List trs = elTbl.getChildren("tr", Global.wns);
			cRows = trs.size();
			
			// zisti pocet stlpcov (zober prvy riadok, prejdi vsetkymi bunkami a davaj si pozor na gridSpany)
			List tcs = ((Element)trs.get(0)).getChildren("tc", Global.wns);
			Iterator tcIterator = tcs.iterator();
			while (tcIterator.hasNext()) {
				Element tcEl = (Element) tcIterator.next();
				Element tcPr = tcEl.getChild("tcPr", Global.wns);
				int c = 1;
				if (tcPr != null) {
					Element gridSpanEl = tcPr.getChild("gridSpan", Global.wns);
					if (gridSpanEl != null) {
						int rowspan = Functions.intValDef(gridSpanEl.getAttributeValue("val", Global.wns),0);
						c = rowspan;
					}
				}
				cCols += c;
			}
			
			// priprav tabulku
			mergedCells = new boolean[cCols][cRows];
			for (int i = 0; i < cCols; i++) {
				for (int j = 0; j < cRows; j++) {
					mergedCells[i][j] = false;
				}
			}
		}
		catch (Exception e) {
			System.err.println(this.getClass().getName() + " (setCounts): " + e.toString());
		}
	}
	
	/**
	 * Funkcia pridá mergenuté bunky v riadku (spájame stĺpce).
	 * @param count počet buniek
	 */
	private void addMergedCellsInRow(int count) {
		try {
			// pri pridani gridspan
			for (int i = col + 1; ((i - col) < count) && (i < cCols); i++ ) {
				mergedCells[i][row] = true;
			}
		}
		catch (Exception e) {
			System.err.println(this.getClass().getName() + " (addMergedCellsInRow): " + e.toString());
		}
	}

	/**
	 * Funkcia pridá mergenuté bunky v stĺpci (spájame riadky).
	 * @param count počet buniek
	 */
	private void addMergedCellsInCol(int count) {
		try {
			// pri pridani rowspan
			for (int i = row + 1; ((i - row) < count) && (i < cRows); i++ ) {
				mergedCells[col][i] = true;
			}
		}
		catch (Exception e) {
			System.err.println(this.getClass().getName() + " (addMergedCellsInCol): " + e.toString());
		}
	}
	
	/**
	 * Funkcia zisťuje pozíciu v tabuľke.
	 * @return číslo nasledujúceho stĺpca
	 */
	private int getNextCol() {
		try {
			if ((col < 0) || (row < 0)) {
				col = 0;
				row = 0;
				return col;
			}
			else {
				col++;
				while ((col<cCols) && (mergedCells[col][row])) {
					col++;
				}
				if (col >= cCols) {
					col = 0;
					row++;
					if (row >= cRows) {
						throw new Exception("V tabuľke sa nenachádza ďalší sĺpec. r:"+row+",c:"+col);
					}
				}
				return col;
			}
		}
		catch (Exception e) {
			System.err.println(this.getClass().getName() + " (getNextCol): " + e.toString());
			return -1;
		}
	}
	
	/**
	 * Funkcia prejde obsahom bunky tabulky a do výstupného XHTML elementu pridá zodpovedajúci obsah
	 * @param tcEl XML element bunky
	 * @param xtrEl XHTML výstupný element
	 */
	private void parseCell(Element tcEl, Element xtrEl, int lCol, int lRow, HashMap<Integer,Element> vMerge, Element hMerge) {
		try {
			// pridavame novu bunku? - ak mergujeme, tak nie
			boolean addNewCell = true;
			// vystupny xhtml td element
			Element xtd = new Element("td");
//			String className = Global.cssOutput.addClassN(".cell", "border: 1px solid black;");
//			xtd.setAttribute("class", className);
			// zisti vlastnosti bunky
			Element tcPr = tcEl.getChild("tcPr", Global.wns);
			if (tcPr != null) {
				// prejdi vsetkymi vlastnostami
				List tcPrs = tcPr.getChildren();
				Iterator tcPrsIterator = tcPrs.iterator();
				while (tcPrsIterator.hasNext()) {
					Element tcPrChild = (Element) tcPrsIterator.next();
					if (tcPrChild.getName().trim().equals("gridSpan")) {
						int colspan = Functions.intValDef(tcPrChild.getAttributeValue("val", Global.wns),0);
						xtd.setAttribute("colspan", colspan + "");
						addMergedCellsInRow(colspan);
					}
					else if (tcPrChild.getName().trim().equals("rowSpan")) {
						int rowspan = Functions.intValDef(tcPrChild.getAttributeValue("val", Global.wns),0);
						xtd.setAttribute("rowspan", rowspan + "");
						addMergedCellsInCol(rowspan);
					}
					else if (tcPrChild.getName().trim().equals("vMerge")) {
						String val = tcPrChild.getAttributeValue("val", Global.wns, "continue");
						if (val.trim().equals("restart")) {
							vMerge.put(Integer.valueOf(lCol), xtd);
						}
						else {
							Element mergingTdEl = vMerge.get(Integer.valueOf(lCol));
							int actRowSpan = Functions.intValDef(mergingTdEl.getAttributeValue("rowspan"),1) + 1;
							mergingTdEl.setAttribute("rowspan", actRowSpan+"");
							addNewCell = false;
						}
					}
					else if (tcPrChild.getName().trim().equals("hMerge")) {
						String val = tcPrChild.getAttributeValue("val", Global.wns, "continue");
						if (val.trim().equals("restart")) {
							hMerge = xtd;
						}
						else {
							int actColSpan = Functions.intValDef(hMerge.getAttributeValue("rowspan"),1) + 1;
							hMerge.setAttribute("rowspan", actColSpan+"");
							addNewCell = false;
						}
					}
					else if (Global.ooxmlPreserve.background && tcPrChild.getName().trim().equals("shd")) {
						String bkgColor = tcPrChild.getAttributeValue("fill", Global.wns, "000000");
						String tdClassNames = xtd.getAttributeValue("class", "");
						String className = Global.cssOutput.addClassN("td", ".cellBkgColor", "", "background-color: #" + bkgColor + ";");
						String classes = (tdClassNames.trim() + " " + className.trim()).trim();
						if (!classes.isEmpty()) {
							if (Global.ooxmlParams.formatPreservingLevel > 0) {
								xtd.setAttribute("class", classes);
							}
						}
					}
				}
			}
			if (addNewCell) {
				xtrEl.addContent(xtd);
				OOXMLparser parser = new OOXMLparser();
				Element elCellContent = parser.parse(tcEl);
				List pEls = elCellContent.getChildren("p");
				List allEls = elCellContent.getChildren();
				// ak bunka obsahuje iba jeden p element, tak jeho atributy a obsah prenes na td
				if ((pEls.size() == 1) && (allEls.size() == 1)) {
					Element pEl = elCellContent.getChild("p");
					String pClassNames = pEl.getAttributeValue("class");
					String tdClassNames = xtd.getAttributeValue("class");
					if (pClassNames == null) {
						pClassNames = "";
					}
					if (tdClassNames == null) {
						tdClassNames = "";
					}
					String classes = (pClassNames.trim() + " " + tdClassNames.trim()).trim();
					if (!classes.isEmpty()) {
						if (Global.ooxmlParams.formatPreservingLevel > 0) {
							xtd.setAttribute("class", classes);
						}
					}
					xtd.addContent(pEl.cloneContent());
				}
				else {
					xtd.addContent(elCellContent.cloneContent());
				}
			}
		}
		catch (Exception e) {
			System.err.println(this.getClass().getName() + " (parseCell): " + e.toString());
		}
	}
	
	/**
	 * Funkcia prejde celou tabuľkou a pripraví výstup.
	 * @return XHTML element - výstupná XHTML tabuľka
	 */
	public Element getXHTMLTable() {
		try {
			// zapamatanie td elementu ktory mergujeme pre kazdy stlpec
			HashMap<Integer,Element> vMerge = new HashMap<Integer,Element>();
			// zapamatanie td elementu, ktory mergujeme v riadku
			Element hMerge = null;
			// table element vystup
			Element table = new Element("table");
			String className = Global.cssOutput.addClassN("table",".table", "", "border-collapse: collapse;");
			Global.cssOutput.addClass("table", "." + className, " td", "border: 1px solid black;");
			if (Global.ooxmlParams.formatPreservingLevel > 0) {
				table.setAttribute("class", className);
			}
			// nastav styly
			Element tblPrEl = elTbl.getChild("tblPr", Global.wns);
			if (tblPrEl != null) {
				Element tblStyleEl = tblPrEl.getChild("tblStyle", Global.wns);
				if (tblStyleEl != null) {
					String styleId = tblStyleEl.getAttributeValue("val", Global.wns);
					LinkedHashMap<String,String> tblStyles = Global.ooxmlStyles.getStyle(styleId);
				}
			}
			// prejdi vsetkymi riadkami
			int lRow = -1;
			List trs = elTbl.getChildren("tr", Global.wns);
			Iterator trIterator = trs.iterator();
			while (trIterator.hasNext()) {
				lRow++;
				Element trEl = (Element) trIterator.next();
				Element xtrEl = new Element("tr");
				table.addContent(xtrEl);
				// prejdi vsetkymi stlpcami
				List tcs = trEl.getChildren("tc", Global.wns);
				Iterator tcIterator = tcs.iterator();
				while (tcIterator.hasNext()) {
					int lCol = getNextCol();
					Element tcEl = (Element) tcIterator.next();
					parseCell(tcEl, xtrEl, lCol, lRow, vMerge, hMerge);
				}
			}
			return table;
		}
		catch (Exception e) {
			System.err.println(this.getClass().getName() + " (getXHTMLTable): " + e.toString());
			return null;
		}
	}

}
