package com.mars.helios.heliosweb.client;

import com.google.gwt.core.client.EntryPoint;
import com.google.gwt.core.client.GWT;
import com.google.gwt.user.client.Timer;
import com.smartgwt.client.core.Function;
import com.smartgwt.client.data.DataSource;
import com.smartgwt.client.data.DataSourceField;
import com.smartgwt.client.data.Record;
import com.smartgwt.client.data.fields.DataSourceTextField;
import com.smartgwt.client.util.SC;
import com.smartgwt.client.widgets.cube.CubeGrid;
import com.smartgwt.client.widgets.cube.Facet;
import com.smartgwt.client.widgets.cube.FacetValue;
import com.smartgwt.client.widgets.cube.FacetValueMap;
import com.smartgwt.client.widgets.events.ClickEvent;
import com.smartgwt.client.widgets.events.ClickHandler;
import com.smartgwt.client.widgets.form.DynamicForm;
import com.smartgwt.client.widgets.form.fields.SelectItem;
import com.smartgwt.client.widgets.form.fields.events.ChangedEvent;
import com.smartgwt.client.widgets.form.fields.events.ChangedHandler;
import com.smartgwt.client.widgets.grid.ListGridRecord;
import com.smartgwt.client.widgets.layout.Layout;
import com.smartgwt.client.widgets.layout.VLayout;
import com.smartgwt.client.widgets.toolbar.ToolStrip;
import com.smartgwt.client.widgets.toolbar.ToolStripButton;
import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.List;
import java.util.Random;

/**
 * Entry point classes define
 * <code>onModuleLoad()</code>.
 */
public class CubeGridSample implements EntryPoint {

    private static final int ROW_COUNT = 50;
    private static final int COL_COUNT = 50;
    private static final int FILL_RATIO = 50; // (0, 25, 50, 75, 100)

    private int currentFillRatio = FILL_RATIO;

    private Layout cubeGridLayout;
    private ToolStrip toolStrip;
    private CubeGrid cubeGrid;

    /**
     * This is the entry point method.
     */
    @Override
    public void onModuleLoad() {
        GWT.setUncaughtExceptionHandler(new HeliosUncaughtExceptionHandler());
        // see http://code.google.com/p/google-web-toolkit/issues/detail?id=1617
        Timer timer = new Timer() {
            @Override
            public void run() {
                cubeGridLayout = new VLayout();
                cubeGridLayout.setWidth100();
                cubeGridLayout.setHeight100();
                cubeGridLayout.setMargin(5);
                cubeGridLayout.setPadding(5);

                buildCubeGrid();
                buildToolbar();

                cubeGridLayout.addMember(toolStrip);
                cubeGridLayout.addMember(cubeGrid);

                cubeGridLayout.draw();
            }
        };

        timer.schedule(1);
    }

