/**
 * License Agreement.
 *
 *  JBoss RichFaces - Ajax4jsf Component Library
 *
 * Copyright (C) 2007  Exadel, Inc.
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License version 2.1 as published by the Free Software Foundation.
 *
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301  USA
 */

package org.richfaces.renderkit;

import java.io.IOException;
import java.util.Iterator;

import javax.faces.FacesException;
import javax.faces.component.UIColumn;
import javax.faces.component.UIComponent;
import javax.faces.context.FacesContext;
import javax.faces.context.ResponseWriter;

import org.ajax4jsf.renderkit.RendererUtils.HTML;
import org.apache.commons.collections.Predicate;
import org.apache.commons.collections.iterators.FilterIterator;
import org.richfaces.component.Column;
import org.richfaces.component.Row;
import org.richfaces.component.UIDataTable;

/**
 * @author shura
 * 
 */
public abstract class AbstractTableRenderer extends AbstractRowsRenderer {

	/**
	 * Encode all table structure - colgroups definitions, caption, header,
	 * footer
	 * 
	 * @param context
	 * @param table
	 * @throws IOException
	 */
	
//	protected final static String PERSENTAGE_SUPPORT_ERROR_MSG = "columnsWidth property: Percentage values are not supported";
	
	public void encodeTableStructure(FacesContext context, UIDataTable table)
			throws IOException {
		ResponseWriter writer = context.getResponseWriter();
		int columns = getColumnsCount(table);
		// Encode colgroup definition.
		writer.startElement("colgroup", table);
		writer.writeAttribute("span", String.valueOf(columns), null);
		String columnsWidth = (String) table.getAttributes().get("columnsWidth");
		
		
		if (null != columnsWidth) {
			// temporary solution RF-957
//			if(columnsWidth.contains("%")){
//				throw new FacesException(PERSENTAGE_SUPPORT_ERROR_MSG); 
//			}
			String[] widths = columnsWidth.split(",");
			for (int i = 0; i < widths.length; i++) {
				writer.startElement("col", table);
				writer.writeAttribute("width", widths[i], null);
				writer.endElement("col");
			}
		}
		writer.endElement("colgroup");
		encodeCaption(context, table);
		encodeHeader(context, table, columns);
		encodeFooter(context, table, columns);
	}

	public void encodeHeader(FacesContext context, UIDataTable table,
			int columns) throws IOException {
		ResponseWriter writer = context.getResponseWriter();
		UIComponent header = table.getHeader();
		Iterator headers = columnFacets(table,"header");
		
		
		if (header != null ||headers.hasNext()) {
			writer.startElement("thead", table);
			String headerClass = (String) table.getAttributes().get(
					"headerClass");
			if (header != null) {
				encodeTableHeaderFacet(context, columns, writer, header,
						"dr-table-header rich-table-header",
						"dr-table-header-continue rich-table-header-continue",
						"dr-table-headercell rich-table-headercell",
						headerClass, "th");
			}

			if (headers.hasNext()&&findFacet(table,"header")) {
				writer.startElement("tr", table);
				encodeStyleClass(writer, null,
						"dr-table-subheader rich-table-subheader", null,
						headerClass);
					encodeHeaderFacets(context, writer, headers,
						"dr-table-subheadercell rich-table-subheadercell",
						headerClass, "header", "th");
				
				writer.endElement("tr");
			}
			writer.endElement("thead");
		}
	}
	
	public boolean findFacet(UIDataTable table, String facetName)
	{
		Iterator iter = columnFacets(table,facetName);
		boolean find = false;
		while(iter.hasNext()){
			UIComponent comp = (UIComponent) iter.next();
			if(comp.getFacet(facetName)!=null)
			{
				find = true;
				break;
			}
		
		}
		return find;
	}
	
