package com.nagarro.client;

import com.google.gwt.core.client.EntryPoint;
import com.smartgwt.client.core.KeyIdentifier;
import com.smartgwt.client.data.Criteria;
import com.smartgwt.client.data.DSRequest;
import com.smartgwt.client.data.DSResponse;
import com.smartgwt.client.data.DataSource;
import com.smartgwt.client.data.Record;
import com.smartgwt.client.data.RecordList;
import com.smartgwt.client.data.SortSpecifier;
import com.smartgwt.client.types.DSOperationType;
import com.smartgwt.client.types.SortDirection;
import com.smartgwt.client.util.EventHandler;
import com.smartgwt.client.util.Page;
import com.smartgwt.client.util.PageKeyHandler;
import com.smartgwt.client.util.SC;
import com.smartgwt.client.widgets.IButton;
import com.smartgwt.client.widgets.Label;
import com.smartgwt.client.widgets.form.DynamicForm;
import com.smartgwt.client.widgets.grid.ListGridRecord;
import com.smartgwt.client.widgets.layout.HLayout;
import com.smartgwt.client.widgets.layout.VStack;
import com.smartgwt.client.widgets.viewer.DetailViewer;

public class CountryDataDemo implements EntryPoint {

    private AAListGrid   boundList;
    private DynamicForm  boundForm;
    private IButton      saveBtn;
    private DetailViewer boundViewer;
    private IButton      newBtn;
    private RecordList   clipBoardData;

    /**
     * This is the entry point method.
     */
    @Override
    public void onModuleLoad() {
        final KeyIdentifier debugKey = new KeyIdentifier();
        debugKey.setCtrlKey(true);
        debugKey.setKeyName("D");

        Page.registerKey(debugKey, new PageKeyHandler() {

            @Override
            public void execute(final String keyName) {
                SC.showConsole();
            }
        });

        final VStack vStack = new VStack();
        vStack.setLeft(175);
        vStack.setTop(75);
        vStack.setWidth("70%");
        vStack.setMembersMargin(20);

        final Label label = new Label();
        label.setContents("<ul>" + "<li>select a datasource from the list at left to bind to these components</li>"
                + "<li>click a record in the grid to view and edit that record in the form</li>"
                + "<li>click <b>New</b> to start editing a new record in the form</li>"
                + "<li>click <b>Save</b> to save changes to a new or edited record in the form</li>"
                + "<li>click <b>Clear</b> to clear all fields in the form</li>"
                + "<li>click <b>Filter</b> to filter (substring match) the grid based on form values</li>"
                + "<li>click <b>Fetch</b> to fetch records (exact match) for the grid based on form values</li>"
                + "<li>double-click a record in the grid to edit inline (press Return, or arrow/tab to another record, to save)</li>" + "</ul>");
        vStack.addMember(label);

        boundList = new AAListGrid(DataSource.get("animals"));
        boundList.setEnableUndoAction(1, "auditAdd", "auditUpdate", "auditRemove");
        boundList.setHeight(200);
        boundList.setCanEdit(true);
        boundList.setAutoSaveEdits(true);

        boundList.addRecordClickHandler(event -> {
            final Record record = event.getRecord();
            boundForm.editRecord(record);
            saveBtn.enable();
            boundViewer.viewSelectedData(boundList);
        });
        boundList.addKeyPressHandler(keyPressEvent -> {
            if (keyPressEvent.isCtrlKeyDown() && "Z".equals(EventHandler.getKey())) {
                performUndo();
            }

            if (keyPressEvent.isCtrlKeyDown() && "C".equals(EventHandler.getKey())) {
                performCopy();
            }
            if (keyPressEvent.isCtrlKeyDown() && "V".equals(EventHandler.getKey()) && (clipBoardData != null)) {
                performPaste();
            }
        });

        vStack.addMember(boundList);

        boundForm = new DynamicForm();
        boundForm.setDataSource(DataSource.get("animals"));
        boundForm.setNumCols(6);
        boundForm.setAutoFocus(false);
        vStack.addMember(boundForm);

        final HLayout hLayout = new HLayout(10);
        hLayout.setMembersMargin(10);
        hLayout.setHeight(22);

        saveBtn = new IButton("Save");
        saveBtn.addClickHandler(event -> boundForm.saveData((dsResponse, data, dsRequest) -> {
            if (dsResponse.getStatus() == DSResponse.STATUS_SUCCESS) {
                // if the save succeeded, clear the UI
                boundForm.clearValues();
                saveBtn.disable();
            }
        }));
        hLayout.addMember(saveBtn);

        newBtn = new IButton("New");
        newBtn.addClickHandler(event -> {
            boundForm.editNewRecord();
            saveBtn.enable();
        });
        hLayout.addMember(newBtn);

        final IButton clearBtn = new IButton("Clear");
        clearBtn.addClickHandler(event -> {
            boundForm.clearValues();
            saveBtn.disable();
        });
        hLayout.addMember(clearBtn);

        final IButton filterBtn = new IButton("Filter");
        filterBtn.addClickHandler(event -> {
            boundList.filterData(boundForm.getValuesAsCriteria());
            saveBtn.disable();
        });
        hLayout.addMember(filterBtn);

        final IButton fetchBtn = new IButton("Fetch");
        fetchBtn.addClickHandler(event -> {
            boundList.fetchData(boundForm.getValuesAsCriteria());
            saveBtn.disable();
        });
        hLayout.addMember(fetchBtn);

        vStack.addMember(hLayout);

        boundViewer = new DetailViewer();
        boundViewer.setDataSource(DataSource.get("animals"));
        vStack.addMember(boundViewer);
        boundList.fetchData();
        vStack.draw();

    }

