/**
 * Copyright (C) 2009 eXo Platform SAS.
 *
 * This is free software; you can redistribute it and/or modify it
 * under the terms of the GNU Lesser General Public License as
 * published by the Free Software Foundation; either version 2.1 of
 * the License, or (at your option) any later version.
 *
 * This software is distributed in the hope that it will be useful,
 * but WITHOUT ANY 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 along with this software; if not, write to the Free
 * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
 * 02110-1301 USA, or see the FSF site: http://www.fsf.org.
 */

package org.exoplatform.navigation.webui.component;

import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.Comparator;
import java.util.Iterator;
import java.util.List;
import java.util.UUID;

import javax.servlet.http.HttpServletRequest;

import org.exoplatform.commons.utils.LazyPageList;
import org.exoplatform.commons.utils.ListAccess;
import org.exoplatform.navigation.webui.TreeNode;
import org.exoplatform.portal.application.PortalRequestContext;
import org.exoplatform.portal.config.DataStorage;
import org.exoplatform.portal.config.Query;
import org.exoplatform.portal.config.UserACL;
import org.exoplatform.portal.config.UserPortalConfig;
import org.exoplatform.portal.config.UserPortalConfigService;
import org.exoplatform.portal.config.model.PortalConfig;
import org.exoplatform.portal.mop.SiteKey;
import org.exoplatform.portal.mop.navigation.Scope;
import org.exoplatform.portal.mop.user.UserNavigation;
import org.exoplatform.portal.mop.user.UserPortal;
import org.exoplatform.portal.webui.page.UISiteBody;
import org.exoplatform.portal.webui.portal.UIPortal;
import org.exoplatform.portal.webui.portal.UIPortalComposer;
import org.exoplatform.portal.webui.util.PortalDataMapper;
import org.exoplatform.portal.webui.util.Util;
import org.exoplatform.portal.webui.workspace.UIEditInlineWorkspace;
import org.exoplatform.portal.webui.workspace.UIPortalApplication;
import org.exoplatform.portal.webui.workspace.UIWorkingWorkspace;
import org.exoplatform.util.ReflectionUtil;
import org.exoplatform.web.application.ApplicationMessage;
import org.exoplatform.web.login.LogoutControl;
import org.exoplatform.webui.application.WebuiRequestContext;
import org.exoplatform.webui.config.annotation.ComponentConfig;
import org.exoplatform.webui.config.annotation.ComponentConfigs;
import org.exoplatform.webui.config.annotation.EventConfig;
import org.exoplatform.webui.core.UIApplication;
import org.exoplatform.webui.core.UIContainer;
import org.exoplatform.webui.core.UIPopupWindow;
import org.exoplatform.webui.core.lifecycle.UIFormLifecycle;
import org.exoplatform.webui.event.Event;
import org.exoplatform.webui.event.Event.Phase;
import org.exoplatform.webui.event.EventListener;

@ComponentConfigs({
        @ComponentConfig(template = "app:/groovy/navigation/webui/component/UISiteManagement.gtmpl", events = {
                @EventConfig(name = "EditPortalLayout", listeners = UISiteManagement.EditPortalLayoutActionListener.class),
                @EventConfig(listeners = UISiteManagement.EditNavigationActionListener.class),
                @EventConfig(listeners = UISiteManagement.DeletePortalActionListener.class, confirm = "UIPortalBrowser.deletePortal") }),
        @ComponentConfig(type = UIPageNodeForm.class, lifecycle = UIFormLifecycle.class, template = "system:/groovy/webui/form/UIFormTabPane.gtmpl", events = {
                @EventConfig(listeners = UIPageNodeForm.SaveActionListener.class),
                @EventConfig(listeners = UIPageNodeForm.ChangeLanguageActionListener.class, phase = Phase.DECODE),
                @EventConfig(listeners = UISiteManagement.BackActionListener.class, phase = Phase.DECODE),
                @EventConfig(listeners = UIPageNodeForm.SwitchLabelModeActionListener.class, phase = Phase.DECODE),
                @EventConfig(listeners = UIPageNodeForm.SwitchPublicationDateActionListener.class, phase = Phase.DECODE),
                @EventConfig(listeners = UIPageNodeForm.SwitchVisibleActionListener.class, phase = Phase.DECODE),
                @EventConfig(listeners = UIPageNodeForm.ClearPageActionListener.class, phase = Phase.DECODE),
                @EventConfig(listeners = UIPageNodeForm.CreatePageActionListener.class, phase = Phase.DECODE),
                @EventConfig(listeners = UIPageNodeForm.SelectTabActionListener.class, phase = Phase.DECODE) }),
        @ComponentConfig(type = UIPopupWindow.class, template = "system:/groovy/webui/core/UIPopupWindow.gtmpl", events = @EventConfig(listeners = UISiteManagement.CloseActionListener.class, name = "ClosePopup")) })
