package writer2latex.xhtml;

import java.util.LinkedList;



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

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


//LinkedList<String>   stringList = new LinkedList<String>();
public class DocumentSeparator {

	private static final String NONE = "none";
	private static final String SECTIONS = "sections";
	private static final String DIV = "div";
	private int splitLevel = 0;
	private boolean splitByPages = false;
	private int lastSplitPageNum = 1;
		
	private static LinkedList<Integer> headerStack = new LinkedList<Integer>();
	private static boolean pageOpened = false; 
	//headings none
	private static String headingSeparation = "sections";
	//sections div none
	private static String pageSeparation = "sections";
	private Converter converter = null;
	private XhtmlConfig config = null;
	private Node prevPageNode = null;

	public DocumentSeparator(XhtmlConfig config,Converter converter) {
		this.config = config;
		this.converter = converter;
		headingSeparation = config.getHeadingTags();
		pageSeparation = config.getPageTags();
		splitLevel = config.getXhtmlSplitLevel();
		splitByPages = pageSeparation.equals(DIV) ? true : false;
	}

	protected Node processOutlineLevel(Node currentNode, Node hnode, int pageNum) {
		
		//Get outline level
		String sLevel = getAttribute(currentNode, XMLString.TEXT_OUTLINE_LEVEL);
		String title = getTitle(currentNode).trim();
		if (sLevel == null || sLevel.isEmpty()) {
			return hnode;
		}
		if (title == null || title.isEmpty()) {
			return hnode;
		}
		int curLevel = Integer.parseInt(sLevel);
		if (needSplitFiles(curLevel,pageNum)){
			prevPageNode = hnode;
		}
		
		if (pageOpened) {
			hnode = closePage(hnode);
		}

		if (headingSeparation.equals(SECTIONS)) {
			closeCommentHeadings(hnode, curLevel);
		}

		if (needSplitFiles(curLevel,pageNum)) {
			hnode = splitFiles(hnode);
			lastSplitPageNum = pageNum;
		}

		if (headingSeparation.equals(SECTIONS)) {
			openCommentHeading(hnode, title);
			headerStack.offerFirst(Integer.parseInt(sLevel));

		}
		if (!noPageSeparation()) {
			hnode = openPage(hnode, pageNum);
		}
			
		

		return hnode;
	}

	private Node splitFiles(Node hnode) {
		hnode = converter.nextOutFile();
		return hnode;
	}

	private boolean needSplitFiles(int curLevel,int pageNum) {
		if (splitLevel >= curLevel && (converter.outFileHasContent() || converter.getOutFileIndex() == 1)) {
			if (lastSplitPageNum != pageNum) {
				return true;	
			}
			
		}
		return false;
	}
	
	protected Node processPageBreak(Node currentNode, Node hnode, Integer pageNum){
		if (noPageSeparation()){
			return hnode;
		}
		if (pageOpened) {
			hnode = closePage(hnode);
		}
		hnode = openPage(hnode, pageNum);

		return hnode;
	}
	
	/**
	 * Opens main document section heading tag
	 */
	protected Node StartDocument(Node hnode, String title, int pageNum){
		
		if (noHeadingSeparation() && noPageSeparation()){
			return hnode;
		}
		if(headingSeparation.equals(SECTIONS)){
			//Create global section
			openCommentHeading(hnode, title);
		}
		hnode = openPage(hnode, pageNum);
		
		return hnode;
	}
	//Method to close open tags at the end of the document
	protected static Node endDocument(Node hnode){
		if (noHeadingSeparation() && noPageSeparation()){
			return hnode;
		}
		if (pageOpened){
			hnode = closePage(hnode);
		}
		if (headingSeparation.equals(SECTIONS)){
			closeCommentHeadings(hnode, 0);
			//Close global section
			addCloseComment(hnode);
		} 
		
		return hnode;
	}
	
	private Node openPageDiv(Node node,int pageNum){
		if (node == null){
			System.out.println("Error: node is null on openPageDiv");
			return node;
		}
		if (prevPageNode != null && splitByPages) {
			arrangePageDivs(node,pageNum);
		}
		Document doc = node.getOwnerDocument();
		Element openBlock = (Element) doc.createElement(DIV);
		openBlock.setAttribute("class", "pageNum");
		openBlock.setAttribute("page", Integer.toString(pageNum));
		// insert open section comment before header node
		node.appendChild((Node)openBlock);
		node = openBlock;
		return openBlock;
	}
	
