/*******************************************************************************
 * Copyright (c) 2007 Exadel, Inc. and Red Hat, Inc.
 * Distributed under license by Red Hat, Inc. All rights reserved.
 * This program is made available under the terms of the
 * Eclipse Public License v1.0 which accompanies this distribution,
 * and is available at http://www.eclipse.org/legal/epl-v10.html
 *
 * Contributors:
 *     Exadel, Inc. and Red Hat, Inc. - initial API and implementation
 ******************************************************************************/
package org.jboss.tools.jsf.vpe.richfaces.template;

import java.util.ArrayList;
import java.util.List;

import org.jboss.tools.jsf.vpe.richfaces.ComponentUtil;
import org.jboss.tools.jsf.vpe.richfaces.RichFacesTemplatesActivator;
import org.jboss.tools.vpe.editor.context.VpePageContext;
import org.jboss.tools.vpe.editor.template.VpeAbstractTemplate;
import org.jboss.tools.vpe.editor.template.VpeChildrenInfo;
import org.jboss.tools.vpe.editor.template.VpeCreationData;
import org.jboss.tools.vpe.editor.template.expression.VpeExpression;
import org.jboss.tools.vpe.editor.template.expression.VpeExpressionException;
import org.jboss.tools.vpe.editor.util.HTML;
import org.jboss.tools.vpe.editor.util.VisualDomUtil;
import org.jboss.tools.vpe.editor.util.VpeClassUtil;
import org.mozilla.interfaces.nsIDOMDocument;
import org.mozilla.interfaces.nsIDOMElement;
import org.mozilla.interfaces.nsIDOMNode;
import org.mozilla.interfaces.nsIDOMNodeList;
import org.w3c.dom.Element;
import org.w3c.dom.Node;

public class RichFacesSubTableTemplate extends VpeAbstractTemplate {

	private static final String DEAFAULT_CELL_CLASS = "dr-subtable-cell"; //$NON-NLS-1$
	private static List<String> rowClasses;
	private static List<String> columnClasses;
	
	private static final String ST_HEADER_CLASS = "dr-subtable-header rich-subtable-header";
	private static final String ST_HEADER_CONTINUE_CLASS = "dr-subtable-header-continue rich-subtable-header-continue";
	private static final String ST_FOOTER_CLASS = "dr-subtable-footer rich-subtable-footer";
	private static final String ST_FOOTER_CONTINUE_CLASS = "dr-subtable-footer-continue rich-subtable-footer-continue";
	
	/** @deprecated no one another template should know about this template */
	public static final RichFacesSubTableTemplate DEFAULT_INSTANCE = new RichFacesSubTableTemplate();

	public RichFacesSubTableTemplate() {
		super();
	}
	
	public VpeCreationData encodeSubTable(final VpePageContext pageContext,
			VpeCreationData creationData, final Element sourceElement,
			final nsIDOMDocument visualDocument, nsIDOMElement parentVisualNode) {
		if(creationData!=null) {
			/*
			 * Encode header
			 */
			encodeHeader(pageContext, creationData, sourceElement, visualDocument, parentVisualNode);
		}

		initClasses(sourceElement, null);

		nsIDOMElement curTr = visualDocument.createElement(HTML.TAG_TR);
		if (parentVisualNode == null) {
			parentVisualNode = curTr;
		}
		VisualDomUtil.copyAttributes(sourceElement, curTr);

		boolean header = false;
		boolean footer = false;
		int curRow = 0;
		int curColumn = 0;

		if(isHeader(sourceElement)) {
			curTr.setAttribute(HTML.ATTR_CLASS, getHeaderClass());
			final String style = getHeaderBackgoundImgStyle();
			if(style!=null) {
				curTr.setAttribute(HTML.ATTR_STYLE, style);
			}
			header = true;
		} else if(isFooter(sourceElement)) {
			curTr.setAttribute(HTML.ATTR_CLASS, getFooterClass());
			footer = true;
		} else {
			curTr.setAttribute(HTML.ATTR_CLASS, getRowClass(curRow));
		}

		if(creationData==null) {
			// Method was called from create()
			creationData = new VpeCreationData(curTr);
		} else {
			// Method was called from dataTable
			parentVisualNode.appendChild(curTr);
		}
		
		creationData.addChildrenInfo(new VpeChildrenInfo(null));

		// Create mapping to Encode body
		VpeChildrenInfo trChildrenInfo = new VpeChildrenInfo(curTr);
		
		final List<Node> children = ComponentUtil.getChildren(sourceElement);
		for (final Node child : children) {
			if (child.getNodeType() == Node.ELEMENT_NODE) {
				String nodeName = child.getNodeName();
				if (nodeName.endsWith(':' + RichFaces.TAG_COLUMN) ||
						nodeName.endsWith(':' + RichFaces.TAG_COLUMNS)) {
					if (RichFacesColumnTemplate.isBreakBefore(child)) {
						curRow++;
						curColumn = 0;
						curTr = visualDocument.createElement(HTML.TAG_TR);
						VisualDomUtil.copyAttributes(sourceElement, curTr);
	
						if (header) {
							curTr.setAttribute(HTML.ATTR_CLASS, getHeaderContinueClass());
						} else if(footer) {
							curTr.setAttribute(HTML.ATTR_CLASS, getFooterContinueClass());
						} else {
							curTr.setAttribute(HTML.ATTR_CLASS, getRowClass(curRow));
						}
	
						parentVisualNode.appendChild(curTr);
						trChildrenInfo = new VpeChildrenInfo(curTr);
						creationData.addChildrenInfo(trChildrenInfo);
					}
	
					final VpeChildrenInfo innerTdChildrenInfo = new VpeChildrenInfo(curTr);
					creationData.addChildrenInfo(innerTdChildrenInfo);
					innerTdChildrenInfo.addSourceChild(child);
					curColumn++;
				} else {
					trChildrenInfo.addSourceChild(child);
				}
			}
		}


		if(parentVisualNode!=null) {
			// Encode footer
			encodeFooter(pageContext, creationData, sourceElement,
					visualDocument, parentVisualNode);
		}
		return creationData;
	}
	

