/*
 * 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.form;

import com.queplix.core.client.app.vo.BaseListFieldData;
import com.queplix.core.client.app.vo.ChartFieldMeta;
import com.queplix.core.client.app.vo.ContextMenuMeta;
import com.queplix.core.client.app.vo.FamgMeta;
import com.queplix.core.client.app.vo.FieldData;
import com.queplix.core.client.app.vo.FieldMeta;
import com.queplix.core.client.app.vo.FieldOnDemandData;
import com.queplix.core.client.app.vo.FormMeta;
import com.queplix.core.client.app.vo.TextboxFieldData;
import com.queplix.core.client.common.OrderedHashMap;

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

/**
 * QueWeb QForm model implementation
 *
 * @author Sergey Kozmin
 * @since 21.09.2006, 18:03:42
 */
class QFormModelImpl implements QFormModel {
    private FormMeta formMeta;
    private String formTitle;
    private int formState;
    /**
     * search state, see {@link QFormModel#SEARCH_SUCCESSFUL_SINGLE}
     */
    private int searchState;
    private int lockForEditState;
    private int dataUpdatedState;
    private int deleteState;
    private int newState;

    private OrderedHashMap elementsMeta = new OrderedHashMap();
    private OrderedHashMap elementsData = new OrderedHashMap();

    private ArrayList dataListeners = new ArrayList();
    private ArrayList stateListeners = new ArrayList();

    private ContextMenuMeta contextMenuMeta;

    private String pkeyID;
    private Long activeRowID;
    private FamgMeta.Index index;

    public QFormModelImpl(FormMeta formMeta) {
        this.formMeta = formMeta;
        this.formTitle = formMeta.getCaption();
        this.contextMenuMeta = formMeta.getContextMenu();
        this.pkeyID = formMeta.getPkeyID();
        this.index = formMeta.getIndex();

        FieldMeta[] fieldMetas = formMeta.getEntityMeta().getFields();
        for(int i = 0; i < fieldMetas.length; i++) {
            FieldMeta fieldMeta = fieldMetas[i];
            putFormElement(fieldMeta);
        }
        fireDataStructureChagendPerformed();
    }

    public String getRecordDescription() {
        for(int i = 0, n = elementsMeta.getKeys().size(); i < n; i++) {
            Object key = elementsMeta.getKeys().get(i);
            FieldMeta tmp = (FieldMeta) elementsMeta.get(key);
            if(tmp.isDescriptive() && tmp.getDataType() == FieldMeta.TEXTBOX) {
                TextboxFieldData result = (TextboxFieldData) getElementData(
                        tmp.getFieldID());
                return result != null ? result.getText():null;
            }
        }
        return null;
    }

    public String getPkeyID() {
        return pkeyID;
    }

    public FamgMeta.Index getIndex() {
        return this.index;
    }

    public FormMeta getFormMeta() {
        return formMeta;
    }

    public ContextMenuMeta getContextMenuMeta() {
        return contextMenuMeta;
    }

    public void setOnDemandDataForElement(FieldOnDemandData data) {
        fireOnDemandDataCome(data);
    }

    private FieldMeta putFormElement(FieldMeta element) {
        return (FieldMeta) elementsMeta.put(element.getFieldID(), element);
    }

    public FieldMeta getElementMeta(String elementID) {
        return (FieldMeta) elementsMeta.get(elementID);
    }

    public void clearElementData(String elementID) {
        FieldData fieldData = getElementData(elementID);
        FieldMeta elementMeta = getElementMeta(elementID);
        //check field is presented on the form and is not empty
        if(fieldData != null &&
                (needClear(elementMeta, fieldData))) {
            fieldData.clear();
            fireDataChagendPerformed(elementID);
        }
    }

    private boolean needClear(FieldMeta elementMeta, FieldData elementData) {
        boolean ret;
        switch (elementMeta.getDataType()) {
            case FieldMeta.MULTISELECT:
            case FieldMeta.LISTBOX:
                ret = ((BaseListFieldData) elementData).getItemsSelected()
                        .getSelectedIDs().length > 0;
                break;
            case FieldMeta.CHART:
                ret = !((ChartFieldMeta) elementMeta).isForDashboard()
                        && !elementData.isEmpty();
                break;
            default:
                ret = !elementData.isEmpty();
                break;
        }
        return ret;
    }

