package writer2latex.xhtml;

import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;

import writer2latex.office.OfficeReader;
import writer2latex.office.StyleWithProperties;
import static writer2latex.office.XMLString.*;
import writer2latex.util.Misc;

public class PageSplitter {
	static Node truncatedListItemNodeContent = null;
	static OfficeReader officeReader = null;

	protected static Node splitSoftPageBreak(Node onode,OfficeReader ofr){
		//Find par node with soft page break inside and split it
		officeReader = ofr;
		Document document = onode.getOwnerDocument();
		Element softPageBreak = document.createElement(TEXT_SOFT_PAGE_BREAK);
		NodeList nodes = onode.getChildNodes();
		
		int i = 0;
		//Loop through the content nodes and split nodes with soft page break
		while (i < nodes.getLength()){
			Node child = nodes.item(i);
			//System.out.println("splitSoftPageBreak ");
			
			//Necessary check if node is an Element
			if (containsSPB(child)){
				String nodeName = child.getNodeName();
				//Create Duplicate Node!
				Element childFirstPart = (Element) child.cloneNode(false);
				StyleWithProperties style = null;
				if ((nodeName.equals(TEXT_P) || nodeName.equals(TEXT_H))) {
					//If SPB not the first node
					Node paraFirstPart = handleParagraph(child);
					if (paraFirstPart != null){
						onode.insertBefore(paraFirstPart, child);
						style = ofr.getParStyle(Misc.getAttribute(child, TEXT_STYLE_NAME));
					}
				} else if (nodeName.equals(TABLE_TABLE)) {
					if (handleTableTable(childFirstPart, child)){
						onode.insertBefore(childFirstPart, child);
						style = ofr.getTableStyle(Misc.getAttribute(child, TABLE_STYLE_NAME));
					}
				} else if (nodeName.equals(TEXT_LIST)) {
					if (handleList(childFirstPart, child)){
						onode.insertBefore(childFirstPart, child);
					}
				} else if (nodeName.equals(TEXT_SECTION)) {
					if (handleSection(childFirstPart, child)){
						onode.insertBefore(childFirstPart, child);
						style = ofr.getSectionStyle(Misc.getAttribute(child, TEXT_SECTION));
					}
				} else if (nodeName.equals(TEXT_TABLE_OF_CONTENT)){
					//HACK
					containsSPB(childFirstPart);
					i++;
					continue;
				} else if (nodeName.equals(TEXT_SOFT_PAGE_BREAK)){
					//HACK
					i++;
					continue;
				}
				//TODO: IF fo:break before in original table - don't create SPB
				
				if (style == null || !"page".equals(style.getProperty(FO_BREAK_BEFORE))){
					onode.insertBefore(softPageBreak.cloneNode(false), child);
				}
				//HACK!
				if (truncatedListItemNodeContent != null){
					NodeList itemNodeList= truncatedListItemNodeContent.getChildNodes();
					while (itemNodeList.getLength() > 0){
						onode.insertBefore(itemNodeList.item(0), child);
					}
					truncatedListItemNodeContent.getParentNode().removeChild(truncatedListItemNodeContent);
					truncatedListItemNodeContent = null;
				}
				if (!child.hasChildNodes()){
					onode.removeChild(child);
				}
				continue;
			}
			i++;
		}
		return onode;
	}
	private static boolean handleList(Node listFirstPart, Node list){
		
		NodeList listNodes = list.getChildNodes();
		int i = 0;
		boolean dataMoved = false;
		while (listNodes.getLength() > i) {
			Node listChild = listNodes.item(i);
			if(listChild.getNodeType() == Node.ELEMENT_NODE){
				String nodeName = listChild.getNodeName();
				if (nodeName.equals(TEXT_LIST_HEADER)) {
					if(containsSPB(listChild)){
					//Remove inner SPB
					removeSPB(listChild);
					//HACK :(
					break;
					} 
					listFirstPart.appendChild(listChild.cloneNode(true));
					//Get next element
					i++;
					
					
				} else if (nodeName.equals(TEXT_LIST_ITEM)) {
					if (containsSPB(listChild)){
						Node listItemFirstPart = listChild.cloneNode(false);
						//remove SPB, move previous nodes to firstPart.
						if (handleListItem(listItemFirstPart,listChild)){
							dataMoved = true;
							//Add first part of list item to previous list item
							listFirstPart.appendChild(listItemFirstPart);
							//Get list parent node and move cutted node  
							//After First Part and SPB but before this list;
							//TODO!!!!!!!!!!
							truncatedListItemNodeContent = listChild;
							listFirstPart.getParentNode();
							//If List item is empty - remove it
							if (!listChild.hasChildNodes()){
								list.removeChild(listChild);
							}
							
							
							
						}
						//Add text:continue-numbering="true"
						if (dataMoved){
							((Element) list).setAttribute(TEXT_CONTINUE_NUMBERING, "true");
						}
						break;
					} else {
						// Not with SPB yet, move node, set dataMoved=true
						listFirstPart.appendChild(listChild);
						dataMoved = true;
						
					}
					
				} 
			} 
		}
		return dataMoved;
	}
	//If SPB before first item - return false, remove SPB
	//Otherwise add childNodes before SPB to firstPart, return true
	private static boolean handleListItem(Node listItemFirstPart, Node listItem){
		int i = 0;
		boolean dataMoved = false;
		NodeList listItemNodes = listItem.getChildNodes();
		while(listItemNodes.getLength() > i){
			Node listItemChild = listItemNodes.item(i);
			if(listItemChild.getNodeType() == Node.ELEMENT_NODE){
				//Node name
				String nodeName = listItemChild.getNodeName();
				if (containsSPB(listItemChild)){
					Node listItemChildFirstPart = listItemChild.cloneNode(false);
					//Break if SPB
					if (nodeName.equals(TEXT_SOFT_PAGE_BREAK)) {
						//Remove SPB.Return result
						listItem.removeChild(listItemChild);
					} else if (nodeName.equals(TEXT_LIST)) {
						if (handleList(listItemChildFirstPart, listItemChild)){
							listItemFirstPart.appendChild(listItemChildFirstPart);
							dataMoved=true;
						}
						
					} else if (nodeName.equals(TEXT_H) || nodeName.equals(TEXT_P)) {
						Node paraFirstPart = handleParagraph(listItemChild);
						if (paraFirstPart != null){
							listItemFirstPart.appendChild(paraFirstPart);
							dataMoved=true;
						}
					}
					break;
				//Move to first part
				} else {
					
					listItemFirstPart.appendChild(listItemChild);
					dataMoved = true;
				}
			} else {
				listItemFirstPart.appendChild(listItemChild);
				dataMoved = true;
			}
			//check internal nodes
		}
		return dataMoved;
	}
	//Needs finish
	private static boolean handleTableTable(Node tableFirstPart, Node table) {
		
		/*
		 * // TODO: 0.Test if soft-page-break not at start of table // - in that
		 * case just remove it and insert before table // 1.Create new table //
		 * 2.Copy to it table:table-column's and // table:table-header-rows //
		 * 3.Move nodes before soft-page-break to new table //4. IF in one
		 * table:row exist more one Algorithm IF SPB at start - just move it
		 * higher IF SPB between rows - just copy table move row and put SPB
		 * between tables IF SPB inside row, inside cell - copy table, copy
		 * empty row, copy each empty cell and in each cell move every node up
		 * to the first SPB
		 * 
		 * 
		 */ 
		NodeList tableChildNodes = table.getChildNodes();
		// Node counter
		int i = 0;
		boolean dataMoved = false;
		// Loop through the TABLE:TABLE child nodes
		while (tableChildNodes.getLength() > i) {
			Node tableChildNode = tableChildNodes.item(i);
			
			if (tableChildNode.getNodeType() == Node.ELEMENT_NODE) {
				//Node name
				String tableChildNodeName = tableChildNode.getNodeName();
				//System.out.println("Table child node " + tableChildNodeName);
				if (containsSPB(tableChildNode)){
					Node tableChildFirstPart = tableChildNode.cloneNode(false); 
					if (tableChildNodeName.equals(TEXT_SOFT_PAGE_BREAK)) {
						// remove inner soft page break node
						table.removeChild(tableChildNode);
						
					} else if (tableChildNodeName.equals(TABLE_TABLE_ROW_GROUP)) {
						if (handleTableRowGroup(tableChildFirstPart, tableChildNode)){
							dataMoved = true;
							tableFirstPart.appendChild(tableChildFirstPart);
						}
					} else if ( tableChildNodeName.equals(TABLE_TABLE_ROWS)) {
						if (handleTableRows(tableChildFirstPart, tableChildNode)){
							dataMoved = true;
							tableFirstPart.appendChild(tableChildFirstPart);
						}
					} else if ( tableChildNodeName.equals(TABLE_TABLE_ROW)) {
						if (handleTableRow(tableChildFirstPart, tableChildNode)){
							dataMoved = true;
							tableFirstPart.appendChild(tableChildFirstPart);
						}
					} else  if (tableChildNodeName.equals(TABLE_TABLE_COLUMN)
							 || tableChildNodeName.equals(TABLE_TABLE_COLUMN_GROUP)
							 || tableChildNodeName.equals(TABLE_TABLE_HEADER_ROWS)
							 || tableChildNodeName.equals(TABLE_TABLE_HEADER_COLUMNS)) {
						//Remove Soft Page Break
						removeSPB(tableChildNode);					
					}
					break;
					
				} else {
					//Before SPB
					//Description nodes
					if (tableChildNodeName.equals(TABLE_TABLE_COLUMN)
					 || tableChildNodeName.equals(TABLE_TABLE_COLUMN_GROUP)
					 || tableChildNodeName.equals(TABLE_TABLE_HEADER_ROWS)
					 || tableChildNodeName.equals(TABLE_TABLE_HEADER_COLUMNS)) {
						//Append to clone table
						tableFirstPart.appendChild(tableChildNode.cloneNode(true));
						//increment counter
						i++;
					} else {
						//Append to clone table
						tableFirstPart.appendChild(tableChildNode);
						dataMoved = true;
					}
				}
			}
		}
		return dataMoved;
	}
 