	/** Adds necessary attributes to its children.*/
	@Override
	public void validate(final VpePageContext pageContext, final Node sourceNode,
			final nsIDOMDocument visualDocument, final VpeCreationData creationData) {
		initClasses(sourceNode, pageContext);
		
		nsIDOMNode visualNode = creationData.getNode();
		/*
		 * https://jira.jboss.org/jira/browse/JBIDE-4311
		 * Tag name can be in any case.
		 */
		if (visualNode != null && visualNode.getNodeName().equalsIgnoreCase(HTML.TAG_TBODY)) {
			// we are called by VpeVisualDomBuilder			
			addStylesToCells(visualDocument, visualNode);
		} else {
			RuntimeException e = new RuntimeException("This is probably a bug. The main tag of subTable shuld be 'TBODY'.");//$NON-NLS-1$
			RichFacesTemplatesActivator.getPluginLog().logError(e);
		}
	}
	
	/**
	 * Adds HTML style classes names to all TDs from the <code>rowsContainer</code>
	 * according to <code>columnClasses</code> attribute of the tag.
	 */
	private void addStylesToCells(nsIDOMDocument visualDocument, nsIDOMNode rowsContainer) {
		nsIDOMNodeList rowsContainerChildren = rowsContainer.getChildNodes();
		for (int j = 0; j < rowsContainerChildren.getLength(); j++) {
			nsIDOMNode tBodyChild = rowsContainerChildren.item(j);
			if (tBodyChild.getNodeType() == nsIDOMNode.ELEMENT_NODE && HTML.TAG_TR.equalsIgnoreCase(tBodyChild.getNodeName())) {
				nsIDOMNodeList rowChildren = tBodyChild.getChildNodes();
				int column = 0;
				for (int i = 0; i < rowChildren.getLength(); i++) {
					final nsIDOMNode visualChild = rowChildren.item(i);
					if ( visualChild.getNodeType() == nsIDOMNode.ELEMENT_NODE && HTML.TAG_TD.equalsIgnoreCase(visualChild.getNodeName()) ) {
						final nsIDOMNode tableCell = visualChild;
						nsIDOMNode columnStyle = tableCell.getAttributes().getNamedItem(HTML.ATTR_CLASS);
						if (columnStyle == null) {
							columnStyle = visualDocument.createAttribute(HTML.ATTR_CLASS);
						}
						columnStyle.setNodeValue(columnStyle.getNodeValue() + HTML.VALUE_CLASS_DELIMITER
								+ getColumnClass(column));
						column++;
					}
				}
			}
		}
	}

	public VpeCreationData create(final VpePageContext pageContext, final Node sourceNode, final nsIDOMDocument visualDocument) {
		final Element sourceElement = (Element)sourceNode;
		final nsIDOMElement tbody = visualDocument.createElement(HTML.TAG_TBODY);
		VpeCreationData creationData = new VpeCreationData(tbody);
		creationData = encodeSubTable(pageContext, creationData, sourceElement,
				visualDocument, tbody);
		return creationData;
	}

	protected void encodeHeader(final VpePageContext pageContext, final VpeCreationData creationData, final Element sourceElement, final nsIDOMDocument visualDocument, final nsIDOMElement parentVisualNode) {
		encodeHeaderOrFooter(
				pageContext,
				creationData,
				sourceElement,
				visualDocument,
				parentVisualNode,
				RichFaces.NAME_FACET_HEADER,
				"dr-subtable-header rich-subtable-header", //$NON-NLS-1$ 
				"dr-subtable-headercell rich-subtable-headercell"); //$NON-NLS-1$
	}

