/*
 * JBoss, Home of Professional Open Source
 * Copyright 2011 Red Hat Inc. and/or its affiliates and other contributors
 * as indicated by the @author tags. All rights reserved.
 * See the copyright.txt in the distribution for a
 * full listing of individual contributors.
 *
 * This copyrighted material is made available to anyone wishing to use,
 * modify, copy, or redistribute it subject to the terms and conditions
 * of the GNU Lesser General Public License, v. 2.1.
 * This program is distributed in the hope that it will be useful, but WITHOUT A
 * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
 * PARTICULAR PURPOSE.  See the GNU Lesser General Public License for more details.
 * You should have received a copy of the GNU Lesser General Public License,
 * v.2.1 along with this distribution; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
 * MA  02110-1301, USA.
 */
package org.jboss.as.console.mbui.reification;

import com.google.gwt.core.client.Scheduler;
import com.google.gwt.dom.client.Style;
import com.google.gwt.event.logical.shared.AttachEvent;
import com.google.gwt.event.logical.shared.BeforeSelectionEvent;
import com.google.gwt.event.logical.shared.BeforeSelectionHandler;
import com.google.gwt.user.client.ui.Composite;
import com.google.gwt.user.client.ui.ScrollPanel;
import com.google.gwt.user.client.ui.TabPanel;
import com.google.gwt.user.client.ui.VerticalPanel;
import com.google.gwt.user.client.ui.Widget;
import com.google.web.bindery.event.shared.EventBus;
import org.jboss.as.console.client.widgets.pages.Pages;
import org.jboss.as.console.client.widgets.tabs.DefaultTabLayoutPanel;
import org.useware.kernel.gui.behaviour.NavigationEvent;
import org.useware.kernel.gui.behaviour.SystemEvent;
import org.useware.kernel.gui.behaviour.common.CommonQNames;
import org.useware.kernel.gui.reification.Context;
import org.useware.kernel.gui.reification.ContextKey;
import org.useware.kernel.gui.reification.strategy.ReificationStrategy;
import org.useware.kernel.gui.reification.strategy.ReificationWidget;
import org.useware.kernel.model.behaviour.Resource;
import org.useware.kernel.model.behaviour.ResourceType;
import org.useware.kernel.model.structure.Container;
import org.useware.kernel.model.structure.InteractionUnit;
import org.useware.kernel.model.structure.QName;
import org.jboss.as.console.mbui.model.StereoTypes;

import java.util.HashMap;
import java.util.Map;
import java.util.Set;

import static org.useware.kernel.model.structure.TemporalOperator.Choice;

/**
 * Strategy for a container with temporal operator == Choice.
 *
 * @author Harald Pehl
 * @author Heiko Braun
 * @date 11/01/2012
 */
public class ChoiceStrategy implements ReificationStrategy<ReificationWidget, StereoTypes>
{
    private EventBus eventBus;
    private static final Resource<ResourceType> NAVIGATION = new Resource<ResourceType>(CommonQNames.NAVIGATION_ID, ResourceType.Navigation);
    private static final Resource<ResourceType> ACTIVATION = new Resource<ResourceType>(CommonQNames.ACTIVATION_ID, ResourceType.System);

    @Override
    public boolean prepare(InteractionUnit<StereoTypes> interactionUnit, Context context) {
        eventBus = context.get(ContextKey.EVENTBUS);
        return eventBus !=null;
    }

    @Override
    public ReificationWidget reify(final InteractionUnit<StereoTypes> interactionUnit, final Context context)
    {
        return new TabPanelAdapter(interactionUnit);
    }

    @Override
    public boolean appliesTo(final InteractionUnit<StereoTypes> interactionUnit)
    {
        return (interactionUnit instanceof Container) && (((Container) interactionUnit)
                .getTemporalOperator() == Choice);
    }


    class TabPanelAdapter  implements ReificationWidget
    {
        private TabPanelContract delegate;
        private InteractionUnit interactionUnit;
        private Map<Integer, QName> childUnits = new HashMap<Integer, QName>();

        TabPanelAdapter(final InteractionUnit<StereoTypes> interactionUnit)
        {
            this.interactionUnit = interactionUnit;

            if(interactionUnit.getStereotype()!=null)
            {
                switch (interactionUnit.getStereotype())
                {
                    case EditorPanel:
                        this.delegate = createEditorPanel(interactionUnit, eventBus);
                        break;
                    case Pages:
                        this.delegate = createPages(interactionUnit, eventBus);
                        break;
                    default:
                        throw new RuntimeException("Unsupported stereotype "+interactionUnit.getStereotype());
                }
            }
            else
            {
                this.delegate = createDefaultTabPanel(interactionUnit, eventBus);
            }
        }