	private static boolean handleTableRowGroup(Node tableRowGroupFistPart, Node tableRowGroup) {
		boolean dataMoved = false;
		// Node counter
		int i = 0;
		NodeList tableRowGroupChildNodes = tableRowGroup.getChildNodes();
		while (tableRowGroupChildNodes.getLength() > i) {
			Node tableRowGroupChildNode = tableRowGroupChildNodes.item(0);
			if ((tableRowGroupChildNode.getNodeType() == Node.ELEMENT_NODE)) {
				
				String nodeName = tableRowGroupChildNode.getNodeName();
				
				if (containsSPB(tableRowGroupChildNode)){
					Node tableRowGroupChildFirstPart = tableRowGroupChildNode.cloneNode(false); 
					if (nodeName.equals(TEXT_SOFT_PAGE_BREAK)){
						// remove inner soft page break node
						tableRowGroup.removeChild(tableRowGroupChildNode);
					
					} else if (nodeName.equals(TABLE_TABLE_HEADER_ROWS)){
						//Nothing IF table-header-rows found - it is description node, 
						//Not needed to set dataMoved = true, not needed to append First part
					
					} else if (nodeName.equals(TABLE_TABLE_ROW)){
						if (handleTableRow(tableRowGroupChildFirstPart, tableRowGroupChildNode)){
							dataMoved = true;
							tableRowGroupFistPart.appendChild(tableRowGroupChildFirstPart);
						}
					} else if (nodeName.equals(TABLE_TABLE_ROW_GROUP)){
						if (handleTableRowGroup(tableRowGroupChildFirstPart, tableRowGroupChildNode)){
							dataMoved = true;
							tableRowGroupFistPart.appendChild(tableRowGroupChildFirstPart);
						}
					} else if (nodeName.equals(TABLE_TABLE_ROWS)){
						if (handleTableRows(tableRowGroupChildFirstPart, tableRowGroupChildNode)){
							dataMoved = true;
							tableRowGroupFistPart.appendChild(tableRowGroupChildFirstPart);
						}
					} 
					break;
				} else {
					if (nodeName.equals(TABLE_TABLE_HEADER_ROWS)){
						tableRowGroupFistPart.appendChild(tableRowGroupChildNode.cloneNode(true));
						//increment counter
						i++;
					
					} else {
						tableRowGroupFistPart.appendChild(tableRowGroupChildNode);
						dataMoved = true;
					}
				}
			} else {
				//Append text nodes
				tableRowGroupFistPart.appendChild(tableRowGroupChildNode);
				dataMoved = true;	
			}
			
		}
		return dataMoved;
	}
	private static boolean handleTableRows(Node tableRowsFistPart, Node tableRows) {
		boolean dataMoved = false;
		// Node counter
		int i = 0;
		NodeList tableRowsChildNodes = tableRows.getChildNodes();
		while (tableRowsChildNodes.getLength() > i) {
			Node tableRowsChildNode = tableRowsChildNodes.item(0);
			if ((tableRowsChildNode.getNodeType() == Node.ELEMENT_NODE)) {
				
				String nodeName = tableRowsChildNode.getNodeName();
				
				if (containsSPB(tableRowsChildNode)){
					Node tableRowGroupChildFirstPart = tableRowsChildNode.cloneNode(false); 
					if (nodeName.equals(TEXT_SOFT_PAGE_BREAK)){
						// remove inner soft page break node
						tableRows.removeChild(tableRowsChildNode);
					
					} else if (nodeName.equals(TABLE_TABLE_ROW)){
						if (handleTableRow(tableRowGroupChildFirstPart, tableRowsChildNode)){
							dataMoved = true;
							tableRowsFistPart.appendChild(tableRowGroupChildFirstPart);
						}
					} 
					break;
				} else {
					tableRowsFistPart.appendChild(tableRowsChildNode);
					dataMoved = true;
				}
			} else {
				System.out.println("ERROR: TEXT NODE FOUND INSIDE tabl:table-rows");
				//Append text nodes
				//tableRowsFistPart.appendChild(tableRowsChildNode);
				//dataMoved = true;	
			}
			
		}
		return dataMoved;
	}
	private static boolean handleTableRow(Node tableRowFistPart, Node tableRow) {
		boolean dataMoved = false;
		// Node counter
		int i = 0;
		NodeList tableRowChildNodes = tableRow.getChildNodes();
		while (tableRowChildNodes.getLength() > i) {
			Node tableRowChildNode = tableRowChildNodes.item(i);
			if ((tableRowChildNode.getNodeType() == Node.ELEMENT_NODE)) {
				
				String nodeName = tableRowChildNode.getNodeName();
				
				if (containsSPB(tableRowChildNode)){
					Node tableRowGroupChildFirstPart = tableRowChildNode.cloneNode(false); 
					if (nodeName.equals(TABLE_TABLE_CELL)){
						if (handleCell(tableRowGroupChildFirstPart, tableRowChildNode)){
							dataMoved = true;
							tableRowFistPart.appendChild(tableRowGroupChildFirstPart);
						}
					
					} else if (nodeName.equals(TABLE_COVERED_TABLE_CELL)){
						//Implement handleCoveredCell in future
						if (handleCell(tableRowGroupChildFirstPart, tableRowChildNode)){
							dataMoved = true;
							tableRowFistPart.appendChild(tableRowGroupChildFirstPart);
						}
					} 
				} else {
					//System.out.println("HERE " + nodeName);
					//Move node without SPB above
					tableRowFistPart.appendChild(tableRowChildNode.cloneNode(true));
					Node emptyCell = tableRowChildNode.cloneNode(false);
					Document document = tableRow.getOwnerDocument();
					Element textP = document.createElement(TEXT_P);
					emptyCell.appendChild(textP);
					tableRow.insertBefore(emptyCell, tableRowChildNode);
					tableRow.removeChild(tableRowChildNode);
					
					
					dataMoved = true;
				}
				i++;
			} else {
				System.out.println("ERROR: TEXT NODE FOUND INSIDE tabl:table-row");
				//Append text nodes
				//tableRowsFistPart.appendChild(tableRowsChildNode);
				//dataMoved = true;	
			}
			
		}
		return dataMoved;
	}
	private static boolean handleCell(Node cellFirstPart, Node cellNode) {
		boolean dataMoved = false;
		// Node counter
		int i = 0;
		NodeList cellChildNodes = cellNode.getChildNodes();
		while (cellChildNodes.getLength() > i) {
			Node cellChildNode = cellChildNodes.item(0);
			if ((cellChildNode.getNodeType() == Node.ELEMENT_NODE)) {
				String nodeName = cellChildNode.getNodeName();
				if (containsSPB(cellChildNode)){
					if (nodeName.equals(TEXT_SOFT_PAGE_BREAK)){
						// remove inner soft page break node
						cellNode.removeChild(cellChildNode);
					
					} else if (nodeName.equals(TEXT_H) || nodeName.equals(TEXT_P)) {
						Node paraFirstPart = handleParagraph(cellChildNode);
						if (paraFirstPart != null){
							cellFirstPart.appendChild(paraFirstPart);
							dataMoved=true;
						}
					} 
					break;
				} else {
						cellFirstPart.appendChild(cellChildNode);
						dataMoved = true;
				}
			} else {
				//Append text nodes
				cellFirstPart.appendChild(cellChildNode);
				dataMoved = true;	
			}
			
		}
		return dataMoved;
	}
	private static boolean handleSection(Node sectionFirstPart, Node section) {
		boolean dataMoved = false;
		// Node counter
		int i = 0;
		NodeList childs = section.getChildNodes();
		while (childs.getLength() > i) {
			
			Node child = childs.item(0);
			if ((child.getNodeType() == Node.ELEMENT_NODE)) {
				String nodeName = child.getNodeName();
				if (containsSPB(child)){
					Node childFirstPart = child.cloneNode(false); 
					if (nodeName.equals(TEXT_SOFT_PAGE_BREAK)){
						// remove inner soft page break node
						section.removeChild(child);
					
					} else if (nodeName.equals(TEXT_H) || nodeName.equals(TEXT_P)) {
						Node paraFirstPart = handleParagraph(child);
						if (paraFirstPart != null){
							sectionFirstPart.appendChild(paraFirstPart);
							dataMoved=true;
						}
					} else if (nodeName.equals(TEXT_TABLE_OF_CONTENT)) {
						
						//HACK
						removeSPB(section);
						i++;
						continue;
					} else if (nodeName.equals(TABLE_TABLE)) {
						if (handleTableTable(childFirstPart, child)){
							sectionFirstPart.appendChild(childFirstPart);
							dataMoved=true;
						}
					} else if (nodeName.equals(TEXT_SECTION)) {
						if (handleSection(childFirstPart, child)){
							sectionFirstPart.appendChild(childFirstPart);
							dataMoved=true;
						}
					} else if (nodeName.equals(TEXT_LIST)) {
						if (handleList(childFirstPart, child)){
							sectionFirstPart.appendChild(childFirstPart);
							dataMoved=true;
							}
						}
					//split node with spb and exit
					break;
				} else {
						sectionFirstPart.appendChild(child);
						dataMoved = true;
				}
			} else {
				//Append text nodes
				sectionFirstPart.appendChild(child);
				dataMoved = true;	
			}
			
		}

		return dataMoved;
	}
	
	
	private static Node handleParagraph(Node para) {
		Node paraBefore = para.cloneNode(false);
		boolean dataMoved = false;
		int i = 0;
		NodeList сhilds = para.getChildNodes();
		while (сhilds.getLength() > i) {
			Node child = сhilds.item(i);
			//NOT TEXT NODES
			if ((child.getNodeType() == Node.ELEMENT_NODE)) {
				String childName = child.getNodeName();
				//SPB FOUND
				if (containsSPB(child)){
					if (childName.equals(TEXT_SOFT_PAGE_BREAK)){
						//removeSPB
						para.removeChild(child);
					} else {
						System.out.println("ERROR: SPB INSIDE Paragraph Element in inner element " + childName);
  					System.exit(1);
					}
					break;
				//Other elements
				} else if (childName.equals(TEXT_BOOKMARK_START)){
					paraBefore.appendChild(child.cloneNode(true));
					i++;
				} else	{
					dataMoved = appendChild(paraBefore, child);
				}
				//TEXT NODES
			} else {
				dataMoved = appendChild(paraBefore, child);
			}
			
		}
		addHyphen(para,paraBefore);

		
		
		if (dataMoved) {
			removeIndent(para);
			return paraBefore;
		}
		
		
		return null;
	}
	