	private void arrangePageDivs(Node node, int pageNum) {
		Document newdoc = node.getOwnerDocument();
		Document olddoc = prevPageNode.getOwnerDocument();
		String prevPageNum = getAttribute(prevPageNode, "page");

		if (prevPageNum != null && prevPageNum.equals(Integer.toString(pageNum))) {
			if (isElement(prevPageNode, DIV)) {
				Node importedNode = newdoc.importNode(prevPageNode, true);
				node.appendChild(importedNode);
				Node prevDocContent = prevPageNode.getParentNode();
				if (prevDocContent != null) {
					prevDocContent.removeChild(prevPageNode);	
				} 
			}
		}
		
		//no more arrange needed till next file separation
		prevPageNode = null;
		
	}

	private static Node exitPageDiv(Node node){
		
		while (	!isRoot(node)	&& !isElement( node,DIV) ){
			node = node.getParentNode();
		}
		Node result = node.getParentNode();
		if (node.getChildNodes().getLength() == 0){
			result.removeChild(node);
		}
		
		return result;
	}
	
	public static String getTitle(Node currentNode) {
		Node content = currentNode.cloneNode(true);
		NodeList contentNodes = content.getChildNodes();
		String title = null;

		int i = 0;
		while (i < contentNodes.getLength()) {
			Node child = contentNodes.item(i);
			if (isElement(child) ){
				if (child.getNodeName().equals(XMLString.TEXT_TAB) ||
					child.getNodeName().equals(XMLString.TEXT_LINE_BREAK)	){
					Document doc = child.getOwnerDocument();
					Node testSpace = doc.createTextNode(" ");
					content.insertBefore(testSpace, child);
					content.removeChild(child);
				}
			}
				i++;
		} 
		NodeList notes = ((Element) content).getElementsByTagName(XMLString.TEXT_NOTE);
		int j = 0;
		while (j < notes.getLength()){
			Node note = notes.item(j);
			note.getParentNode().removeChild(note);
		}
		title = content.getTextContent();
		return title;
	}

	
	private static void openPageComment(Node hnode, Integer pageNum){
		Document doc = hnode.getOwnerDocument();
		Node openSection = doc.createComment(openPageCommentText(pageNum));
		// insert open section comment before header node
		hnode.appendChild(openSection);
	}
	
	private static void openCommentHeading(Node hnode, String title){
		Document doc = hnode.getOwnerDocument();
		Node openSection = doc.createComment(openHeadingCommentText(title));
		// insert open section comment before header node
		hnode.appendChild(openSection);
	}

	private static void addCloseComment(Node node){
		Document doc = node.getOwnerDocument();
		Node closeSection = doc.createComment("</Section>");
		//insert open section comment before header node
		node.appendChild(closeSection);
	}
	
	private static void closeCommentHeadings(Node hnode, int nLevel){
		if (headerStack.isEmpty()) {
			return;
		}
		//Close all sections with level less than current
		while (nLevel <= headerStack.peek()) {
			addCloseComment(hnode);
			headerStack.poll();
			if (headerStack.isEmpty()) {
				break;
			}
		}
	}
	protected static Node closePage(Node hnode){
		if (pageSeparation.equals(SECTIONS)){
			//If section is empty. In case we are closing section
			// the last comment is opened page section
			if (hnode.getLastChild().getNodeType() == Node.COMMENT_NODE){
				hnode.removeChild(hnode.getLastChild());
			} else {
				addCloseComment(hnode);	
			}
		} else if (pageSeparation.equals(DIV)){
			hnode = exitPageDiv(hnode);
		}
		pageOpened = false;
		return hnode;
	}
	protected Node openPage(Node hnode, Integer pageNum){
		if (pageSeparation.equals(SECTIONS)){
			openPageComment(hnode, pageNum);
			pageOpened = true;
		} 
		else if (pageSeparation.equals(DIV)){
			hnode = openPageDiv(hnode, pageNum);
			pageOpened = true;
		} 
		
		return hnode;
	}
	private static boolean noHeadingSeparation() {
		return headingSeparation.equals(NONE);
	}
	private static boolean noPageSeparation() {
		return pageSeparation.equals(NONE);
	}
	private static String openHeadingCommentText(String title) {
		String comment = "<Section>\n<Description>\n<Metadata name=\"Title\">" + title
				+ "</Metadata>\n</Description>";
		return comment;
	}
	private static String openPageCommentText(Integer pageNum) {
		String comment = "<Section>\n<Description>\n<Metadata name=\"Title\">" + pageNum
				+ "</Metadata>\n<Metadata name=\"Page\">" + pageNum + "</Metadata>\n</Description>";
		return comment;
	}

}
	