    private void buildToolbar() {

        // Layout
        toolStrip = new ToolStrip();

        ToolStripButton showConsole = new ToolStripButton("Show console");
        showConsole.addClickHandler(new ClickHandler() {

            @Override
            public void onClick(ClickEvent event) {
                SC.showConsole();
            }
        });
        toolStrip.addButton(showConsole);
        ToolStripButton btSetRandomValue = new ToolStripButton("Set Random Values");
        toolStrip.addButton(btSetRandomValue);
        ToolStripButton btDelete = new ToolStripButton("Delete values");
        toolStrip.addButton(btDelete);
        toolStrip.addSpacer(10);
        ToolStripButton btApply = new ToolStripButton("Apply values");
        toolStrip.addButton(btApply);
        ToolStripButton btIncrement = new ToolStripButton("+1");
        toolStrip.addButton(btIncrement);
        ToolStripButton btReload = new ToolStripButton("Reload");
        toolStrip.addButton(btReload);
        toolStrip.addSpacer(10);
        ToolStripButton btSum = new ToolStripButton("Sum Rows");
        toolStrip.addButton(btSum);

        DynamicForm cubeParametersForm = new DynamicForm();
        SelectItem fillPercent = new SelectItem("fillPercent", "Fill %");
        fillPercent.setValueMap("0", "25", "50", "75", "100");
        fillPercent.setValue(FILL_RATIO);
        fillPercent.setWrapTitle(false);

        cubeParametersForm.setFields(fillPercent);
        toolStrip.addMember(cubeParametersForm);

        // Events
        btSetRandomValue.addClickHandler(new SetToRandomHandler());
        btDelete.addClickHandler(new DeleteHandler());
        btApply.addClickHandler(new ApplyHandler());
        btIncrement.addClickHandler(new IncrementHandler());
        btSum.addClickHandler(new SumHandler());
        btReload.addClickHandler(new ReloadHandler());
        fillPercent.addChangedHandler(new ChangedHandler() {

            @Override
            public void onChanged(ChangedEvent event) {
                currentFillRatio = Integer.valueOf((String) event.getValue());
                cubeGridLayout.removeMember(cubeGrid);
                buildCubeGrid();
                cubeGridLayout.addMember(cubeGrid);
                // No one works to clear the data
//                cubeGrid.discardAllEdits();

//                cubeGrid.setDataSource(getDataSource());
//                cubeGrid.setData(new Record[0]);
//                cubeGrid.invalidateCache();
////                cubeGrid.fetchData();
//                cubeGrid.setData(getRecords(ROW_COUNT, COL_COUNT));
            }
        });
    }

    public void buildCubeGrid() {
        cubeGrid = new CubeGrid();
        cubeGrid.setHeight100();
        cubeGrid.setWidth100();
        cubeGrid.setAutoFetchData(true);
        cubeGrid.setCanEdit(true);

//        cubeGrid.setDataSource(getDataSource());

        Facet rowFacet = createFacet("row", ROW_COUNT);
        Facet colFacet = createFacet("col", COL_COUNT);

        cubeGrid.setData(getRecords(ROW_COUNT, COL_COUNT));

        cubeGrid.setFacets(rowFacet, colFacet);
        cubeGrid.setRowFacets("row");
        cubeGrid.setColumnFacets("col");
//        cubeGrid.setEmptyCellValue("0");
        cubeGrid.show();
    }

    private Facet createFacet(String rowName, int size) {
        Facet rowFacet = new Facet(rowName, rowName);
        List<FacetValue> facetList = new ArrayList<FacetValue>(size);
        for (int i = 0; i < size; i++) {
            facetList.add(new FacetValue("" + i, "" + i));
        }
        rowFacet.setValues(facetList.toArray(new FacetValue[size]));
        return rowFacet;
    }

    private ListGridRecord[] getRecords(int row, int col) {
        List<ListGridRecord> result = new ArrayList<ListGridRecord>(row * col);
        for (int i = 0; i < row; i++) {
            for (int j = 0; j < col; j++) {
                if (Math.random() < (currentFillRatio / 100d)) {
                    ListGridRecord record = new ListGridRecord();

                    record.setAttribute("row", i);
                    record.setAttribute("col", j);
                    record.setAttribute("_value", (int) (Math.random() * 10 + 1));
                    result.add(record);
                }
            }
        }

        return result.toArray(new ListGridRecord[result.size()]);
    }

    private class SetToRandomHandler implements ClickHandler {

        private Random random = new Random();

        @Override
        public void onClick(ClickEvent event) {
            long start = System.currentTimeMillis();
            for (int[] cellCoord : cubeGrid.getCellSelection().getSelectedCells()) {
                final int row = cellCoord[0];
                final int col = cellCoord[1];

                cubeGrid.setEditValue(row, col, random.nextInt(1000));
            }

            long end = System.currentTimeMillis();
            displayDuration("Random", end - start);
        }
    }

    private class DeleteHandler implements ClickHandler {

        @Override
        public void onClick(ClickEvent event) {
            cubeGrid.removeSelectedData();
        }
    }

    private class ApplyHandler implements ClickHandler {