	private static boolean appendChild(Node parent, Node child) {
		boolean dataMoved;
		parent.appendChild(child);
		dataMoved = true;
		return dataMoved;
	}
	
	private static void addHyphen(Node para, Node paraBefore) {
		//Debug.printNode(paraBefore);
		//Debug.printNode(para);
		Character softHyphen = 0x00ad;
		Character lastChar = getLastChar(paraBefore);
		Character firstChar = getFirstChar(para);
		
		if (lastChar == null || firstChar == null) {
			return;
		}
		Node lastNode = paraBefore;
		while (lastNode.hasChildNodes()) {
			lastNode = lastNode.getLastChild();
		}
		String lineEndsWith = lastNode.getTextContent();
		if (Character.isLetter(lastChar) && Character.isLetter(firstChar)) {
			
			lastNode.setTextContent(lastNode.getTextContent() + "\u2010");
		} else if (lastChar.equals(softHyphen)) {
			lastNode.setTextContent(lineEndsWith.substring(0, lineEndsWith.length()-1) + "\u2010");
		}
	}
	private static Character getLastChar(Node para) {
		if (para == null) {		return null;		}
		Node lastNode = para;
		while (lastNode.hasChildNodes()) {
			lastNode = lastNode.getLastChild();
		}
		String content = lastNode.getTextContent();
		if (content != null && !content.isEmpty()) {
			return content.charAt(content.length()-1);
		}
		return null;
	}
	
