/*
 * 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.frames.mainframe.impl;

import com.queplix.core.client.app.vo.EntityData;
import com.queplix.core.client.app.vo.EntityLinkFieldData;
import com.queplix.core.client.app.vo.EntityLinkFieldMeta;
import com.queplix.core.client.app.vo.EntityLinkOnDemandData;
import com.queplix.core.client.app.vo.EntityMeta;
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.GridData;
import com.queplix.core.client.app.vo.RowData;
import com.queplix.core.client.app.vo.TabMeta;
import com.queplix.core.client.common.ui.DialogHelper;
import com.queplix.core.client.controls.form.QForm;
import com.queplix.core.client.controls.form.QFormController;
import com.queplix.core.client.controls.grid.QGrid;
import com.queplix.core.client.frames.mainframe.FormState;

import java.util.Collection;
import java.util.Iterator;

/**
 * Class listen forms activation and inject appropriate data into those.
 * To determine what data is appropriate it also listen the request responses and user actions.
 *
 * @author Sergey Kozmin
 * @since 24.11.2006, 16:51:59
 */
class FormsDataInjector {
    private FormsDataRegistry dataRegister;
    private MainFrameFA mainFrameFA;
    private MainFrameGA mainFrameGA;

    private TabMeta lastViewedTab;

    public FormsDataInjector() {
        dataRegister = new FormsDataRegistry();
    }

    public void initialize(MainFrameFA formAreaMainFrame,
                           MainFrameGA mainFrameGA) {
        this.mainFrameGA = mainFrameGA;
        this.mainFrameFA = formAreaMainFrame;
    }

    public void onClearAction(FamgMeta meta) {
        String entityID = meta.getForm().getEntityMeta().getEntityID();
        dataRegister.addFormDataConsumption(entityID,
                meta.getIndex().serialize());
        //clear grid selection
        QGrid grid = mainFrameGA.getGrid(meta.getIndex());
        if(grid != null) {
            grid.getController().clearSelectedRecord();
        }

        //clear form
        QForm form = mainFrameFA.getForm(meta.getIndex());
        if(form != null) {
            QFormController qfc = form.getFormController();
            qfc.resetAndSetFormState(FormState.SEARCH_STATE);
            qfc.clearForm();
        }

        //clear linked controls states for the cleared form and all forms, that linked to it.
        EntityData data = dataRegister.getData(
                meta.getForm().getEntityMeta().getEntityID());
        fillUpDistributedControls(meta.getForm().getEntityMeta(), data,
                mainFrameFA, lastViewedTab.getFamgs());

        //clear register
        dataRegister.clearData(meta.getEntityName());
    }

    /**
     * @param formStateAfter see {@link com.queplix.core.client.frames.mainframe.FormState#SEARCH_STATE}
     */
    public void onClearAllAction(int formStateAfter) {
        dataRegister.clearAllData();
        dataRegister.clearDataConsumptions();
        mainFrameGA.clearAllGrids();

        Iterator it = mainFrameFA.existingFormsIterator();
        while(it.hasNext()) {
            FamgMeta.Index index = (FamgMeta.Index) it.next();
            QForm form = mainFrameFA.getForm(index);
            QFormController qfc = form.getFormController();
            qfc.resetAndSetFormState(formStateAfter);
            qfc.clearForm();
        }
    }

    public void onFormsDataUpdated(EntityData[] entityData,
                                   EntityData[] fieldsData,
                                   Collection gridData) {
        dataRegister.mergeData(entityData, fieldsData, gridData);
        injectData(lastViewedTab);
    }

    public void onVisualAreaChanged(TabMeta changedTo) {
        lastViewedTab = changedTo;
        injectData(changedTo);
    }

    private void injectData(TabMeta changedTo) {
        if(mainFrameFA != null) {
            FamgMeta[] metas = changedTo.getFamgs();
            EntityData entityData;
            for(int i = 0; i < metas.length; i++) {
                entityData = dataRegister.getData(
                        metas[i].getForm().getEntityMeta().getEntityID());
                if(entityData != null) {//same condition for grid data.
                    String entityName = entityData.getEntityID();
                    FamgMeta formMeta = metas[i];
                    fillFormWithData(entityName, formMeta, entityData, metas);
                }
            }
        }
    }