    public ArrayList elementsKeys() {
        return elementsMeta.getKeys();
    }

    /**
     * Set data for element. Generate update event and control notication
     * if data object is changed.
     *
     * @param data data to set
     */
    public void setDataForElement(FieldData data) {
        if(data != null) {
            String fieldId = data.getFieldID();
            if(elementsMeta.containsKey(fieldId)) {
                FieldData currentFieldData = (FieldData) elementsData.get(
                        fieldId);
                if(!data.dataEqualsTo(currentFieldData)) {
                    elementsData.put(fieldId, data);
                    fireDataChagendPerformed(fieldId);
                }
            }
        }
    }

    public void setDataForElements(FieldData[] data,
                                   boolean clearRestOfElements) {
        for(int i = 0; i < data.length; i++) {
            setDataForElement(data[i]);
        }

        if(clearRestOfElements) {
            List restElements = new ArrayList(elementsKeys());

            for(int i = 0; i < data.length; i++) {
                restElements.remove(data[i].getFieldID());
            }

            for(int i = 0; i < restElements.size(); i++) {
                String element = (String) restElements.get(i);
                clearElementData(element);
            }
        }
    }

    /**
     * Returns data element. Can be null if nothing was loaded for the field
     *
     * @param elementID element id in form
     * @return data element. Can be null if nothing was loaded for the field
     */
    public FieldData getElementData(String elementID) {
        FieldData fieldData = null;
        if(elementsMeta.containsKey(elementID)) {//check if field exists in form meta
            fieldData = (FieldData) elementsData.get(elementID);
            if(fieldData == null) {//the field could be a null, because it was never used before 
                fireNeedDataInModel(elementID);//upload data from control to model
                fieldData = (FieldData) elementsData.get(elementID);//get it
            }
        }
        return fieldData;
    }

    public Long getActiveRowID() {
        return activeRowID;
    }

    public void setActiveRowID(Long activeRowID) {
        if(activeRowID == null) {
            throw new IllegalArgumentException(
                    "Parameter 'activeRowID' could not be a null");
        }

        this.activeRowID = activeRowID;
    }

    public void clearActiveRowID() {
        activeRowID = null;
    }

    public Collection elementsValues() {
        return new ArrayList(elementsData.values());
    }

    public List getNonEmptyFilters() {
        fireNeedDataInModel();
        ArrayList keys = elementsKeys();
        ArrayList list = new ArrayList();
        for(int i = 0, n = keys.size(); i < n; i++) {
            FieldData element = getElementData((String) keys.get(i));
            if(null != element) {
                if(!element.isEmpty()) {
                    list.add(element);
                }
            }
        }
        return list;
    }

    public String getFormTitle() {
        return formTitle;
    }

    public void setFormTitle(String formTitle) {
        this.formTitle = formTitle;
    }

    public int getFormState() {
        return formState;
    }

    public void setFormState(int formState) {
        if(this.formState != formState) {
            this.formState = formState;
            fireFormStateChanged();
        }
    }

    public void setSearchState(int searchState) {
        if(this.searchState != searchState) {
            this.searchState = searchState;
            fireSearchStateChanged(searchState);
            this.searchState = SEARCH_NEUTRAL;
        }
    }

    public void setLockForEditState(int lockForEditState) {
        if(this.lockForEditState != lockForEditState) {
            this.lockForEditState = lockForEditState;
            fireFormLockForEditStateChanged(lockForEditState);
            this.lockForEditState = LOCK_FOR_EDIT_NEUTRAL;
        }
    }

    public void setCreateState(int newState) {
        if(this.newState != newState) {
            this.newState = newState;
            fireNewStateChanged(newState);
            this.newState = NEW_NEUTRAL;
        }
    }