        private TabPanelContract createEditorPanel(final InteractionUnit interactionUnit, final EventBus eventBus) {
            final DefaultTabLayoutPanel tabLayoutpanel = new DefaultTabLayoutPanel(40, Style.Unit.PX);
            tabLayoutpanel.addStyleName("default-tabpanel");

            tabLayoutpanel.addBeforeSelectionHandler(new NavigationHandler(interactionUnit, childUnits, tabLayoutpanel));

           /* tabLayoutpanel.addAttachHandler(new AttachEvent.Handler() {
                @Override
                public void onAttachOrDetach(AttachEvent attachEvent) {

                    if(attachEvent.isAttached() && tabLayoutpanel.getWidgetCount()>0)
                        tabLayoutpanel.selectTab(0);
                }
            });*/

            TabPanelContract tabPanelContract = new TabPanelContract() {
                @Override
                public void add(InteractionUnit unit, Widget widget) {
                    final VerticalPanel vpanel = new VerticalPanel();
                    vpanel.setStyleName("rhs-content-panel");
                    vpanel.add(widget);

                    ScrollPanel scroll = new ScrollPanel(vpanel);
                    tabLayoutpanel.add(scroll, unit.getLabel());

                    // register tab2index mapping
                    childUnits.put(tabLayoutpanel.getWidgetCount() - 1, unit.getId());
                }

                @Override
                public Widget as() {
                    return tabLayoutpanel;
                }
            };


            // activation listener
            eventBus.addHandler(SystemEvent.TYPE,
                    new ChildActivationHandler<DefaultTabLayoutPanel>(tabLayoutpanel, childUnits, new ChildUnitCommand<DefaultTabLayoutPanel>() {
                        @Override
                        public void execute(DefaultTabLayoutPanel parent, int key) {
                            tabLayoutpanel.selectTab(key, false);
                        }
                    })
            );

            eventBus.addHandler(SystemEvent.TYPE,
                    new SystemEvent.Handler() {
                        @Override
                        public boolean accepts(SystemEvent event) {

                            return event.getId().equals(CommonQNames.ACTIVATION_ID)
                                    && childUnits.containsValue(event.getPayload()
                            );
                        }

                        @Override
                        public void onSystemEvent(SystemEvent event) {
                            QName id = (QName) event.getPayload();

                            Set<Integer> keys = childUnits.keySet();
                            for (Integer key : keys) {
                                if (childUnits.get(key).equals(id)) {
                                    tabLayoutpanel.selectTab(key, false);
                                    break;
                                }
                            }
                        }
                    }
            );


            // complement model

            getInteractionUnit().setOutputs(NAVIGATION);
            getInteractionUnit().setInputs(ACTIVATION);

            return tabPanelContract;
        }

        private TabPanelContract createPages(InteractionUnit<StereoTypes> interactionUnit, EventBus eventBus) {
            final Pages pagedView = new Pages();

            pagedView.addBeforeSelectionHandler(new NavigationHandler(interactionUnit, childUnits, pagedView));

            // activation listener

            eventBus.addHandler(SystemEvent.TYPE,
                    new ChildActivationHandler<Pages>(pagedView, childUnits, new ChildUnitCommand<Pages>() {
                        @Override
                        public void execute(Pages parent, int key) {
                            // TODO: this doesn't really work (rendering bugs) ...
                            parent.selectTab(key, false);
                            parent.getDeckPanel().showWidget(key); // workaround
                        }
                    })
            );

            TabPanelContract tabPanelContract = new TabPanelContract() {

                @Override
                public void add(InteractionUnit unit, Widget widget) {

                    //widget.addStyleName("rhs-content-panel");

                    pagedView.add(widget, unit.getLabel());

                    // register tab2index mapping
                    childUnits.put(pagedView.getWidgetCount() - 1, unit.getId());
                }

                @Override
                public Widget as() {
                    Widget widget = pagedView.asWidget();
                    widget.addStyleName("fill-layout");

                    widget.addAttachHandler(new AttachEvent.Handler() {
                        @Override
                        public void onAttachOrDetach(AttachEvent attachEvent) {
                            if (pagedView.getWidgetCount() > 0)
                                pagedView.selectTab(0, true);
                        }
                    });

                    //pagedView.showPage(0);
                    return pagedView;
                }
            };


            // complement model

            getInteractionUnit().setOutputs(NAVIGATION);
            getInteractionUnit().setInputs(ACTIVATION);

            return tabPanelContract;
        }