    private void fillFormWithData(String entityName, FamgMeta formMeta,
                                  EntityData entityData, FamgMeta[] metas) {
        FamgMeta.Index index = formMeta.getIndex();
        //fill the grid before form filling in order
        //to avoid impact of form state changing
        if(!dataRegister.wasDataConsumedByGrid(entityName, index.serialize())) {
            dataRegister.addGridDataConsumption(entityName, index.serialize());
            QForm form = mainFrameFA.getForm(index);
            int formState = form.getModel().getFormState();
            //fill the grid only if it is not in selected state.
            if(formState == FormState.SEARCH_STATE
                    || formState == FormState.NEW_STATE
                    || formState == FormState
                    .EDIT_STATE) {
                fillGrid(entityName, index);
            }
        }
        if(!dataRegister.wasDataConsumedByForm(entityName, index.serialize())) {
            dataRegister.addFormDataConsumption(entityName, index.serialize());

            QForm form = mainFrameFA.getForm(index);
            int formState = form.getModel().getFormState();
            if(dataRegister.isEntityDataInExternalFields(
                    entityName)) {//check should we move form to selected state. We should if we should set data for all form.
                if(formState == FormState.SEARCH_STATE ||
                        formState == FormState.REPORT_DESIGN_STATE ||
                        formState == FormState.NEW_STATE ||
                        formState == FormState.EDIT_STATE) {
                    mainFrameFA.performCommand(new SetDataForFormCommand(
                            entityData.getFields(), null, false), index);
                }
            } else {
                boolean perform;
                if(formState == FormState.NEW_STATE || formState == FormState
                        .EDIT_STATE) {//todo we don't know actually what form requests this update, there is dedicated form, that should be filled out even in this states. need to fix
                    String message = form.getFormController()
                            .getRecordEditingMessage();
                    perform = (DialogHelper.showModalQuestionDialog(message)
                            == DialogHelper.YES);
                } else {
                    perform = true;//todo init with it
                }
                if(perform) {
                    if(mainFrameFA.performCommand(new TurnFormToStateCommand(
                            FormState.SELECTED_STATE), index)) {
                        //fill up form
                        mainFrameFA.performCommand(new SetDataForFormCommand(
                                entityData.getFields(), entityData.getRowID(),
                                true),
                                index);
                        //fill up distributed controls
                        fillUpDistributedControls(
                                formMeta.getForm().getEntityMeta(), entityData,
                                mainFrameFA, metas);
                    }
                }
            }
        }
    }

    /**
     * Fill up grid.
     *
     * @param entityName grid entity
     * @param index      grid index
     */
    private void fillGrid(String entityName, FamgMeta.Index index) {
        GridData gridData = dataRegister.getGridData(entityName);
        if(gridData
                != null) {//if we want to fill it with empty data create empty grid object then.
            mainFrameGA.performCommand(new SetDataForGridCommand(gridData, 1,
                    0), index);//todo set correct totalrecords and curpage
            RowData[] rows = gridData.getRows();
            if(rows.length > 0) {
                mainFrameGA.getGrid(index).getModel().setSelectedRecordId(
                        rows[0].getId());//select first
            }
        }
    }

    /**
     * Fill up distributed form controls. Search such controls in the given entityMeta and set appropriate data to
     * availableForms
     *
     * @param entityMeta     meta to search controls
     * @param entityData     data to perform operation
     * @param mainFrameFA    main frame focus area object
     * @param availableForms avalable forms, where data can be set.
     */
    private void fillUpDistributedControls(EntityMeta entityMeta,
                                           EntityData entityData,
                                           MainFrameFA mainFrameFA,
                                           FamgMeta[] availableForms) {
        FieldMeta[] meta = entityMeta.getFields();
        for(int i = 0; i < meta.length; i++) {
            FieldMeta fieldMeta = meta[i];
            switch(fieldMeta.getDataType()) {
                case FieldMeta.ENTITYLINK: {
                    FieldData data = (entityData == null) ? null
                            :entityData.getFieldById(fieldMeta.getFieldID());
                    EntityLinkFieldMeta elfm = (EntityLinkFieldMeta) fieldMeta;
                    for(int j = 0; j < availableForms.length; j++) {
                        FamgMeta proposedForm = availableForms[j];
                        if(mainFrameFA.getForm(proposedForm.getIndex())
                                != null) {
                            if(proposedForm.getEntityName().equalsIgnoreCase(
                                    elfm.getLinkedEntity())) {
                                EntityLinkOnDemandData onDemandData;
                                if(data != null) {
                                    EntityLinkFieldData elfd
                                            = (EntityLinkFieldData) data;
                                    onDemandData = new EntityLinkOnDemandData(
                                            elfm.getLinkedFieldName(),
                                            elfd.getCurrentEntityID());
                                } else {
                                    onDemandData = new EntityLinkOnDemandData(
                                            elfm.getLinkedFieldName(),
                                            EntityLinkFieldData.EMPTY_VALUE);
                                }
                                SetOnDemandDataForControlCommand forControlCommand
                                        = new SetOnDemandDataForControlCommand(
                                        onDemandData);

                                mainFrameFA.performCommand(forControlCommand,
                                        proposedForm.getIndex());
                            }
                        }
                    }
                    break;
                }
            }
        }
    }

    /**
     * Returns entity data that stored in dataRegister without taking into consideration its consumption.
     *
     * @param entityId unique entity name
     * @return entity data. can be null
     */
    public EntityData getEntityData(String entityId) {
        return dataRegister.getData(entityId);
    }

    /**
     * Is this entity filled out as external field entity.
     *
     * @param entityId entity id
     * @return false if entity filled out as whole entity or there is no such
     *         entity in data at all, true if entity was filled out by external fields.
     */
    public boolean isExternalFieldEntity(String entityId) {
        return dataRegister.isEntityDataInExternalFields(entityId);
    }
}