    public void setDataUpdatedState(int dataUpdatedState) {
        if(this.dataUpdatedState != dataUpdatedState) {
            this.dataUpdatedState = dataUpdatedState;
            fireFormDataUpdateStateChanged(dataUpdatedState);
            this.dataUpdatedState = UPDATE_NEUTRAL;
        }
    }

    public void setDeleteState(int deleteState) {
        if(this.deleteState != deleteState) {
            this.deleteState = deleteState;
            fireDeleteStateChanged(deleteState);
            this.deleteState = DELETE_NEUTRAL;
        }
    }

    public void addDataListener(QFormDataListener listener) {
        if(listener != null) {
            dataListeners.add(listener);
        }
    }

    public void removeDataListener(QModelStateListener listener) {
        if(listener != null) {
            stateListeners.remove(listener);
        }
    }

    public void addStateListener(QModelStateListener listener) {
        if(listener != null) {
            stateListeners.add(listener);
        }
    }

    public void removeStateListener(QFormDataListener listener) {
        if(listener != null) {
            dataListeners.remove(listener);
        }
    }

    protected void fireDataStructureChagendPerformed() {
        for(int i = 0; i < dataListeners.size(); i++) {
            QFormDataListener listener = (QFormDataListener) dataListeners.get(
                    i);
            listener.dataStructureChanged();
        }
    }

    public void fireDataChagendPerformed(String controlId) {
        for(int i = 0; i < dataListeners.size(); i++) {
            QFormDataListener listener = (QFormDataListener) dataListeners.get(
                    i);
            listener.dataChanged(controlId);
        }
    }

    public void fireOnDemandDataCome(FieldOnDemandData data) {
        for(int i = 0; i < dataListeners.size(); i++) {
            QFormDataListener listener = (QFormDataListener) dataListeners.get(
                    i);
            listener.dataOnDemandCome(data);
        }
    }

    private void fireFormStateChanged() {
        for(int i = 0; i < stateListeners.size(); i++) {
            QModelStateListener listener = (QModelStateListener) stateListeners
                    .get(i);
            listener.formStateChanged();
        }
    }

    private void fireSearchStateChanged(int searchState) {
        for(int i = 0; i < stateListeners.size(); i++) {
            QModelStateListener listener = (QModelStateListener) stateListeners
                    .get(i);
            listener.formSearchStateChanged(searchState);
        }
    }

    private void fireFormLockForEditStateChanged(int lockForEditState) {
        for(int i = 0; i < stateListeners.size(); i++) {
            QModelStateListener listener = (QModelStateListener) stateListeners
                    .get(i);
            listener.formLockForEditStateChanged(lockForEditState);
        }
    }

    private void fireFormDataUpdateStateChanged(int dataUpdatedState) {
        for(int i = 0; i < stateListeners.size(); i++) {
            QModelStateListener listener = (QModelStateListener) stateListeners
                    .get(i);
            listener.formDataUpdateStateChanged(dataUpdatedState);
        }
    }

    private void fireDeleteStateChanged(int deleteState) {
        for(int i = 0; i < stateListeners.size(); i++) {
            QModelStateListener listener = (QModelStateListener) stateListeners
                    .get(i);
            listener.formDeleteStateChanged(deleteState);
        }
    }

    private void fireNewStateChanged(int newState) {
        for(int i = 0; i < stateListeners.size(); i++) {
            QModelStateListener listener = (QModelStateListener) stateListeners
                    .get(i);
            listener.formNewStateChanged(newState);
        }
    }

    private void fireNeedDataInModel() {
        for(int i = 0; i < stateListeners.size(); i++) {
            QModelStateListener listener = (QModelStateListener) stateListeners
                    .get(i);
            listener.uploadDataToModel();
        }
    }

    private void fireNeedDataInModel(String fieldId) {
        for(int i = 0; i < stateListeners.size(); i++) {
            QModelStateListener listener = (QModelStateListener) stateListeners
                    .get(i);
            listener.uploadDataToModel(fieldId);
        }
    }

    public String getEntityID() {
        return formMeta.getEntityMeta().getEntityID();
    }
}