	protected void encodeHeaderFacets(FacesContext context,
			ResponseWriter writer, Iterator headers, String skinCellClass,
			String headerClass, String facetName, String element)
			throws IOException {
		while (headers.hasNext()) {
			UIComponent column = (UIComponent) headers.next();
			String classAttribute = facetName + "Class";
			String columnHeaderClass = (String) column.getAttributes().get(
					classAttribute);
			
			writer.startElement(element, column);
			encodeStyleClass(writer, null, skinCellClass, headerClass,
					columnHeaderClass);
			writer.writeAttribute("scope", "col", null);
			getUtils().encodeAttribute(context, column, "colspan");
			UIComponent facet = column.getFacet(facetName);
			if (facet != null) {
				renderChild(context, facet);
			}
			
			writer.endElement(element);
		}
	}

	public void encodeFooter(FacesContext context, UIDataTable table,
			int columns) throws IOException {
		ResponseWriter writer = context.getResponseWriter();
		UIComponent footer = table.getFooter();
		Iterator footers = columnFacets(table,"footer");
		if (footer != null || footers.hasNext()) {
			writer.startElement("tfoot", table);
			String footerClass = (String) table.getAttributes().get(
					"footerClass");

			if (footers.hasNext()&&findFacet(table,"footer")) {
				writer.startElement("tr", table);
				encodeStyleClass(writer, null,
						"dr-table-subfooter rich-table-subfooter", null,
						footerClass);
		
				encodeHeaderFacets(context, writer, footers,
						"dr-table-subfootercell rich-table-subfootercell",
						footerClass, "footer", "td");
				
				writer.endElement("tr");
			}
			if (footer != null) {
				encodeTableHeaderFacet(context, columns, writer, footer,
						"dr-table-footer rich-table-footer",
						"dr-table-footer-continue rich-table-footer-continue",
						"dr-table-footercell rich-table-footercell",
						footerClass, "td");
			}
			writer.endElement("tfoot");
		}

	}

	public void encodeOneRow(FacesContext context, TableHolder holder)
			throws IOException {
		UIDataTable table = (UIDataTable) holder.getTable();
		ResponseWriter writer = context.getResponseWriter();
		Iterator iter = table.columns();
		boolean first = true;
		int currentColumn = 0;
		UIComponent column = null;
		while (iter.hasNext()) {
			column = (UIComponent) iter.next();
			// Start new row for first column - expect a case of the detail
			// table, wich will be insert own row.
			if (first && !(column instanceof Row)) {
				encodeRowStart(context, getFirstRowSkinClass(), holder
						.getRowClass(), table, writer);
			}
			if (column instanceof Column) {
				boolean breakBefore = ((Column) column).isBreakBefore()
						|| column instanceof Row;
				if (breakBefore && !first) {
					// close current row
					writer.endElement(HTML.TR_ELEMENT);
					// reset columns counter.
					currentColumn = 0;
					// Start new row, expect a case of the detail table, wich
					// will be insert own row.
					if (!(column instanceof Row)) {
						holder.nextRow();
						encodeRowStart(context, holder.getRowClass(), table,
								writer);
					}
				}
				encodeCellChildren(context, column,
						first ? getFirstRowSkinClass() : null,
						getRowSkinClass(), holder.getRowClass(),
						getCellSkinClass(), holder
								.getColumnClass(currentColumn));
				// renderChild(context, column);
				if ((column instanceof Row) && iter.hasNext()) {
					// Start new row for remained columns.
					holder.nextRow();
					encodeRowStart(context, holder.getRowClass(), table, writer);
					// reset columns counter.
					currentColumn = -1;
				}
			} else if (column.isRendered()) {
				// UIColumn don't have own renderer
				writer.startElement(HTML.td_ELEM, table);
				getUtils().encodeId(context, column);
				String columnClass = holder.getColumnClass(currentColumn);
				encodeStyleClass(writer, null, getCellSkinClass(), null,
						columnClass);
				// TODO - encode column attributes.
				renderChildren(context, column);
				writer.endElement(HTML.td_ELEM);
			}
			currentColumn++;
			first = false;
		}
		// Close row if then is open.
		if (!first && !(column instanceof Row)) {
			writer.endElement(HTML.TR_ELEMENT);
		}
	}