	protected void encodeFooter(final VpePageContext pageContext, final VpeCreationData creationData, final Element sourceElement, final nsIDOMDocument visualDocument, final nsIDOMElement parentVisualNode) {
		encodeHeaderOrFooter(
				pageContext,
				creationData,
				sourceElement,
				visualDocument,
				parentVisualNode,
				RichFaces.NAME_FACET_FOOTER,
				"dr-subtable-footer rich-subtable-footer", //$NON-NLS-1$  
				"dr-subtable-footercell rich-subtable-footercell"); //$NON-NLS-1$
	}

	protected void encodeHeaderOrFooter(final VpePageContext pageContext,
			final VpeCreationData creationData, final Element sourceElement,
			final nsIDOMDocument visualDocument,
			final nsIDOMElement parentVisualNode, final String facetName,
			final String trClass, final String tdClass) {

		final ArrayList<Element> columns = RichFaces.getColumns(sourceElement);
		// final ArrayList<Element> columnsHeaders =
		// ComponentUtil.getColumnsWithFacet(columns, facetName);
		final boolean hasColumnWithFacet = RichFaces.hasColumnWithFacet(columns, facetName);
		if (hasColumnWithFacet) {
			final nsIDOMElement tr = visualDocument.createElement(HTML.TAG_TR);
			parentVisualNode.appendChild(tr);
			final String styleClass = trClass;
			if (styleClass != null) {
				tr.setAttribute(HTML.ATTR_CLASS, styleClass);
			}
			RichFacesDataTableTemplate.encodeHeaderOrFooterFacets(pageContext,
					creationData, tr, visualDocument, columns, tdClass, null,
					facetName, HTML.TAG_TD);
		}
	}

	private boolean isHeader(final Element sourceElement) {
		return icludedInFacet(sourceElement, RichFaces.NAME_FACET_HEADER);
	}

	private boolean isFooter(final Element sourceElement) {
		return icludedInFacet(sourceElement, RichFaces.NAME_FACET_FOOTER);
	}

	private boolean icludedInFacet(final Element sourceElement, final String facetName) {
		final Node parent = sourceElement.getParentNode();
		return parent!=null && ComponentUtil.isFacet(parent, facetName);
	}

	protected String getHeaderClass() {
		return ST_HEADER_CLASS;
	}

	protected String getHeaderContinueClass() {
		return ST_HEADER_CONTINUE_CLASS;
	}

	protected String getFooterClass() {
		return ST_FOOTER_CLASS;
	}

	protected String getFooterContinueClass() {
		return ST_FOOTER_CONTINUE_CLASS;
	}
	
	protected String getRowClass(final int row) {
		String rowClass = DEAFAULT_CELL_CLASS;

		if (rowClasses != null) {
			final int rowClassesSize = rowClasses.size();
			if(rowClassesSize > 0) {
				rowClass = rowClasses.get(row % rowClassesSize);
			}
		}

		return rowClass;
	}

	private String getColumnClass(final int column) {
		String columnClass = DEAFAULT_CELL_CLASS;
		if (columnClasses != null) {
			final int columnClassesSize = columnClasses.size();
			if (columnClassesSize > 0) {
				columnClass = columnClasses.get(column % columnClassesSize);
			}
		}
		return columnClass;
	}

	private void initClasses(final Node sourceNode, final VpePageContext pageContext) {
		final VpeExpression exprRowClasses = RichFaces.getExprRowClasses();
		final VpeExpression exprColumnClasses = RichFaces.getExprColumnClasses();

		try {
			rowClasses = VpeClassUtil.getClasses(exprRowClasses, sourceNode,
					pageContext);
			columnClasses = VpeClassUtil.getClasses(exprColumnClasses, sourceNode,
					pageContext);
		} catch (final VpeExpressionException e) {
			throw new RuntimeException(e);
		}
	}

	protected String getHeaderBackgoundImgStyle() {
		return null;
	}


	/* (non-Javadoc)
	 * @see org.jboss.tools.vpe.editor.template.VpeAbstractTemplate#isRecreateAtAttrChange(org.jboss.tools.vpe.editor.context.VpePageContext, org.w3c.dom.Element, org.mozilla.interfaces.nsIDOMDocument, org.mozilla.interfaces.nsIDOMElement, java.lang.Object, java.lang.String, java.lang.String) */
	@Override
	public boolean recreateAtAttrChange(final VpePageContext pageContext,
			final Element sourceElement, final nsIDOMDocument visualDocument,
			final nsIDOMElement visualNode, final Object data, final String name, final String value) {
		return true;
	}


	/* (non-Javadoc)
	 * @see org.jboss.tools.vpe.editor.template.VpeAbstractTemplate#getNodeForUpdate(org.jboss.tools.vpe.editor.context.VpePageContext, org.w3c.dom.Node, org.mozilla.interfaces.nsIDOMNode, java.lang.Object) */
	@Override
	public Node getNodeForUpdate(VpePageContext pageContext, Node sourceNode,
			nsIDOMNode visualNode, Object data) {
		Node parent = sourceNode.getParentNode();
		return parent;
	}
}
