/*
 * 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.google.gwt.user.client.Window;
import com.google.gwt.user.client.WindowCloseListener;
import com.queplix.core.client.app.Application;
import com.queplix.core.client.app.vo.FamgMeta;
import com.queplix.core.client.app.vo.FocusMeta;
import com.queplix.core.client.app.vo.MetaData;
import com.queplix.core.client.app.vo.SubFocusMeta;
import com.queplix.core.client.app.vo.TabMeta;
import com.queplix.core.client.common.event.Event;
import com.queplix.core.client.common.event.EventSource;
import com.queplix.core.client.common.ui.resizable.ResizableLazyDeckPanel;
import com.queplix.core.client.controls.form.QForm;
import com.queplix.core.client.i18n.I18N;
import com.queplix.core.client.frames.mainframe.ViewPropertiesProvider;

import java.util.Iterator;
import java.util.NoSuchElementException;

/**
 * Acronym FA - Form Area.
 * On focus change you should select corresponding FocusFA in this class to
 * show correct contents.
 * This class contains array of FocusFA.
 * Class FocusFA contains array of SubFocusFA.
 * Class SubFocusFA contains array of TabFA.
 * Class TabFA contains array of QForm.
 *
 * @author Aliaksandr Melnik
 * @since 19 Oct 2006
 */
