/*
 * Copyright 2006-2007 Queplix Corp.
 *
 * Licensed under the Queplix Public License, Version 1.1.1 (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.queplix.com/solutions/commercial-open-source/queplix-public-license/
 *
 * 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 com.queplix.core.client.controls.informgrid;

import com.queplix.core.client.app.vo.FieldDataRequest;
import com.queplix.core.client.app.vo.GetControlDataInformRequest;
import com.queplix.core.client.app.vo.GetRecordsDataInformRequest;
import com.queplix.core.client.app.vo.GridData;
import com.queplix.core.client.app.vo.GridSearchProperties;
import com.queplix.core.client.app.vo.InFormDataRequest;
import com.queplix.core.client.app.vo.InFormGridFieldMeta;
import com.queplix.core.client.app.vo.RecordFilter;
import com.queplix.core.client.app.vo.RowData;
import com.queplix.core.client.app.vo.SearchInformRequest;
import com.queplix.core.client.app.vo.GridMeta;
import com.queplix.core.client.app.vo.SortFieldImpl;
import com.queplix.core.client.app.vo.GetSortedDataInformRequest;
import com.queplix.core.client.app.vo.SortField;
import com.queplix.core.client.common.ui.DialogHelper;
import com.queplix.core.client.common.ui.grid.GridSortListener;
import com.queplix.core.client.common.ui.grid.SourcesGridSortEvents;
import com.queplix.core.client.common.ui.grid.SortColumn;
import com.queplix.core.client.controls.QFormElementControllerImpl;
import com.queplix.core.client.controls.QFormElementModel;
import com.queplix.core.client.controls.QFormElementView;

import java.util.Collection;
import java.util.Iterator;
import java.util.List;
import java.util.ArrayList;

class QInformGridControllerImpl extends QFormElementControllerImpl
        implements QInformGridController,
        ViewActionsListener, GridSortListener {
    private static final String INCORRECT_STATE_CHANGE
            = "Internal error. Unit wants to change to unknown QForm state.";

    private QInformGridModelImpl model;
    private QInformGridViewImpl view;

    private QInformGridState searchState;
    private QInformGridState selectedState;
    private QInformGridState editState;

    private QInformGridState currentState;

    private GetControlDataInformRequest getControlDataReq;
    private GetRecordsDataInformRequest getRecordsDataReq;
    private GetSortedDataInformRequest getSortedDataReq;
    private SearchInformRequest searchReq;

    public QInformGridControllerImpl(QInformGridModelImpl model,
                                     QInformGridViewImpl view) {
        this.model = model;
        this.view = view;

        InformGridStateContext context = new InformGridStateContext();
        searchState = new SearchGridState(context);
        editState = new EditGridState(context);
        selectedState = new SelectedGridState(context);

        currentState = searchState;
        currentState.enterToState();
        view.addActionListener(this);
        view.addSortListener(this);
    }

    protected void turnToState(QInformGridState newState) {
        if(!currentState.equals(newState)) {
            currentState.exitFromState();
            currentState = newState;
            currentState.enterToState();
        }
    }

    protected void turnToState(int newQInformGridState)
            throws IncorrectGridActionPerformed {
        QInformGridState state;
        switch(newQInformGridState) {
            case QInformGridState.EDIT_STATE:
                state = editState;
                break;
            case QInformGridState.SEARCH_STATE:
                state = searchState;
                break;
            case QInformGridState.SELECTED_STATE:
                state = selectedState;
                break;
            default:
                throw new IncorrectGridActionPerformed(INCORRECT_STATE_CHANGE);
        }
        turnToState(state);
        view.formStateChanged(state);
    }

    public void onAction(int actionType) {
        try {
            currentState.actionPerformed(actionType);
        } catch (IncorrectGridActionPerformed incorrectGridActionPerformed) {
            DialogHelper.showModalErrorDialog(incorrectGridActionPerformed);
        }
    }

    public void onSearchRequest(RecordFilter filter,
                                GridSearchProperties gridProperties) {
        sendRequest(getSearchRequest(filter, gridProperties));
    }

    public void onMoreDataRequest(FieldDataRequest dataRequest) {
        sendRequest(getControlDataRequest(dataRequest));
    }

    public void addRecordsToModel(List rows, Collection filters) {
        for(int i = 0; i < rows.size(); i++) {//todo implement insertRows(List)
            RowData rowData = (RowData) rows.get(i);
            model.getData().getGridData().insertRow(rowData);
            model.getData().setFilters(filters);
        }
        model.fireDataChanged();
    }

    public void setRecordsInModel(List rows){
        GridData gridData = model.getData().getGridData();
        gridData.clearRecords();
        Iterator it = rows.iterator();
        while(it.hasNext()){
            RowData rowData = (RowData)it.next();
            gridData.insertRow(rowData);
        }
        model.fireDataChanged();
    }

    private InFormDataRequest getSearchRequest(RecordFilter filter,
                                               GridSearchProperties gridProperties) {
        if(searchReq == null) {
            InFormGridFieldMeta fieldMeta = model.getMeta();
            String entityId = fieldMeta.getFilteringFormMeta().getEntityMeta()
                    .getEntityID();
            searchReq = new SearchInformRequest(fieldMeta.getFieldID(),
                    filter.getFiltersList(), entityId, gridProperties);
        } else {
            searchReq.setFieldsFilters(filter.getFiltersList());
            searchReq.setGridProperties(gridProperties);
        }
        return searchReq;
    }

    private InFormDataRequest getControlDataRequest(
            FieldDataRequest dataRequest) {
        if(getControlDataReq == null) {
            InFormGridFieldMeta fieldMeta = model.getMeta();
            String entityId = fieldMeta.getPresentationEntityName();
            getControlDataReq = new GetControlDataInformRequest(
                    fieldMeta.getFieldID(),
                    dataRequest, entityId);
        } else {
            getControlDataReq.setRequest(dataRequest);
        }
        return getControlDataReq;
    }

    private InFormDataRequest getSortedRecordsRequest(SortField sortField){
        InFormGridFieldMeta fieldMeta = model.getMeta();
        String entityID = fieldMeta.getPresentationEntityName();
        RowData[] rowData = model.getData().getGridData().getRows();
        if(getSortedDataReq == null){
            getSortedDataReq = new GetSortedDataInformRequest(
                fieldMeta.getFieldID(), sortField, entityID, rowData);
        } else {
            getSortedDataReq.setRowData(rowData);
        }
        return getSortedDataReq;
    }

    private InFormDataRequest getRecordsDataRequest(
            RecordFilter filter) {
        if(getRecordsDataReq == null) {
            InFormGridFieldMeta fieldMeta = model.getMeta();
            String entityId = fieldMeta.getPresentationEntityName();
            getRecordsDataReq = new GetRecordsDataInformRequest(
                    fieldMeta.getFieldID(),
                    filter, entityId);
        } else {
            getRecordsDataReq.setFilter(filter);
        }
        return getRecordsDataReq;
    }

    private void sendRequest(InFormDataRequest request) {
        controlEventsListener.needMoreData(request);
    }

    public QFormElementModel getModel() {
        return model;
    }

    public QFormElementView getView() {
        return view;
    }

    public void onSort(SourcesGridSortEvents sender, SortColumn sortColumn) {
        GridMeta meta = model.getMeta().getPresentationGridMeta();
        int columnIndex = (int) meta.getColumns().getSelectedIDs()[sortColumn
                    .getColumnIndex()];
        String columnID = meta.getFields()[columnIndex].getFieldID();
        SortFieldImpl sortField = model.getSortFieldImpl();
        sortField.setFieldID(columnID);
        sortField.setSortOrder(sortColumn.getAscending());
        sendRequest(getSortedRecordsRequest(sortField));
    }

    private class InformGridStateContext implements StateContext {
        public void changeState(int informGridState)
                throws IncorrectGridActionPerformed {
            turnToState(informGridState);
        }

        public void enableLinkButtons() {
            view.setButtonsEnabled(QInformGrid.LINK_BUTTON_INDEX, true);
            view.setButtonsEnabled(QInformGrid.UNLINK_BUTTON_INDEX, true);
        }

        public void disableLinkButtons() {
            view.setButtonsEnabled(QInformGrid.LINK_BUTTON_INDEX, false);
            view.setButtonsEnabled(QInformGrid.UNLINK_BUTTON_INDEX, false);
        }

        public void setEnabledUnLinkButton(boolean isEnabled) {
            view.setButtonsEnabled(QInformGrid.UNLINK_BUTTON_INDEX, isEnabled);
        }

        public void showLinkButtons() {
            view.setButtonVisible(QInformGrid.LINK_BUTTON_INDEX, true);
            view.setButtonVisible(QInformGrid.UNLINK_BUTTON_INDEX, true);
        }

        public void showEnabledFilterButton() {
            view.setButtonVisible(QInformGrid.FILTERS_BUTTON_INDEX, true);
            view.setButtonsEnabled(QInformGrid.FILTERS_BUTTON_INDEX, true);
        }

        public void hideFilterButton() {
            view.setButtonVisible(QInformGrid.FILTERS_BUTTON_INDEX, false);
        }

        public void hideLinkButtons() {
            view.setButtonVisible(QInformGrid.LINK_BUTTON_INDEX, false);
            view.setButtonVisible(QInformGrid.UNLINK_BUTTON_INDEX, false);
        }

        public int getControlState() {
            return view.getViewMode();
        }

        public void showFilteringDialog() {
            view.showFilteringDialog();
        }

        public RecordFilter getDialogFilter() {
            return view.getDialogFilter();
        }

        public void setQFormElementFilter(RecordFilter filter) {
            model.getData().clear();
            sendRequest(getRecordsDataRequest(filter));
        }

        public void addQFormElementFilter(RecordFilter filter) {
            if(!filter.isEmpty()) {
                if(!filter.isRecordsFilter()) {
                    showMessage("Couldn't add records by the filters.");
                } else {
                    if(processRecordsFilters(filter)) {
                        sendRequest(getRecordsDataRequest(filter));
                    }
                }
            }
        }

        /**
         * Check, if any records exists in records collection that is not
         * presented yet in grid.
         *
         * @param filter filter object
         * @return true, if any unique records presents in records list, false
         *         otherwise.
         */
        private boolean processRecordsFilters(RecordFilter filter) {
            GridData existingRecords = model.getData().getGridData();
            Collection ids = filter.getRecordIds();
            for(Iterator iterator = ids.iterator(); iterator.hasNext();) {
                Long selectedRecordId = (Long) iterator.next();
                int index = existingRecords.getRowIndexByID(selectedRecordId);
                if(index >= 0) {
                    iterator.remove();
                }
            }
            return ids.size() > 0;
        }

        public void showMessage(String msg) {
            DialogHelper.showMessageDialog(msg);
        }

        /**
         * @return List<Integer> of selected indexes
         */
        public List getSelectedRecords() {
            return view.getSelectedRecords();
        }

        public void removeRecords(List records) {
            if(model.getData().getGridData().removeRows(records)) {
                model.fireDataChanged();
            }
        }
    }
}
