/*
 * Copyright 2011 JBoss Inc 
 *
 * Licensed 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 org.jbpm.formbuilder.client.form.items;

import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import org.jbpm.formapi.client.FormBuilderException;
import org.jbpm.formapi.client.effect.FBFormEffect;
import org.jbpm.formapi.client.form.FBFormItem;
import org.jbpm.formapi.shared.api.FormItemRepresentation;
import org.jbpm.formapi.shared.api.items.LineGraphRepresentation;
import org.jbpm.formbuilder.client.FormBuilderGlobals;
import org.jbpm.formbuilder.client.messages.I18NConstants;

import com.google.gwt.user.client.ui.Widget;
import com.google.gwt.visualization.client.AbstractDataTable.ColumnType;
import com.google.gwt.visualization.client.DataTable;
import com.google.gwt.visualization.client.visualizations.LineChart;
import com.google.gwt.visualization.client.visualizations.LineChart.Options;
import com.gwtent.reflection.client.Reflectable;

/**
 * UI form item. Represents a line graph
 */
@Reflectable
public class LineGraphFormItem extends FBFormItem {

    private final I18NConstants i18n = FormBuilderGlobals.getInstance().getI18n();

    private LineChart chart = new LineChart(DataTable.create(), Options.create());
    
    private List<List<String>> dataTableRep = new ArrayList<List<String>>();
    private List<Map.Entry<String, String>> dataStructRep = new ArrayList<Map.Entry<String, String>>();
    
    private String graphTitle;
    private String graphXTitle;
    private String graphYTitle;
    
    public LineGraphFormItem() {
        this(new ArrayList<FBFormEffect>());
    }
    
    public LineGraphFormItem(List<FBFormEffect> formEffects) {
        super(formEffects);
        setSize("200px", "150px");
        chart.setSize("200px", "150px");
        add(chart);
    }

    @Override
    public Map<String, Object> getFormItemPropertiesMap() {
        Map<String, Object> map = new HashMap<String, Object>();
        map.put("width", getWidth());
        map.put("height", getHeight());
        map.put("graphTitle", this.graphTitle);
        map.put("graphXTitle", this.graphXTitle);
        map.put("graphYTitle", this.graphYTitle);
        return map;
    }

    @Override
    public void saveValues(Map<String, Object> asPropertiesMap) {
        Map<String, Object> map = new HashMap<String, Object>();
        setWidth(extractString(asPropertiesMap.get("width")));
        setHeight(extractString(asPropertiesMap.get("height")));
        this.graphTitle = extractString(map.get("graphTitle"));
        this.graphXTitle = extractString(map.get("graphXTitle"));
        this.graphYTitle = extractString(map.get("graphYTitle"));
        populate(this.chart);
    }
    
    private int getIntValue(Object obj) {
        return Integer.valueOf(obj.toString());
    }
    
    private void populate(LineChart chart) {
        Options options = Options.create();
        options.setWidth(getOffsetWidth());
        options.setHeight(getOffsetHeight());
        options.setTitle(this.graphTitle);
        options.setTitleX(this.graphXTitle);
        options.setTitleY(this.graphYTitle);
        DataTable dataTable = DataTable.create();
        if (dataStructRep != null) {
            for (Map.Entry<String, String> col : dataStructRep) {
                dataTable.addColumn(ColumnType.valueOf(col.getValue()), col.getKey());
            }
        }
        if (dataTableRep != null) {
            for (List<String> row : dataTableRep) {
                dataTable.setValue(getIntValue(row.get(0)), getIntValue(row.get(1)), row.get(2));
            }
        }
        chart.draw(dataTable, options);  
    }

    @Override
    public FormItemRepresentation getRepresentation() {
        LineGraphRepresentation rep = super.getRepresentation(new LineGraphRepresentation());
        rep.setDataStructure(new ArrayList<Map.Entry<String, String>>(dataStructRep));
        rep.setDataTable(new ArrayList<List<String>>(dataTableRep));
        rep.setGraphTitle(graphTitle);
        rep.setGraphXTitle(graphXTitle);
        rep.setGraphYTitle(graphYTitle);
        return rep;
    }
    