public class UISiteManagement extends UIContainer {

    public static String[] ACTIONS = { "EditNavigation", "DeletePortal", "EditPortalLayout" };

    private LazyPageList<PortalConfig> pageList;

    private UINavigationManagement naviManager;

    // set navigationScope to GrandChildren for default value
    private Scope navigationScope = Scope.GRANDCHILDREN;

    public UISiteManagement() throws Exception {
        UIPopupWindow editNavigation = addChild(UIPopupWindow.class, null, null);
        editNavigation.setWindowSize(400, 400);
        editNavigation.setId(editNavigation.getId() + "-" + UUID.randomUUID().toString().replaceAll("-", ""));
    }

    public List<PortalConfig> getPortalConfigs() throws Exception {
        return pageList.getAll();
    }

    public String[] getActions() {
        return ACTIONS;
    }

    public Object getFieldValue(Object bean, String field) throws Exception {
        Method method = ReflectionUtil.getGetBindingMethod(bean, field);
        return method.invoke(bean, ReflectionUtil.EMPTY_ARGS);
    }

    @SuppressWarnings("unchecked")
    public void loadPortalConfigs() throws Exception {
        DataStorage service = getApplicationComponent(DataStorage.class);

        Query<PortalConfig> query = new Query<PortalConfig>(null, null, null, null, PortalConfig.class);
        LazyPageList<PortalConfig> temp = service.find(query, new Comparator<PortalConfig>() {
            public int compare(PortalConfig pconfig1, PortalConfig pconfig2) {
                return pconfig1.getName().toLowerCase().compareTo(pconfig2.getName().toLowerCase());
            }
        });

        final ArrayList<PortalConfig> tempArrayList = new ArrayList<PortalConfig>();
        if (temp != null) {
            tempArrayList.addAll(temp.getAll());
        }

        // Get portals without edit permission
        UserACL userACL = getApplicationComponent(UserACL.class);
        Iterator<PortalConfig> iterPortals = tempArrayList.iterator();
        PortalConfig portalConfig;
        while (iterPortals.hasNext()) {
            portalConfig = iterPortals.next();
            if (!userACL.hasEditPermission(portalConfig)) {
                iterPortals.remove();
            }
        }

        this.pageList = new LazyPageList<PortalConfig>(new ListAccess<PortalConfig>() {

            public int getSize() throws Exception {
                return tempArrayList.size();
            }

            public PortalConfig[] load(int index, int length) throws Exception {
                PortalConfig[] pcs = new PortalConfig[tempArrayList.size()];

                if (index < 0) {
                    throw new IllegalArgumentException("Illegal index: index must be a positive number");
                }

                if (length < 0) {
                    throw new IllegalArgumentException("Illegal length: length must be a positive number");
                }

                if (index + length > tempArrayList.size()) {
                    throw new IllegalArgumentException(
                            "Illegal index or length: sum of the index and the length cannot be greater than the list size");
                }

                for (int i = 0; i < length; i++) {
                    pcs[i] = tempArrayList.get(i + index);
                }

                return pcs;
            }

        }, 10);

    }

    public void setScope(Scope scope) {
        this.navigationScope = scope;
    }

    public Scope getScope() {
        return this.navigationScope;
    }

    private boolean stillKeptInPageList(String portalName) throws Exception {
        List<PortalConfig> portals = this.getPortalConfigs();
        for (PortalConfig p : portals) {
            if (p.getName().equals(portalName))
                return true;
        }
        return false;
    }