    private void bindComponents(final String dsName) {
        final DataSource ds = DataSource.get(dsName);
        boundList.setDataSource(ds);
        boundViewer.setDataSource(ds);
        boundForm.setDataSource(ds);
        boundList.fetchData();
        newBtn.enable();
        saveBtn.disable();
    }

    private void performUndo() {

        final DataSource gridDataSource = boundList.getDataSource();
        final String auditDataSourceId = "audit_" + gridDataSource.getID();

        final String primaryKeyName = gridDataSource.getPrimaryKeyFieldName();
        final Criteria contactCriteria = new Criteria();
        // TODO this has to be committed contactCriteria.addCriteria("audit_modifier", auditModifier);
        final DSRequest properties = new DSRequest();
        final SortSpecifier sortByversion = new SortSpecifier("audit_revision", SortDirection.DESCENDING);
        final SortSpecifier[] sorting = new SortSpecifier[0];
        sorting[0] = sortByversion;
        properties.setSortBy(sorting);
        // This is to fetch the top most row with highest audit_revision number, which means the latest change done by user
        properties.setStartRow(0);
        properties.setEndRow(2);
        final DataSource auditDS = DataSource.get(auditDataSourceId);
        if (null != auditDS) {
            auditDS.fetchData(contactCriteria, (final com.smartgwt.client.data.DSResponse dsResponse, final Object data, final DSRequest dsRequest) -> {
                if ((dsResponse.getData() != null) && (dsResponse.getData().length > 0)) {
                    final Record currentAuditRecord = dsResponse.getData()[0];
                    final String primaryKeyValInAudit = currentAuditRecord.getAttribute(primaryKeyName);
                    final String operationType = currentAuditRecord.getAttributeAsString("audit_operationType");
                    final DSRequest auditActionProperties = new DSRequest();

                    if (DSOperationType.UPDATE.getValue().equals(operationType) && (dsResponse.getData().length > 1)) {
                        final Record lastAuditRecord = dsResponse.getData()[1];
                        final Record changedRecord = boundList.getResultSet().find(primaryKeyName, primaryKeyValInAudit);
                        boundList.getRecordIndex(changedRecord);
                        if (lastAuditRecord.getAttribute(primaryKeyName).equals(primaryKeyValInAudit)) {
                            final Object[] fieldsChangedObj = (Object[]) currentAuditRecord.getAttributeAsObject("audit_changedFields");
                            for (final Object fieldChangedObj : fieldsChangedObj) {
                                changedRecord.setAttribute(fieldChangedObj.toString(), lastAuditRecord.getAttribute(fieldChangedObj.toString()));
                            }
                            auditActionProperties.setOperationId("auditUpdate");
                            gridDataSource.updateData(changedRecord, (response, obj, request) -> boundList.refreshRow(boundList.getRecordIndex(changedRecord)),
                                    auditActionProperties);
                        }

                    } else if (DSOperationType.ADD.getValue().equals(operationType)) {
                        final Record changedRecord = boundList.getResultSet().find(primaryKeyName, primaryKeyValInAudit);
                        final int rowNum = boundList.getRowNum(new ListGridRecord(changedRecord));
                        auditActionProperties.setOperationId("auditRemove");
                        gridDataSource.removeData(boundList.getRecord(rowNum),
                                (response, obj, request) -> boundList.refreshRow(boundList.getRecordIndex(changedRecord)), auditActionProperties);

                    } else if (DSOperationType.REMOVE.getValue().equals(operationType)) {
                        currentAuditRecord.setAttribute(primaryKeyName, (String) null);
                        auditActionProperties.setOperationId("auditAdd");
                        gridDataSource.addData(currentAuditRecord, (response, obj, request) -> boundList.refreshData(), auditActionProperties);
                    }
                    // This remove is for the actual Audit entry which was created by User's action. This remove can be considered similar to a Stack Pop
                    // action.
                    auditDS.removeData(currentAuditRecord);

                } else {
                    SC.say("can not perform undo");
                }

            }, properties);
        } else {
            SC.say("undo");
        }

    }

    private void performCopy() {
        clipBoardData = boundList.getSelectedCellData();

    }

    private void performPaste() {
        if (clipBoardData != null) {
            boundList.applyCellData(clipBoardData);
            clipBoardData = null;
        }

    }
}
