package it.mice.voila.runtime.displaytag.exporter;

import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;

import javax.servlet.jsp.PageContext;

import org.apache.commons.lang.ObjectUtils;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.displaytag.decorator.TotalTableDecorator;
import org.displaytag.exception.ObjectLookupException;
import org.displaytag.export.BinaryExportView;
import org.displaytag.model.HeaderCell;
import org.displaytag.model.TableModel;
import org.displaytag.util.LookupUtil;

public abstract class AbstractTotalExporter implements BinaryExportView {
    /**
     * Logger.
     */
    private static Log log = LogFactory.getLog(TotalTableDecorator.class);
    /**
     * total amount.
     */
    private Map<String, Double> grandTotals = new HashMap<String, Double>();

    /**
     * total amount for current group.
     */
    private Map<String, Double> subTotals = new HashMap<String, Double>();

    /**
     * Previous values needed for grouping.
     */
    private Map<String, Object> previousValues = new HashMap<String, Object>();

    /**
     * Name of the property used for grouping.
     */
    private String groupPropertyName;

    private TableModel tableModel;

    /**
     * @see org.displaytag.decorator.Decorator#init(PageContext, Object, TableModel)
     */
    public void init(TableModel tableModel)
    {
        // reset
        groupPropertyName = null;
        grandTotals.clear();
        subTotals.clear();
        previousValues.clear();
        
        this.tableModel = tableModel;

        for (Iterator<HeaderCell> it = tableModel.getHeaderCellList().iterator(); it.hasNext();)
        {
            HeaderCell cell = it.next();
            if (cell.getGroup() == 1)
            {
                groupPropertyName = cell.getBeanPropertyName();
            }
        }
    }

    public Map<String, Double> startRow(Object currentObject)
    {
    	Map<String, Double> subtotalRow = null;

        if (groupPropertyName != null)
        {
            Object groupedPropertyValue = evaluate(groupPropertyName, currentObject);
            Object previousGroupedPropertyValue = previousValues.get(groupPropertyName);
            // subtotals
            if (previousGroupedPropertyValue != null
                && !ObjectUtils.equals(previousGroupedPropertyValue, groupedPropertyValue))
            {
                subtotalRow = new HashMap<String, Double>();
                subtotalRow.putAll(subTotals);
                // reset subtotal
                this.subTotals.clear();
            }
            previousValues.put(groupPropertyName, groupedPropertyValue);
        }

        for (Iterator<HeaderCell> it = tableModel.getHeaderCellList().iterator(); it.hasNext();)
        {
            HeaderCell cell = it.next();
            if (cell.isTotaled())
            {
                String totalPropertyName = cell.getBeanPropertyName();
                Number amount = (Number) evaluate(totalPropertyName, currentObject);

                Number previousSubTotal = subTotals.get(totalPropertyName);
                Number previousGrandTotals = grandTotals.get(totalPropertyName);

                subTotals.put(totalPropertyName, new Double((previousSubTotal != null
                    ? previousSubTotal.doubleValue()
                    : 0)
                    + (amount != null ? amount.doubleValue() : 0)));

                grandTotals.put(totalPropertyName, new Double((previousGrandTotals != null ? previousGrandTotals
                    .doubleValue() : 0)
                    + (amount != null ? amount.doubleValue() : 0)));
            }
        }

        return subtotalRow;
    }

    protected Map<String, Double> getTotalRow()
    {
        return grandTotals;
    }

    /**
     * Shortcut for evaluating properties in the current row object. Can be useful for implementing anonymous decorators
     * in jsp pages without having to know/import the decorated object Class.
     * @param propertyName property to lookup in current row object. Can also be a nested or indexed property.
     * @since 1.1
     */
    protected Object evaluate(String propertyName, Object currentObject)
    {
        try
        {
            return LookupUtil.getBeanProperty(currentObject, propertyName);
        }
        catch (ObjectLookupException e)
        {
            return null;
        }
    }

}