    public static class DeletePortalActionListener extends EventListener<UISiteManagement> {
        public void execute(Event<UISiteManagement> event) throws Exception {
            UISiteManagement uicomp = event.getSource();
            String portalName = event.getRequestContext().getRequestParameter(OBJECTID);

            UserPortalConfigService service = uicomp.getApplicationComponent(UserPortalConfigService.class);
            String defaultPortalName = service.getDefaultPortal();

            PortalRequestContext prContext = Util.getPortalRequestContext();
            UIPortalApplication uiPortalApp = Util.getUIPortalApplication();

            if (defaultPortalName.equals(portalName)) {
                uiPortalApp.addMessage(new ApplicationMessage("UISiteManagement.msg.delete-default-portal",
                        new String[] { defaultPortalName }, ApplicationMessage.WARNING));
                return;
            }

            DataStorage dataStorage = uicomp.getApplicationComponent(DataStorage.class);
            UserACL acl = uicomp.getApplicationComponent(UserACL.class);

            PortalConfig pConfig = dataStorage.getPortalConfig(portalName);
            if (pConfig != null) {
                if (acl.hasPermission(pConfig)) {
                    service.removeUserPortalConfig(portalName);
                } else {
                    uiPortalApp.addMessage(new ApplicationMessage("UISiteManagement.msg.Invalid-deletePermission",
                            new String[] { pConfig.getName() }));
                    return;
                }
            } else {
                if (uicomp.stillKeptInPageList(portalName)) {
                    uiPortalApp.addMessage(new ApplicationMessage("UISiteManagement.msg.portal-not-exist",
                            new String[] { portalName }));
                }
                return;
            }

            if (pConfig == null && !Util.getUIPortal().getName().equals(portalName)) {
                uiPortalApp.addMessage(new ApplicationMessage("UISiteManagement.msg.Invalid-deletePermission",
                        new String[] { portalName }));
                return;
            }

            if (pConfig == null || Util.getUIPortal().getName().equals(portalName)) {
                HttpServletRequest request = prContext.getRequest();
                LogoutControl.wantLogout();
                event.getRequestContext().sendRedirect(request.getContextPath());
                return;
            }

            event.getRequestContext().addUIComponentToUpdateByAjax(uicomp);
        }
    }

    public static class EditPortalLayoutActionListener extends EventListener<UISiteManagement> {
        public void execute(Event<UISiteManagement> event) throws Exception {
            UISiteManagement uicomp = event.getSource();
            String portalName = event.getRequestContext().getRequestParameter(OBJECTID);
            UserPortalConfigService service = uicomp.getApplicationComponent(UserPortalConfigService.class);
            DataStorage dataStorage = uicomp.getApplicationComponent(DataStorage.class);
            PortalRequestContext prContext = Util.getPortalRequestContext();
            UIPortalApplication portalApp = (UIPortalApplication) prContext.getUIApplication();
            UIWorkingWorkspace uiWorkingWS = portalApp.getChildById(UIPortalApplication.UI_WORKING_WS_ID);

            PortalConfig pConfig = dataStorage.getPortalConfig(portalName);

            if (pConfig == null) {
                portalApp.addMessage(new ApplicationMessage("UISiteManagement.msg.portal-not-exist",
                        new String[] { portalName }));
                uiWorkingWS.updatePortletsByName("UserToolbarSitePortlet");
                return;
            }

            UserACL userACL = portalApp.getApplicationComponent(UserACL.class);
            if (!userACL.hasEditPermission(pConfig)) {
                portalApp.addMessage(new ApplicationMessage("UISiteManagement.msg.Invalid-editPermission",
                        new String[] { pConfig.getName() }));
                return;
            }

            // UIEditInlineWorkspace uiEditWS = uiWorkingWS.addChild(UIEditInlineWorkspace.class, null,
            // UIPortalApplication.UI_EDITTING_WS_ID);
            UIEditInlineWorkspace uiEditWS = uiWorkingWS.getChildById(UIPortalApplication.UI_EDITTING_WS_ID);
            UIPortalComposer uiComposer = uiEditWS.getComposer().setRendered(true);
            uiComposer.setEditted(false);
            uiComposer.setCollapse(false);
            uiComposer.setShowControl(true);
            uiComposer.setComponentConfig(UIPortalComposer.class, null);
            uiComposer.setId(UIPortalComposer.UIPORTAL_COMPOSER);

            UIPortal uiPortal = Util.getUIPortal();
            uiWorkingWS.setBackupUIPortal(uiPortal);

            UIPortal editPortal = uiWorkingWS.createUIComponent(UIPortal.class, null, null);
            PortalDataMapper.toUIPortal(editPortal, pConfig);
            uiEditWS.setUIComponent(editPortal);

            // Check if edit current portal
            if (uiPortal.getName().equals(editPortal.getName())) {
                // editPortal.setSelectedNode(uiPortal.getSelectedNode());
                // editPortal.setNavigation(uiPortal.getNavigation());
                // editPortal.setSelectedPath(uiPortal.getSelectedPath());
                editPortal.setNavPath(uiPortal.getNavPath());
                UISiteBody siteBody = uiWorkingWS.findFirstComponentOfType(UISiteBody.class);
                siteBody.setUIComponent(null);
            }

            editPortal.refreshUIPage();
            portalApp.setModeState(UIPortalApplication.APP_BLOCK_EDIT_MODE);
            uiWorkingWS.setRenderedChild(UIPortalApplication.UI_EDITTING_WS_ID);

            prContext.addUIComponentToUpdateByAjax(uiWorkingWS);
            prContext.ignoreAJAXUpdateOnPortlets(true);
        }
    }

    public static class EditNavigationActionListener extends EventListener<UISiteManagement> {