	protected void encodeRowStart(FacesContext context, String rowClass,
			UIDataTable table, ResponseWriter writer) throws IOException {
		encodeRowStart(context, getRowSkinClass(), rowClass, table, writer);
	}

	/**
	 * @return
	 */
	protected String getRowSkinClass() {
		return "dr-table-row rich-table-row";
	}

	/**
	 * @return
	 */
	protected String getFirstRowSkinClass() {
		return "dr-table-firstrow rich-table-firstrow";
	}

	/**
	 * @return
	 */
	protected String getCellSkinClass() {
		return "dr-table-cell rich-table-cell";
	}

	protected void encodeRowStart(FacesContext context, String skinClass,
			String rowClass, UIDataTable table, ResponseWriter writer)
			throws IOException {
		writer.startElement(HTML.TR_ELEMENT, table);
		encodeStyleClass(writer, null, skinClass, null, rowClass);
		encodeRowEvents(context, table);
	}

	/*
	 * protected Iterator columnFacets(UIDataTable table) { return
	 * table.columns();
	 * 
	 * Changed by Alexej Kushunin
	 */
	protected Iterator columnFacets(UIDataTable table,final String name){	
			return new FilterIterator(table.columns(), new Predicate() {
	
			public boolean evaluate(Object input) {
				UIComponent component = (UIComponent) input;
				// accept only columns with corresponding facets.
				 if (component instanceof Column || component instanceof UIColumn) {
					return component.isRendered()&&(component.getFacet(name) != null);			
				}
				 else{
					 return false;		
				 }
			}});
	}

	/**
	 * Calculate total number of columns in table.
	 * 
	 * @param context
	 * @param table
	 * @return
	 */
	protected int getColumnsCount(UIDataTable table) {
		int count = 0;
		// check for exact value in component
		Integer span = (Integer) table.getAttributes().get("columns");
		if (null != span && span.intValue() != Integer.MIN_VALUE) {
			count = span.intValue();
		} else {
			// calculate max html columns count for all columns/rows children.
			Iterator col = table.columns();
			count = calculateRowColumns(col);
		}
		return count;
	}

	/**
	 * Calculate max number of columns per row. For rows, recursive calculate
	 * max length.
	 * 
	 * @param col -
	 *            Iterator other all columns in table.
	 * @return
	 */
	protected int calculateRowColumns(Iterator col) {
		int count = 0;
		int currentLength = 0;
		while (col.hasNext()) {
			UIComponent column = (UIComponent) col.next();
			if (column.isRendered()) {
				if (column instanceof Row) {
					// Store max calculated value of previsous rows.
					if (currentLength > count) {
						count = currentLength;
					}
					// Calculate number of columns in row.
					currentLength = calculateRowColumns(((Row) column)
							.columns());
					// Store max calculated value
					if (currentLength > count) {
						count = currentLength;
					}
					currentLength = 0;
				} else if (column instanceof Column) {
					Column tableColumn = (Column) column;
					// For new row, save length of previsous.
					if (tableColumn.isBreakBefore()) {
						if (currentLength > count) {
							count = currentLength;
						}
						currentLength = 0;
					}
					Integer colspan = (Integer) column.getAttributes().get(
							"colspan");
					// Append colspan of this column
					if (null != colspan
							&& colspan.intValue() != Integer.MIN_VALUE) {
						currentLength += colspan.intValue();
					} else {
						currentLength++;
					}
				} else if (column instanceof UIColumn) {
					// UIColumn always have colspan == 1.
					currentLength++;
				}

			}
		}
		if (currentLength > count) {
			count = currentLength;
		}
		return count;
	}
}