class MainFrameFA extends ResizableLazyDeckPanel
        implements WindowCloseListener {
    private ViewPropertiesProvider viewPropertiesProvider;

    // -------------------- public events ------------------------
    public static interface Events extends FocusFA.Events {
        Event FOCUS_SELECTED_EVENT = new Event();
    }

    private EventSource eventSource = new EventSource(this);

    public EventSource getEventSource() {
        return eventSource;
    }
    // ----------------- end of public events --------------------

    private FocusFA[] focusesFA;
    private FocusMeta[] focusesMeta;

    private int activeFocus = -1;

    public MainFrameFA(MetaData appMetaData,
                       ViewPropertiesProvider viewPropertiesProvider) {
        super(appMetaData.getFocuses().length);
        this.viewPropertiesProvider = viewPropertiesProvider;
        focusesMeta = appMetaData.getFocuses();
        focusesFA = new FocusFA[focusesMeta.length];

        if(focusesMeta.length > 0) {
            activateFocus(0);
        }
        Window.addWindowCloseListener(this);
    }

    public void setFormsState(int formState) {
        for(int i = 0; i < focusesFA.length; i++) {
            if(focusesFA[i] != null) {
                focusesFA[i].setFormsState(formState);
            }
        }
    }

    protected void createCard(int index) {
        Application.setStatus(
                "Building forms area for focus: " + focusesMeta[index]
                        .getCaption());
        FocusFA focus = new FocusFA(focusesMeta[index], viewPropertiesProvider,
                false, indexToActivate);
        focus.getEventSource().addEventListener(
                eventSource); // retranslate events
        remove(index);
        insert(focus, index);
        focusesFA[index] = focus;
        focus.initialized();
    }

    public QForm getForm(FamgMeta.Index index) {
        QForm form = null;
        FocusFA focusFA = focusesFA[index.focus];
        if(focusFA != null) {
            form = focusFA.getForm(index);
        }
        return form;
    }

    public TabFA getTab(TabMeta.Index index) {
        TabFA ret = null;
        FocusFA focusFA = focusesFA[index.focus];
        if(focusFA != null) {
            ret = focusFA.getTab(index);
        }
        return ret;
    }

    public SubFocusFA getSubFocus(SubFocusMeta.Index index) {
        SubFocusFA ret = null;
        FocusFA focusFA = focusesFA[index.focus];
        if(focusFA != null) {
            ret = focusFA.getSubFocus(index);
        }
        return ret;
    }

    public QForm getActiveForm() {
        return focusesFA[activeFocus].getActiveForm();
    }

    public FamgMeta.Index getActiveFormIndex() {
        QForm activeForm = getActiveForm();
        return activeForm.getModel().getIndex();//todo check it on null object. 
    }

    public void activateForm(FamgMeta.Index index) {
        activateFocus(index);
        focusesFA[activeFocus].activateForm(index);
    }

    public void activateTab(TabMeta.Index index) {
        activateFocus(index);
        focusesFA[activeFocus].activateTab(index);
    }

    public void activateSubFocus(SubFocusMeta.Index index) {
        activateFocus(index);
        focusesFA[activeFocus].activateSubFocus(index);
    }

    public void activateFocus(FocusMeta.Index index) {
        switch(index.getType()) {
            case FocusMeta.Index.SUBFOCUS:
                setIndexToActivate((SubFocusMeta.Index) index);
                break;
            case FocusMeta.Index.TAB:
                setIndexToActivate((TabMeta.Index) index);
                break;
            case FocusMeta.Index.FAMG:
                setIndexToActivate((FamgMeta.Index) index);
                break;
            default:
                setIndexToActivate(index);
                break;
        }

        if(index.focus != activeFocus) {
            activateFocus(index.focus);
        } else {
            focusesFA[index.focus].setIndexToActivate(indexToActivate);
        }
    }

    private void activateFocus(int index) {
        activeFocus = index;
        showWidget(index);
        eventSource.fireEvent(Events.FOCUS_SELECTED_EVENT);
    }

    public boolean performCommand(FormCommand command,
                                  FamgMeta.Index formIndex) {
        return focusesFA[formIndex.focus].performCommand(command, formIndex);
    }

    public void ensureVisibility(FamgMeta.Index index) {
        getSubFocus(index).ensureVisibility(index);
    }

    public String onWindowClosing() {
        return isInEditMode() ? I18N.getMessages()
                .applicationCloseConfirmationShort():null;
    }

    public void onWindowClosed() {
    }

    public boolean isInEditMode() {
        for(int i = 0; i < focusesFA.length; i++) {
            if(focusesFA[i] != null) {
                if(focusesFA[i].isInEditMode()) {
                    return true;
                }
            }
        }
        return false;
    }

    public void collectUISettings() {
        for(int i = 0; i < focusesFA.length; i++) {
            if(focusesFA[i] != null) {
                focusesFA[i].collectUISettings();
            }
        }
    }

    /**
     * @return next existing index
     */
    public FamgMeta.Index getNextExistingFormIndex(int focus, int subFocus,
                                                   int tab, int famg) {
        FamgMeta.Index ret = null;
        for(int i = focus; i < focusesFA.length; i++) {
            if(focusesFA[i] != null) {
                FamgMeta.Index nextIndex = focusesFA[i]
                        .getNextExistingFormIndex(subFocus, tab, famg);
                if(nextIndex != null) {
                    ret = nextIndex;
                    break;
                }
            }
            subFocus = 0;//reset to first
            tab = 0;
            famg = -1;
        }
        return ret;
    }

    public Iterator existingFormsIterator() {
        return new ExistingFormsIterator();
    }

    class ExistingFormsIterator implements Iterator {
        private boolean callPerformed = false;
        private FamgMeta.Index curIndex;

        public ExistingFormsIterator() {
            curIndex = new FamgMeta.Index();
            curIndex.focus = 0;
            curIndex.subFocus = 0;
            curIndex.tab = 0;
            curIndex.famg = -1;
        }

        public boolean hasNext() {
            performNextIteration();
            return curIndex != null;
        }

        private void performNextIteration() {
            if(!callPerformed) {
                curIndex = getNextExistingFormIndex(curIndex.focus,
                        curIndex.subFocus, curIndex.tab, curIndex.famg);
                callPerformed = true;
            }
        }

        /**
         * Can't return null if #hasNext() returns true.
         *
         * @return next element. FamgMeta.Index
         */
        public Object next() {
            if(curIndex == null) {
                throw new NoSuchElementException("Ask new iterator. ");
            }
            performNextIteration();
            callPerformed = false;
            return curIndex;
        }

        public void remove() {
            throw new IllegalStateException(
                    "Can't remove form from metadata. ");
        }
    }
}