	private static Character getFirstChar(Node para) {
		if (para == null) {		return null;		}
		Node firstNode = para;
		while (firstNode.hasChildNodes()) {
			firstNode = firstNode.getFirstChild();
		}
		String content = firstNode.getTextContent();
		if (content != null && !content.isEmpty()) {
			return content.charAt(0);
		}
		return null;
	}
	
	private static void removeIndent(Node paraAfter) {
		String baseStyleName = Misc.getAttribute(paraAfter, TEXT_STYLE_NAME);
		String newStyleName = officeReader.cloneParStyle(baseStyleName);
		
		Node styleAttr = paraAfter.getAttributes().getNamedItem(TEXT_STYLE_NAME);
		styleAttr.setTextContent(newStyleName);
		
		StyleWithProperties newStyle = officeReader.getParStyle(Misc.getAttribute(paraAfter, TEXT_STYLE_NAME));
		newStyle.setParProperty(FO_TEXT_INDENT, "0");
	}

	private static void removeSPB(Node node) {
		if (node.getNodeType() == Node.ELEMENT_NODE) {
			if (node.getNodeName().equals(TEXT_SOFT_PAGE_BREAK)) {
				Node parent = node.getParentNode();
				parent.removeChild(node);
				return;
			}
			if (node.hasChildNodes()) {
				int currentNo = 0;
				NodeList childNodes = node.getChildNodes();
				while (currentNo < childNodes.getLength()) {
					Node childNode = childNodes.item(currentNo);
					removeSPB(childNode);					
					currentNo++;
				}
			}
		}
	}
	private static boolean containsSPB(Node node) {
		if (node.getNodeType() == Node.ELEMENT_NODE) {
			if (node.getNodeName().equals(TEXT_SOFT_PAGE_BREAK)) {
				return true;
			}
			if (node.hasChildNodes()) {
				int currentNo = 0;
				NodeList childNodes = node.getChildNodes();
				while (currentNo < childNodes.getLength()) {
					Node childNode = childNodes.item(currentNo);
					if (containsSPB(childNode)) {
						return true;
					}
					currentNo++;
				}
			}
		}
		return false;
	}

}

