package com.mars.helios.heliosweb.client.screen.debug;

import com.google.gwt.user.client.Timer;
import com.google.gwt.user.client.rpc.AsyncCallback;
import com.google.inject.Inject;
import com.google.inject.Singleton;
import com.mars.helios.heliosweb.client.connection.ConnectionController;
import com.mars.helios.heliosweb.client.datasource.HeliosGridDS;
import com.mars.helios.heliosweb.shared.gridmodels.SampleGridData;
import com.mars.helios.heliosweb.shared.service.RemoteGridCacheServiceAsync;
import com.smartgwt.client.types.Alignment;
import com.smartgwt.client.widgets.Canvas;
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.events.ClickEvent;
import com.smartgwt.client.widgets.events.ClickHandler;
import com.smartgwt.client.widgets.form.DynamicForm;
import com.smartgwt.client.widgets.form.events.SubmitValuesEvent;
import com.smartgwt.client.widgets.form.events.SubmitValuesHandler;
import com.smartgwt.client.widgets.form.fields.CheckboxItem;
import com.smartgwt.client.widgets.form.fields.SliderItem;
import com.smartgwt.client.widgets.form.fields.SubmitItem;
import com.smartgwt.client.widgets.grid.ListGridRecord;
import com.smartgwt.client.widgets.grid.events.EditorExitEvent;
import com.smartgwt.client.widgets.grid.events.EditorExitHandler;
import com.smartgwt.client.widgets.layout.VLayout;
import com.smartgwt.client.widgets.toolbar.ToolStrip;
import com.smartgwt.client.widgets.toolbar.ToolStripButton;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Random;
import java.util.Set;
import java.util.logging.Logger;

/**
 * Sample CubeGrid with dynamic remote data used to test lazy loading features.
 *
 * @author Yannick Kirschhoffer <yannick.kirschhoffer@cgi.com>
 */
@Singleton
public class DynamicCubeGridSection {

    @Inject
    private RemoteGridCacheServiceAsync gridCacheService;
    @Inject
    private ConnectionController connectionController;
    private Logger logger = Logger.getLogger(DynamicCubeGridSection.class.getName());
    private ToolStripButton btSetRandomValue;
    private ToolStripButton btDelete;
    private ToolStripButton btApply;
    private ToolStripButton btIncrement;
    private CheckboxItem boxChanges;
    private VLayout cubeGridLayout = new VLayout();
    private DynamicForm form = new DynamicForm();

    /**
     * Create the target canvas for the cube.
     *
     * @return the cube grid container canvas
     */
    public Canvas buildCubeGrid() {

        cubeGridLayout.setHeight100();
        cubeGridLayout.setWidth100();

        final SliderItem dimension1 = new SliderItem();
        dimension1.setTitle("Dimension 1 Items");
        dimension1.setMinValue(1);
        dimension1.setMaxValue(10000);
        dimension1.setValue(26);
        dimension1.setRequired(true);
        final SliderItem dimension2 = new SliderItem();
        dimension2.setMinValue(1);
        dimension2.setMaxValue(10000);
        dimension2.setValue(26);
        dimension2.setTitle("Dimension 2 Items");
        dimension2.setRequired(true);
        final SliderItem dimension3 = new SliderItem();
        dimension3.setMinValue(1);
        dimension3.setMaxValue(10000);
        dimension3.setValue(13);
        dimension3.setTitle("Dimension 3 Items");
        dimension3.setRequired(true);
        final SliderItem fillRatio = new SliderItem();
        fillRatio.setTitle("Fill Ratio");
        fillRatio.setMinValue(0);
        fillRatio.setMaxValue(100);
        fillRatio.setValue(70);
        fillRatio.setRequired(true);

        SubmitItem openGrid = new SubmitItem();
        openGrid.setTitle("Open Grid");
        openGrid.setWidth(250);
        openGrid.setAlign(Alignment.CENTER);
        openGrid.setColSpan(2);

        form.setColWidths(150, "*");
        form.setWidth100();
        form.setPadding(10);
        form.setMargin(10);
        form.setItems(dimension1, dimension2, dimension3, fillRatio, openGrid);

        form.addSubmitValuesHandler(new SubmitValuesHandler() {
            @Override
            public void onSubmitValues(SubmitValuesEvent event) {
                cubeGridLayout.removeMember(form);
                setupDataLoading(
                        dimension1.getValueAsFloat(),
                        dimension2.getValueAsFloat(),
                        dimension3.getValueAsFloat(),
                        fillRatio.getValueAsFloat());
            }
        });

        ToolStrip toolStrip = new ToolStrip();
        btSetRandomValue = new ToolStripButton("Set Random Values");
        toolStrip.addButton(btSetRandomValue);

        btIncrement = new ToolStripButton("+1");
        toolStrip.addButton(btIncrement);

        btDelete = new ToolStripButton("Delete values");
        toolStrip.addButton(btDelete);

        toolStrip.addSpacer(10);

        btApply = new ToolStripButton("Apply values");
        toolStrip.addButton(btApply);

        toolStrip.addSpacer(10);

        boxChanges = new CheckboxItem("Has changes");
        toolStrip.addFormItem(boxChanges);

        cubeGridLayout.addMember(toolStrip);
        cubeGridLayout.addMember(form);
        return cubeGridLayout;
    }