        @Override
        public void onClick(ClickEvent event) {
            final long start = System.currentTimeMillis();
            if (cubeGrid.hasChanges()) {
                cubeGrid.saveAllEdits(new Function() {
                    @Override
                    public void execute() {
                        long end = System.currentTimeMillis();
                        displayDuration("Save", end - start);
                    }
                });
            }
        }
    }

    private class IncrementHandler implements ClickHandler {

        @Override
        public void onClick(ClickEvent event) {
            long start = System.currentTimeMillis();
            for (int[] cellCoord : cubeGrid.getCellSelection().getSelectedCells()) {
                final int row = cellCoord[0];
                final int col = cellCoord[1];

                Object value = cubeGrid.getEditValue(row, col);

                if (value == null) {
                    ListGridRecord cellRecord = cubeGrid.getCellRecord(row, col);
                    if (cellRecord != null) {
                        value = cellRecord.getAttribute(cubeGrid.getValueProperty());
                    }
                }

                if (value != null) {
                    try {
                        Integer valueInt = Integer.parseInt(value.toString());
                        value = Integer.toString(valueInt + 1);
                        cubeGrid.setEditValue(row, col, value);
                    } catch (NumberFormatException e) {
                        // Do nothing
                    }
                }
            }
            long end = System.currentTimeMillis();

            displayDuration("Increment", end - start);
        }
    }

    /**
     * Compute the sum of all columns in the last column.
     */
    private class SumHandler implements ClickHandler {

        @Override
        public void onClick(ClickEvent event) {
            String valueProperty = cubeGrid.getValueProperty();

            for (int row = 0; row < 10; row++) {
                boolean isFirst = true;
                BigDecimal sum = BigDecimal.ZERO;

                for (String periodValue : cubeGrid.getColumnFacets()) {
                    if (isFirst) {
                        isFirst = false;
                        continue;
                    }
                    FacetValueMap valueMap = cubeGrid.getCellFacetValues(row, 0);
                    valueMap.addMapping("Period", periodValue);

                    int calcRow = cubeGrid.getFacetValuesRow(valueMap);
                    int calcCol = cubeGrid.getFacetValuesColumn(valueMap);

                    Object currentValue = cubeGrid.getEditValue(calcRow, calcCol);
                    if (currentValue == null) {
                        Record record = cubeGrid.getCellRecord(calcRow, calcCol);
                        if (record != null) {
                            currentValue = record.getAttribute(valueProperty);
                        } else {
                            currentValue = "";
                        }
                    }

                    String currentValueString = currentValue.toString();
                    BigDecimal curBigValue = new BigDecimal(
                            currentValueString.isEmpty() ? "0" : currentValueString);
                    sum = sum.add(curBigValue);
                }

                FacetValueMap valueMap = cubeGrid.getCellFacetValues(row, 0);
                int calcRow = cubeGrid.getFacetValuesRow(valueMap);
                int calcCol = cubeGrid.getFacetValuesColumn(valueMap);
                cubeGrid.setEditValue(calcRow, calcCol, sum);
            }
        }
    }

    /**
     * Force cache reload of all data.
     */
    private class ReloadHandler implements ClickHandler {

        @Override
        public void onClick(ClickEvent event) {
            cubeGrid.invalidateCache();
            cubeGrid.fetchData();
        }
    }

    private void displayDuration(String operation, long duration) {
        int size = cubeGrid.getCellSelection().getSelectedCells().length;
        SC.say("Duration for operation " + operation + " : "
                + duration + "ms (" + size + " cells) => time per cell : " + (double) duration / size + "ms");
    }

    private DataSource getDataSource() {
        DataSource dataSource = new DataSource();
        dataSource.setID("clientDS");
        DataSourceField row = new DataSourceTextField("row");
        DataSourceField col = new DataSourceTextField("col");
        DataSourceField value = new DataSourceTextField(cubeGrid.getValueProperty());
        dataSource.setFields(row, col, value);
        for (Record rec : getRecords(ROW_COUNT, COL_COUNT)) {
            dataSource.addData(rec);
        }
        dataSource.setClientOnly(true);
        return dataSource;
    }
}