        public void execute(Event<UISiteManagement> event) throws Exception {
            UISiteManagement uicomp = event.getSource();
            String portalName = event.getRequestContext().getRequestParameter(OBJECTID);
            WebuiRequestContext context = event.getRequestContext();
            UIApplication uiApplication = context.getUIApplication();

            // Minh Hoang TO: User could edit navigation if he/she has edit permissions on PortalConfig. That is not
            // at all logical and should be modified after release 3.1 GA
            UserPortalConfigService configService = uicomp.getApplicationComponent(UserPortalConfigService.class);
            UserPortalConfig userPortalConfig = configService.getUserPortalConfig(portalName, context.getRemoteUser(),
                    PortalRequestContext.USER_PORTAL_CONTEXT);
            if (userPortalConfig == null) {
                uiApplication.addMessage(new ApplicationMessage("UISiteManagement.msg.portal-not-exist",
                        new String[] { portalName }));
                UIWorkingWorkspace uiWorkingWS = Util.getUIPortalApplication().getChildById(
                        UIPortalApplication.UI_WORKING_WS_ID);
                uiWorkingWS.updatePortletsByName("UserToolbarSitePortlet");
                return;
            }

            UserACL userACL = uicomp.getApplicationComponent(UserACL.class);
            if (!userACL.hasEditPermission(userPortalConfig.getPortalConfig())) {
                uiApplication.addMessage(new ApplicationMessage("UISiteManagement.msg.Invalid-editPermission", null));
                return;
            }

            // Minh Hoang TO: For release 3.1, Edit Permission check would be rollback to former checks on PortalConfig
            /*
             * if (edittedNavigation == null) { uiApplication.addMessage(new
             * ApplicationMessage("UISiteManagement.msg.portal-not-exist", new String[]{portalName})); return; }
             *
             * UserACL userACL = uicomp.getApplicationComponent(UserACL.class); if
             * (!userACL.hasEditPermission(edittedNavigation)) { uiApplication.addMessage(new
             * ApplicationMessage("UISiteManagement.msg.Invalid-editPermission", null));; return; }
             */

            UIPopupWindow popUp = uicomp.getChild(UIPopupWindow.class);
            UINavigationManagement naviManager = popUp.createUIComponent(UINavigationManagement.class, null, null, popUp);
            uicomp.naviManager = naviManager;

            naviManager.setSiteKey(SiteKey.portal(portalName));

            UserPortal userPortal = userPortalConfig.getUserPortal();
            UserNavigation edittedNavigation = userPortal.getNavigation(SiteKey.portal(portalName));

            UINavigationNodeSelector selector = naviManager.getChild(UINavigationNodeSelector.class);
            selector.setScope(uicomp.getScope());
            selector.setEdittedNavigation(edittedNavigation);
            selector.setUserPortal(userPortal);
            selector.initTreeData();
            popUp.setUIComponent(naviManager);
            popUp.setShowMask(true);
            popUp.setShow(true);
            popUp.setWindowSize(400, 400);
        }
    }

    public static class BackActionListener extends EventListener<UIPageNodeForm> {

        public void execute(Event<UIPageNodeForm> event) throws Exception {
            UIPageNodeForm uiPageNodeForm = event.getSource();
            UISiteManagement uiSiteManagement = uiPageNodeForm.getAncestorOfType(UISiteManagement.class);
            UINavigationManagement navigationManager = uiSiteManagement.naviManager;

            UINavigationNodeSelector selector = navigationManager.getChild(UINavigationNodeSelector.class);
            TreeNode selectedParent = (TreeNode) uiPageNodeForm.getSelectedParent();
            selector.setScope(uiSiteManagement.getScope());
            selector.selectNode(selectedParent);

            WebuiRequestContext context = event.getRequestContext();
            UIPopupWindow uiNavigationPopup = uiSiteManagement.getChild(UIPopupWindow.class);
            uiNavigationPopup.setUIComponent(navigationManager);
            uiNavigationPopup.setWindowSize(400, 400);
            context.addUIComponentToUpdateByAjax(uiNavigationPopup.getParent());

            TreeNode pageNode = uiPageNodeForm.getPageNode();
            if (pageNode != null) {
                selector.getUserNodeLabels().put(pageNode.getId(), pageNode.getI18nizedLabels());
            }
            selector.createEvent("NodeModified", Phase.PROCESS, context).broadcast();
        }

    }

    public static class CloseActionListener extends UIPopupWindow.CloseActionListener {
        public void execute(Event<UIPopupWindow> event) throws Exception {
            UIPopupWindow popWindow = event.getSource();
            popWindow.setUIComponent(null);

            UISiteManagement siteMan = popWindow.getAncestorOfType(UISiteManagement.class);
            siteMan.naviManager = null;

            super.execute(event);
        }
    }

}
