/*
 * 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.WindowResizeListener;
import com.google.gwt.user.client.ui.AbsolutePanel;
import com.google.gwt.user.client.ui.HasHorizontalAlignment;
import com.google.gwt.user.client.ui.HorizontalPanel;
import com.google.gwt.user.client.ui.Label;
import com.google.gwt.user.client.ui.SourcesTabEvents;
import com.google.gwt.user.client.ui.TabListener;
import com.google.gwt.user.client.ui.Widget;
import com.google.gwt.user.client.Timer;
import com.queplix.core.client.app.rpc.RPC;
import com.queplix.core.client.app.rpc.RPC.QAsyncCallback;
import com.queplix.core.client.app.vo.UnreadMessagesResponse;
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.FocusMeta;
import com.queplix.core.client.app.vo.MetaData;
import com.queplix.core.client.app.vo.TabMeta;
import com.queplix.core.client.common.event.Event;
import com.queplix.core.client.common.event.EventListener;
import com.queplix.core.client.common.event.EventSource;
import com.queplix.core.client.common.ui.ButtonData;
import com.queplix.core.client.common.ui.DesignHelper;
import com.queplix.core.client.common.ui.DialogHelper;
import com.queplix.core.client.common.ui.QTabBar;
import com.queplix.core.client.common.ui.SourcesTabMouseEvents;
import com.queplix.core.client.common.ui.SplittedPanel;
import com.queplix.core.client.common.ui.TabMouseListener;
import com.queplix.core.client.common.ui.WindowHelper;
import com.queplix.core.client.common.ui.resizable.Resizable;
import com.queplix.core.client.common.ui.resizable.ResizableScrollPanel;
import com.queplix.core.client.common.ui.resizable.ResizableStackPanel;
import com.queplix.core.client.frames.mainframe.MainToolbarBuilderFactory;
import com.queplix.core.client.i18n.I18N;

/**
 * MainFrameSA - Main Frame Static Area
 * This class contains all static elements of the main frame.
 *
 * @author Aliaksandr Melnik
 * @since 20 October 2006
 */
