/*
 * Licensed to the Apache Software Foundation (ASF) under one or more
 * contributor license agreements.  See the NOTICE file distributed with
 * this work for additional information regarding copyright ownership.
 * The ASF licenses this file to You under the Apache License, Version 2.0
 * (the "License"); you may not use this file except in compliance with
 * the License.  You may obtain a copy of the License at
 * 
 *      http://www.apache.org/licenses/LICENSE-2.0
 * 
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package ro.nextreports.server.web.pivot;

import java.io.Serializable;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import org.apache.wicket.AttributeModifier;
import org.apache.wicket.Component;
import org.apache.wicket.markup.html.WebMarkupContainer;
import org.apache.wicket.markup.html.basic.Label;
import org.apache.wicket.markup.html.panel.Panel;
import org.apache.wicket.markup.repeater.RepeatingView;
import org.apache.wicket.model.Model;
import org.apache.wicket.util.collections.MultiMap;
import org.apache.wicket.util.convert.IConverter;

import ro.nextreports.server.pivot.PivotField;
import ro.nextreports.server.pivot.PivotModel;
import ro.nextreports.server.pivot.PivotUtils;


/**
 * @author Decebal Suiu
 */
public class PivotTable extends Panel {

	private static final long serialVersionUID = 1L;

	public PivotTable(String id, PivotModel pivotModel) {
		super(id);
		
		List<PivotField> columnFields = pivotModel.getFields(PivotField.Area.COLUMN);
		List<PivotField> rowFields = pivotModel.getFields(PivotField.Area.ROW);
		List<PivotField> dataFields = pivotModel.getFields(PivotField.Area.DATA);
		
		List<List<Object>> rowKeys = pivotModel.getRowKeys();
		List<List<Object>> columnKeys = pivotModel.getColumnKeys();
		
		// rendering header
		RepeatingView column = new RepeatingView("header");
		add(column);
		int headerRowCount = columnFields.size();
		if (headerRowCount == 0) {
			headerRowCount = 1;
		}
		if (dataFields.size() > 1) {
			// add an extra row (the row with data field titles)
			headerRowCount++;
		}
		
		Component tmp = null;
		for (int i = 0; i < headerRowCount; i++) {
			// rendering row header (first columns)
			WebMarkupContainer tr = new WebMarkupContainer(column.newChildId());
			column.add(tr);
			RepeatingView rowHeader = new RepeatingView("rowHeader");
			tr.add(rowHeader);
			
			for (int j = 0; j < rowFields.size(); j++) {
				if (i < headerRowCount - 1) {
					// rendering an empty cell
					tmp = new Label(rowHeader.newChildId(), "");
					tmp.add(AttributeModifier.append("class", "empty"));
					rowHeader.add(tmp);
				} else {
					// rendering row field
					tmp = createTitleLabel(rowHeader.newChildId(), rowFields.get(j));
					rowHeader.add(tmp);
				}
			}
			
			// rendering column keys
			RepeatingView value = new RepeatingView("value");
			tr.add(value);
			for (List<Object> columnKey : columnKeys) {
				if (i < columnFields.size()) {
					PivotField columnField = columnFields.get(i);
					tmp = createValueLabel(value.newChildId(), columnKey.get(i), columnField);
					tmp.add(AttributeModifier.append("colspan", dataFields.size()));
					value.add(tmp);
				} else {
					for (PivotField dataField : dataFields) {
						tmp = createTitleLabel(value.newChildId(), dataField);
						value.add(tmp);
					}
				}
			}
			
			// rendering grand total column
			RepeatingView grandTotalColumn = new RepeatingView("grandTotalColumn");
			if (i == 0) {
				tmp = new Label(grandTotalColumn.newChildId(), getString("pivot.grandTotal"));
				tmp.add(AttributeModifier.append("colspan", dataFields.size()));
				grandTotalColumn.add(tmp);
			} else if (i < columnFields.size()) {
				tmp = new WebMarkupContainer(grandTotalColumn.newChildId());
				tmp.add(AttributeModifier.append("colspan", dataFields.size()));
				tmp.add(AttributeModifier.append("class", "empty"));
				grandTotalColumn.add(tmp);
			} else {
				for (PivotField dataField : dataFields) {
					tmp = createTitleLabel(value.newChildId(), dataField);
					grandTotalColumn.add(tmp);
				}				
			}
			grandTotalColumn.setVisible(!columnFields.isEmpty() && pivotModel.isShowGrandTotalForRow());
			tr.add(grandTotalColumn);
		}
		
		// rendering rows
		RepeatingView row = new RepeatingView("row");
		add(row);
		for (List<Object> rowKey : rowKeys) {
			WebMarkupContainer tr = new WebMarkupContainer(row.newChildId());
			row.add(tr);
			RepeatingView rowHeader = new RepeatingView("rowHeader");
			tr.add(rowHeader);

			for (int k = 0; k < rowKey.size(); k++) {
				PivotField rowField = rowFields.get(k);
				tmp = createValueLabel(rowHeader.newChildId(), rowKey.get(k), rowField);
				rowHeader.add(tmp);
			}
			
			RepeatingView value = new RepeatingView("value");
			tr.add(value);
			
			for (List<Object> columnKey : columnKeys) {
				for (PivotField dataField : dataFields) {
					Number cellValue = (Number) pivotModel.getValueAt(dataField, rowKey, columnKey);
					tmp = createValueLabel(value.newChildId(), cellValue, dataField);				
					value.add(tmp);					
				}
			}
				
			if (!columnFields.isEmpty() && pivotModel.isShowGrandTotalForRow()) {
				MultiMap<PivotField, Object> values = new MultiMap<PivotField, Object>();
				for (List<Object> columnKey: columnKeys) {
					for (PivotField dataField : dataFields) {
						values.addValue(dataField, pivotModel.getValueAt(dataField, rowKey, columnKey));
					}
				}
				for (PivotField dataField : dataFields) {
                    double grandTotalForRow = 0.0d;

                    List<Object> items = values.get(dataField);
                    for (Object item : items) {
                        if (item != null) {
                            grandTotalForRow += ((Number) item).doubleValue();
                        }
                    }

                    tmp = createGrandTotalLabel(value.newChildId(), grandTotalForRow, true);
					tmp.add(AttributeModifier.append("class", "grand-total"));
					value.add(tmp);
				}
			}
		}
		
		WebMarkupContainer grandTotalRow = new WebMarkupContainer("grandTotalRow");
		grandTotalRow.setVisible(!rowFields.isEmpty() && pivotModel.isShowGrandTotalForColumn());
		add(grandTotalRow);
		
		Label grandTotalRowHeader = new Label("rowHeader", getString("pivot.grandTotal"));
		grandTotalRowHeader.add(AttributeModifier.append("colspan", rowFields.size()));
		grandTotalRow.add(grandTotalRowHeader);
		
		RepeatingView value = new RepeatingView("value");
		grandTotalRow.add(value);
		Map<PivotField, Double> grandTotal = new HashMap<PivotField, Double>();
		for (List<Object> columnKey : columnKeys) {
			MultiMap<PivotField, Object> values = new MultiMap<PivotField, Object>();
			for (List<Object> rowKey: rowKeys) {
				for (PivotField dataField : dataFields) {
					values.addValue(dataField, pivotModel.getValueAt(dataField, rowKey, columnKey));
				}
			}
			for (PivotField dataField : dataFields) {
                double grandTotalForColumn = 0.0d;

                List<Object> items = values.get(dataField);
                for (Object item : items) {
                    if (item != null) {
                        grandTotalForColumn += ((Number) item).doubleValue();
                    }
                }

				if (!grandTotal.containsKey(dataField)) {
					grandTotal.put(dataField, grandTotalForColumn);
				} else {
					grandTotal.put(dataField, grandTotal.get(dataField) + grandTotalForColumn);
				}
				tmp = createGrandTotalLabel(value.newChildId(), grandTotalForColumn, false);
				value.add(tmp);
			}
		}
		if (!columnFields.isEmpty() && pivotModel.isShowGrandTotalForRow()) {
			for (PivotField dataField : dataFields) {
				tmp = createGrandTotalLabel(value.newChildId(), grandTotal.get(dataField), true);
				value.add(tmp);
			}
		}
	}

	/**
	 * Retrieves a label that display the pivot table title (for fields on ROW and DATA areas) 
	 */
	protected Label createTitleLabel(String id, PivotField pivotField) {
		String title = pivotField.getTitle();
		if (pivotField.getArea().equals(PivotField.Area.DATA)) {
			title += " (" + pivotField.getAggregator().getFunction().toUpperCase() + ")"; 
		}

		return new Label(id, title);
	}

	protected Label createValueLabel(String id, Object value, final PivotField pivotField) {
		return new Label(id, Model.of((Serializable) value)) {
			
			private static final long serialVersionUID = 1L;

			@SuppressWarnings("unchecked")
			@Override
			public <C> IConverter<C> getConverter(Class<C> type) {
				IConverter<C> converter = (IConverter<C>) pivotField.getConverter();
				if (converter != null) {
					return converter;
				}
				
				return super.getConverter(type);
			}

		};
	}
	
	protected Label createGrandTotalLabel(String id, Object value, boolean forRow) {
		return new Label(id, Model.of((Serializable) value));
	}

}