        private TabPanelContract createDefaultTabPanel(InteractionUnit interactionUnit, EventBus eventBus) {
            final TabPanel tabPanel = new TabPanel();
            tabPanel.setStyleName("default-tabpanel");

            tabPanel.addBeforeSelectionHandler(new NavigationHandler(interactionUnit, childUnits, tabPanel));

            /*tabPanel.addAttachHandler(new AttachEvent.Handler() {
                @Override
                public void onAttachOrDetach(AttachEvent attachEvent) {

                    if(attachEvent.isAttached() && tabPanel.getWidgetCount()>0)
                        tabPanel.selectTab(0);
                }
            });*/


            // activation listener
            eventBus.addHandler(SystemEvent.TYPE,
                    new ChildActivationHandler<TabPanel>(tabPanel, childUnits, new ChildUnitCommand<TabPanel>() {
                        @Override
                        public void execute(TabPanel parent, int key) {
                            // TODO: this doesn't really work (rendering bugs) ...
                            parent.selectTab(key, false);
                            parent.getDeckPanel().showWidget(key); // workaround
                        }
                    })
            );

            TabPanelContract tabPanelContract = new TabPanelContract() {
                @Override
                public void add(InteractionUnit unit, Widget widget) {
                    tabPanel.add(widget, unit.getLabel());

                    // register tab2index mapping
                    childUnits.put(tabPanel.getWidgetCount() - 1, unit.getId());
                }

                @Override
                public Widget as() {
                    return tabPanel;
                }
            };

            // complement model

            getInteractionUnit().setOutputs(NAVIGATION);
            getInteractionUnit().setInputs(ACTIVATION);


            return tabPanelContract;
        }

        @Override
        public InteractionUnit getInteractionUnit() {
            return interactionUnit;
        }

        @Override
        public void add(final ReificationWidget widget)
        {
            delegate.add(widget.getInteractionUnit(), widget.asWidget());
        }

        @Override
        public Widget asWidget()
        {
            return delegate.as();
        }
    }

    class NavigationHandler implements BeforeSelectionHandler<Integer>
    {
        private final Composite widget;
        private InteractionUnit interactionUnit;
        private Map<Integer, QName> childUnits = new HashMap<Integer, QName>();

        NavigationHandler(InteractionUnit interactionUnit, Map<Integer, QName> childUnits, Composite widget) {
            this.interactionUnit = interactionUnit;
            this.childUnits = childUnits;
            this.widget = widget;
        }

        @Override
        public void onBeforeSelection(BeforeSelectionEvent<Integer> event) {

            Object source = event.getSource();

            QName targetTab = childUnits.get(event.getItem());

            if (targetTab != null) {
                eventBus.fireEventFromSource(
                        new NavigationEvent(
                                CommonQNames.NAVIGATION_ID,
                                targetTab
                        ), interactionUnit.getId() //source
                );

            }
            event.cancel();
        }
    };

    class ChildActivationHandler<T> implements SystemEvent.Handler {

        private final T parent;
        private Map<Integer, QName> childUnits = new HashMap<Integer, QName>();
        private ChildUnitCommand<T> cmd;

        ChildActivationHandler(T parent, Map<Integer, QName> childUnits, ChildUnitCommand<T> cmd) {
            this.parent = parent;
            this.childUnits = childUnits;
            this.cmd = cmd;
        }

        @Override
        public boolean accepts(SystemEvent event) {

            return event.getId().equals(CommonQNames.ACTIVATION_ID)
                    && childUnits.containsValue(event.getPayload()
            );
        }

        @Override
        public void onSystemEvent(SystemEvent event) {
            QName id = (QName) event.getPayload();

            Set<Integer> keys = childUnits.keySet();
            for (final Integer key : keys) {
                if (childUnits.get(key).equals(id)) {

                    Scheduler.get().scheduleDeferred(
                            new Scheduler.ScheduledCommand() {
                                @Override
                                public void execute() {
                                    cmd.execute(parent, key);
                                }
                            }
                    );

                    break;
                }
            }
        }


    };

    interface ChildUnitCommand<T> {
        void execute(T parent, int key);
    }
}