class MainFrameSA extends AbsolutePanel
        implements Resizable, WindowResizeListener, EventListener {
    // -------------------- public events ------------------------
    public static interface Events extends MainTree.Events,
            com.queplix.core.client.frames.mainframe.MainFrameLinkEvents,
            DefaultMainToolbarBuilder.Events,
            LoadSearchBox.Events,
            SaveSearchBox.Events {
        Event/*]<String>[*/ SHORTCUT_MENU_ITEM = new Event/*]<String>[*/();
        Event/*]<GridHistoryRecord>[*/ HISTORY_MENU_ITEM
                = new Event/*]<GridHistoryRecord>[*/();
    }

    private EventSource eventSource = new EventSource(this);

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

    public static interface Indicator extends MainToolBar.Indicator {
    }

    private MetaData appMetaData;

    private MainFrameLinks frameLinks;
    private FocusMeta[] focuses;
    private QTabBar tabBar;
    private MainToolBar mainToolBar;
    private StatusBar statusBar;
    private ShortCuts shortCuts;
    private LastViewed lastViewed;
    private MainTree mainTree;
    private SplittedPanel gridAndForms;
    private SplittedPanel navAndForms;
    private int splitterPosition = -1;
    private Timer mailTimer;
    private Label welcomeLabel;

    private static SavedSearch savedSearch;
    private static SaveSearchBox saveSearchBox;
    private static LoadSearchBox loadSearchBox;

    private SubFocusesPanel subFocuses;

//    private boolean ignoreEvents; // used to disable firing events when programmatic actions occur

//    private ResizableScrollPanel mainFrameFARSP;

    private ResizableStackPanel treeStack;

    private boolean ignoreTreeSelection;
    
    public MainFrameSA(MetaData appMetaData, MainFrameFA mainFrameFA,
                       MainFrameGA mainFrameGA) {
        this.appMetaData = appMetaData;
        frameLinks = new MainFrameLinks(appMetaData);
        frameLinks.getEventSource().addEventListener(
                eventSource); // retranslate events

        initTabBar(appMetaData);
        initSavedSearch(appMetaData);
        initSaveSearchBox();
        initLoadSearchBox(appMetaData);
        initMainToolbarBuilders();

        subFocuses = new SubFocusesPanel(appMetaData);
        subFocuses.getEventSource().addEventListener(eventSource);
        subFocuses.getEventSource().addEventListener(this);

        mainToolBar = new MainToolBar(appMetaData.getPortalConfigMeta().
                                      getToolbar());
        mainToolBar.getEventSource().addEventListener(this);
        mainToolBar.getEventSource().addEventListener(
                eventSource); // retranslate events

        shortCuts = new ShortCuts();
        shortCuts.getEventSource().addEventListener(this);

        lastViewed = new LastViewed();
        lastViewed.getEventSource().addEventListener(this);

        statusBar = new StatusBar(appMetaData);

        mainTree = new MainTree(appMetaData);
        mainTree.getEventSource().addEventListener(
                eventSource); // retranslate events

        ResizableScrollPanel treeScroll = new ResizableScrollPanel(
                mainTree.getView());

        treeStack = new ResizableStackPanel();
        WindowHelper.disableSelection(treeStack.getElement());
        treeStack.add(treeScroll, DesignHelper.stackPanelHeader(
                "Navigation Tree", "tree"), true);
        treeStack.add(new ResizableScrollPanel(shortCuts),
                DesignHelper.stackPanelHeader("Shortcuts", "shortcuts"), true);
        treeStack.add(new ResizableScrollPanel(lastViewed),
                DesignHelper.stackPanelHeader("Last Viewed", "lastviewed"),
                true);

        treeStack.addStyleName("treeStack");

        //mainFrameFARSP = new ResizableScrollPanel(mainFrameFA);
        mainFrameFA.setStyleName("tab_deckpanel");

        navAndForms = new SplittedPanel(
                treeStack,
                //mainFrameFARSP,
                mainFrameFA,
                20,
                SplittedPanel.HORIZONTAL);

        gridAndForms = new SplittedPanel(
                //new ResizableScrollPanel(mainFrameGA),
                mainFrameGA,
                navAndForms,
                30,
                SplittedPanel.VERTICAL);

        subFocuses.addStyleName("subFocus_panel");
        subFocuses.setWidth("100%");

        HorizontalPanel subFocusArea = new HorizontalPanel();
        welcomeLabel = new Label(
                I18N.getMessages().welcomeUserMessage() + appMetaData
                        .getUserProfile().getFullName(), false);
        welcomeLabel.setStyleName("welcomeUserMessage");

        subFocusArea.add(subFocuses);
        subFocusArea.add(welcomeLabel);
        subFocusArea.setStyleName("subFocus_panel");
        subFocusArea.setWidth("100%");
        subFocusArea.setCellHorizontalAlignment(welcomeLabel,
                HasHorizontalAlignment.ALIGN_RIGHT);

        mainToolBar.setWidth("100%");

        addUnselectable(frameLinks);
        if (appMetaData.getPortalConfigMeta().isShowFocusMenu()) {
            addUnselectable(tabBar);
        }
        if (appMetaData.getPortalConfigMeta().isShowSubFocusMenu()) {
            addUnselectable(subFocusArea);
        }
        addUnselectable(mainToolBar);
        add(gridAndForms);
        addUnselectable(statusBar);

        addStyleName("common_bg");

        Window.addWindowResizeListener(this);
    }

    private void initSavedSearch(final MetaData appMetaData) {
        savedSearch = new SavedSearch();
        savedSearch.addEventListener(new EventListener() {
            public void onEvent(Event event, Widget sender) {
                if(SavedSearch.Events.LOADED.equals(event)) {
                    loadSearchBox.onRefresh();
                    if(!loadSearchBox.isDialogShown()) {
                        displayLoadSearchBox();
                    }
                } else if(SavedSearch.Events.DONE == event) {
                    DialogHelper.showModalMessageDialog(
                            I18N.getMessages().searchSaved());
                } else if(SavedSearch.Events.ERROR == event) {
                    DialogHelper.showModalMessageDialog(
                            I18N.getMessages().searchError());
                } else if(SavedSearch.Events.RENAME == event) {
                    displaySaveSearchBox(SaveSearchBox.RENAME);
                }
            }
        });
    }

    private void initSaveSearchBox() {
        saveSearchBox = new SaveSearchBox(I18N.getMessages().saveSearch());
        saveSearchBox.getEventSource().addEventListener(eventSource);
    }

    private void initLoadSearchBox(MetaData appMetaData) {
        loadSearchBox = new LoadSearchBox(I18N.getMessages().loadSavedSearch(),
                savedSearch, appMetaData);
        loadSearchBox.getEventSource().addEventListener(eventSource);
    }

    private void displaySaveSearchBox(int state) {
        int left = (Window.getClientWidth() - saveSearchBox.getOffsetWidth())
                / 2;
        int top = (Window.getClientHeight() - saveSearchBox.getOffsetHeight())
                / 2;
        saveSearchBox.show(left, top, state);
    }

    private void displayLoadSearchBox() {
        int left = (Window.getClientWidth() - loadSearchBox.getOffsetWidth())
                / 2;
        int top = (Window.getClientHeight() - loadSearchBox.getOffsetHeight())
                / 2;
        loadSearchBox.show(left, top);
    }

    private static void loadSavedSearch() {
        savedSearch.load();
    }

    public static void saveSearch(String name, String formID,
                                  FieldData[] elementsValues) {
        savedSearch.save(name, formID, elementsValues);
    }

    public static void onSearchDeleted() {
        loadSavedSearch();
    }

    private void addUnselectable(Widget widget) {
        WindowHelper.disableSelection(widget.getElement());
        add(widget);
    }

    public void onEvent(Event event, Widget sender) {//catch and dispatch events
        if(Events.LOAD_SAVED_SEARCH.equals(event)) {
            loadSavedSearch();
        }
        if(Events.SAVE_CURRENT_SEARCH.equals(event)) {
            displaySaveSearchBox(SaveSearchBox.NEW);
        } else if(shortCuts.equals(sender)) {
            Events.SHORTCUT_MENU_ITEM.setData(event.getData());
            eventSource.fireEvent(Events.SHORTCUT_MENU_ITEM);
        } else if(lastViewed.equals(sender)) {
            Events.HISTORY_MENU_ITEM.setData(event.getData());
            eventSource.fireEvent(Events.HISTORY_MENU_ITEM);
        } else if(subFocuses.equals(sender)) {
            if(event == SubFocusesPanel.Events.MOUSE_LEFT) {
                tabBar.removeHighlight();
            }
        }
    }

    private void initTabBar(MetaData appMetaData) {
        tabBar = new QTabBar();

        focuses = appMetaData.getFocuses();

        for(int i = 0; i < focuses.length; i++) {
            tabBar.addTab(focuses[i].getCaption());
        }

        if(focuses.length > 0) {
            tabBar.selectTab(0);
        }

        tabBar.addTabListener(new TabListener() {
            public void onTabSelected(SourcesTabEvents sender, int index) {
                Events.FOCUS_SELECTED.setData(focuses[index].getIndex());
                eventSource.fireEventGeneratedByUser(Events.FOCUS_SELECTED);
            }

            public boolean onBeforeTabSelected(SourcesTabEvents sender,
                                               int tabIndex) {
                return true;
            }
        });
        tabBar.addTabMouseListener(new TabMouseListener() {
            public void onTabMouseOver(SourcesTabMouseEvents sender,
                                       int index) {
                subFocuses.showFocusSubFocuses(focuses[index].getIndex());
            }

            public void onTabMouseOut(SourcesTabMouseEvents sender, int index) {
            }

        });
    }

    public void setOffsetHeight(int height) {
        height -= tabBar.getOffsetHeight() +
                mainToolBar.getOffsetHeight() +
                statusBar.getOffsetHeight() +
                subFocuses.getOffsetHeight();
        gridAndForms.setOffsetHeight(height);
    }

    public void setOffsetWidth(int width) {
        gridAndForms.setOffsetWidth(width);
        setWidgetPosition(frameLinks, width - frameLinks.getOffsetWidth(), 0);
//        mainToolBar.setOffsetWidth(width);
    }

    public void onWindowResized(int width, int height) {
        setOffsetHeight(height);
        setOffsetWidth(width);
        this.setPixelSize(width, height);
    }

    protected void onAttach() {
        super.onAttach();
        onWindowResized(Window.getClientWidth(), Window.getClientHeight());

        subFocuses.adjustItemsPositions(tabBar);
        
        if (appMetaData.getCheckMailTimeout() > 0) {
            checkMailStatus();
            mailTimer = new Timer(){
                public void run() {
                    checkMailStatus();
                }
            };
            mailTimer.scheduleRepeating(appMetaData.getCheckMailTimeout());
        }
    }

    /**
     * @param ignoreTreeSelection - do not allow to fire tree selection event.
     */
    public void selectTreeItem(FocusMeta.Index index,
                               boolean ignoreTreeSelection) {
        if(ignoreTreeSelection) {
            ignoreTreeSelection = true;
        }
        if(treeStack.getSelectedIndex() == 0) {
            mainTree.getController().selectItem(index);
        }
    }

    public void activateFocus(FamgMeta.Index index) {
        tabBar.selectTab(index.focus);
        //if tree is visible, if selectItem in invisible tree throws exception
        selectTreeItem(index, true);
        subFocuses.activateFocus(index);
        ignoreTreeSelection = false;
        positionSplitter(index);
        generateShortCuts(index);
    }

    public void activateSubFocus(FamgMeta.Index index) {
        tabBar.selectTab(index.focus);
        selectTreeItem(index, true);
        subFocuses.activateSubFocus(index);
        ignoreTreeSelection = false;
        positionSplitter(index);
        generateShortCuts(index);
    }

    public void activateTab(FamgMeta.Index index) {
        activateFocus(index);
    }

    private void generateShortCuts(FamgMeta.Index index) {
        ContextMenuMeta menuMeta = appMetaData.getFormMeta(index)
                .getContextMenu();
        shortCuts.reGenerateShortCuts(menuMeta);
    }

    public void addHistoryItem(GridHistoryRecord record) {
        lastViewed.addHistoryItem(record);
    }

    void setIndicatorState(ButtonData state) {
        if (mainToolBar != null) {
            mainToolBar.setIndicatorState(state);
        }
    }

    public boolean isIgnoreTreeSelection() {
        return ignoreTreeSelection;
    }

    public void setIgnoreTreeSelection(boolean ignoreTreeSelection) {
        this.ignoreTreeSelection = ignoreTreeSelection;
    }

    private void positionSplitter(TabMeta.Index index) {
        boolean canHaveGrid = appMetaData.getTabMeta(index).canHaveGrid();
        if(!canHaveGrid) {
            gridAndForms.setSplitterCollapsed(true);
        } else {
            if(gridAndForms.isSplitterCollapsed()) {
                gridAndForms.setSplitterCollapsed(false);
            }
            ;
        }
    }
    
    private void initMainToolbarBuilders() {
        MainToolbarBuilderFactory.getInstance().
            addBuilder(new DefaultMainToolbarBuilder());
    }
    
    public void checkMailStatus() {
        RPC.getRPC().hasUnreadMessages(new QAsyncCallback() {
            public void onRequestEnded(boolean success, Object result) {
                setMailStatus(success && 
                        ((UnreadMessagesResponse)result).isHasUnreadMessages());
            }
            protected void handleError(Throwable error) {
                System.out.println("CheckMail error: ");
                error.printStackTrace();
            }
        });
    }

    protected void setMailStatus(boolean hasUnreadMessages) {
        frameLinks.setMailStatus(hasUnreadMessages);
    }
}