    private void setupDataLoading(
            float dimensionItems1, float dimensionItems2,
            float dimensionItems3, float fillRatio) {
        gridCacheService.generateSampleData(
                connectionController.getConnectionId(),
                (int) dimensionItems1, (int) dimensionItems2,
                (int) dimensionItems3, (int) fillRatio,
                new DataCallback());
    }

    private class DataCallback implements AsyncCallback<SampleGridData> {

        @Override
        public void onFailure(Throwable caught) {
            throw new UnsupportedOperationException("Not supported yet.");
        }

        @Override
        public void onSuccess(SampleGridData result) {
            List<String> headers = new ArrayList<String>(result.getFacetValues().getFieldIds());
            headers.add("Value");

            HeliosGridDS gridDS = new HeliosGridDS(
                    connectionController.getConnectionId(),
                    result.getGridId(), headers);

            List<Facet> facets = new ArrayList<Facet>();

            for (String fieldId : result.getFacetValues().getFieldIds()) {
                Facet facet = new Facet(fieldId, fieldId);
                Set<FacetValue> values = new HashSet<FacetValue>();

                for (String value : result.getFacetValues().getDistinctValues(fieldId)) {
                    values.add(new FacetValue(value, value));
                }

                facet.setValues(values.toArray(new FacetValue[0]));
                facets.add(facet);
            }

            CubeGrid cubeGrid = new CubeGrid();
            cubeGrid.setWidth100();
            cubeGrid.setHeight100();
            cubeGrid.setCanEdit(true);

            cubeGrid.setFacets(facets.toArray(new Facet[0]));
            cubeGrid.setColumnFacets("Period");
            cubeGrid.setRowFacets("FirstName", "LastName");
            cubeGrid.setValueProperty("Value");

            cubeGrid.setDataSource(gridDS);
            cubeGrid.setAutoFetchData(true);

            cubeGrid.setAutoSaveEdits(false);

            btSetRandomValue.addClickHandler(new SetToRandomHandler(cubeGrid));
            btDelete.addClickHandler(new DeleteHandler(cubeGrid));
            btApply.addClickHandler(new ApplyHandler(cubeGrid));
            btIncrement.addClickHandler(new IncrementHandler(cubeGrid));
            Timer changesChecker = new ChangesChecker(cubeGrid);
            changesChecker.schedule(500);

            cubeGrid.addEditorExitHandler(new EditorExitHandler() {
                @Override
                public void onEditorExit(EditorExitEvent event) {
                    logger.fine("Event: " + event);
                    logger.fine("Edit completion event: " + event.getEditCompletionEvent());
                    logger.fine("New value: " + event.getNewValue());
                    logger.fine("Record: " + event.getRecord());
                    logger.fine("Changes: " + ((CubeGrid) event.getSource()).hasChanges());
                }
            });

            cubeGridLayout.addMember(cubeGrid);
        }
    }

    private class SetToRandomHandler implements ClickHandler {

        private Random random = new Random();
        private CubeGrid cubeGrid;

        public SetToRandomHandler(CubeGrid cubeGrid) {
            this.cubeGrid = cubeGrid;
        }

        @Override
        public void onClick(ClickEvent event) {
            logger.fine("Changes before updates: " + cubeGrid.hasChanges());

            for (int[] cellCoord : cubeGrid.getCellSelection().getSelectedCells()) {
                final int row = cellCoord[0];
                final int col = cellCoord[1];

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

            logger.fine("Changes after updates: " + cubeGrid.hasChanges());
        }
    }

    private class DeleteHandler implements ClickHandler {

        private CubeGrid cubeGrid;

        public DeleteHandler(CubeGrid cubeGrid) {
            this.cubeGrid = cubeGrid;
        }

        @Override
        public void onClick(ClickEvent event) {
            for (int[] cell : cubeGrid.getCellSelection().getSelectedCells()) {
                cubeGrid.setEditValue(cell[0], cell[1], "");
            }
        }
    }

    private class ApplyHandler implements ClickHandler {

        private CubeGrid cubeGrid;

        public ApplyHandler(CubeGrid cubeGrid) {
            this.cubeGrid = cubeGrid;
        }

        @Override
        public void onClick(ClickEvent event) {
            logger.fine("Changes before apply: " + cubeGrid.hasChanges());
            cubeGrid.saveAllEdits();
            logger.fine("Changes after apply: " + cubeGrid.hasChanges());
        }
    }

    private class IncrementHandler implements ClickHandler {

        private CubeGrid cubeGrid;

        public IncrementHandler(CubeGrid cubeGrid) {
            this.cubeGrid = cubeGrid;
        }

        @Override
        public void onClick(ClickEvent event) {
            logger.fine("Changes before increment: " + cubeGrid.hasChanges());
            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
                    }
                }
            }

            logger.fine("Changes after increment: " + cubeGrid.hasChanges());
        }
    }

    private class ChangesChecker extends Timer {

        private CubeGrid cubeGrid;

        public ChangesChecker(CubeGrid cubeGrid) {
            this.cubeGrid = cubeGrid;
        }

        @Override
        public void run() {
            boxChanges.setValue(cubeGrid.hasChanges());
            this.schedule(500);
        }
    }
}