    @Override
    public void populate(FormItemRepresentation rep) throws FormBuilderException {
        if (!(rep instanceof LineGraphRepresentation)) {
            throw new FormBuilderException(i18n.RepNotOfType(rep.getClass().getName(), "LineGraphRepresentation"));
        }
        super.populate(rep);
        LineGraphRepresentation grep = (LineGraphRepresentation) rep;
        this.dataStructRep = new ArrayList<Map.Entry<String, String>>(grep.getDataStructure());
        this.dataTableRep = new ArrayList<List<String>>(grep.getDataTable());
        this.graphTitle = grep.getGraphTitle();
        this.graphXTitle = grep.getGraphXTitle();
        this.graphYTitle = grep.getGraphYTitle();
        populate(this.chart);
    }

    @Override
    public FBFormItem cloneItem() {
        LineGraphFormItem item = super.cloneItem(new LineGraphFormItem(getFormEffects()));
        item.chart = (LineChart) cloneDisplay(null);
        item.dataStructRep = new ArrayList<Map.Entry<String, String>>(this.dataStructRep);
        item.dataTableRep = new ArrayList<List<String>>(this.dataTableRep);
        item.graphTitle = this.graphTitle;
        item.graphXTitle = this.graphXTitle;
        item.graphYTitle = this.graphYTitle;
        return item;
    }

    @Override
    public Widget cloneDisplay(Map<String, Object> data) {
        LineChart chart = new LineChart();
        populate(chart);
        if (getInput() != null && getInput().getName() != null) {
            DataTable dataTable = DataTable.create();
            Object myData = data.get(getInput().getName());
            populateInput(dataTable, myData);
            chart.draw(dataTable);
        }
        super.populateActions(chart.getElement());
        return chart;
    }

    private void populateInput(DataTable dataTable, Object myData) {
        if (myData.getClass().isArray()) {
            Object[] myDataArray = (Object[]) myData;
            int index = 0;
            for (Object item : myDataArray) {
                setRowDataFromInput(dataTable, index, item);
                index++;
            }
        } else if (myData instanceof Collection) {
            Collection<?> myDataCol = (Collection<?>) myData;
            int index = 0;
            for (Object item : myDataCol) {
                setRowDataFromInput(dataTable, index, item);
                index++;
            }
        } else if (myData instanceof Map) {
            Map<?, ?> myDataMap = (Map<?, ?>) myData;
            int index = 0;
            for (Object item : myDataMap.values()) {
                setRowDataFromInput(dataTable, index, item);
                index++;
            }
        }
        
    }

    private void setRowDataFromInput(DataTable dataTable, int index, Object item) {
        if (item.getClass().isArray()) {
            Object[] subObjArray = (Object[]) item;
            int columnIndex = 0;
            for (Object subObj : subObjArray) {
                String value = subObj.toString();
                dataTable.setCell(index, columnIndex, value, value, null);
                columnIndex++;
            }
        } else if (item instanceof Collection) {
            Collection<?> subObjCol = (Collection<?>) item;
            int columnIndex = 0;
            for (Object subObj : subObjCol) {
                String value = subObj.toString();
                dataTable.setCell(index, columnIndex, value, value, null);
                columnIndex++;
            }
        } else if (item instanceof Map) {
            Map<?, ?> subObjMap = (Map<?, ?>) item;
            int columnIndex = 0;
            for (Object subObj : subObjMap.values()) {
                String value = subObj.toString();
                dataTable.setCell(index, columnIndex, value, value, null);
                columnIndex++;
            }

        } else {
            String value = item.toString();
            dataTable.setCell(index, 0, value, value, null);
        }
    }

}
