/*
 * Copyright (C) 2003-2007 eXo Platform SAS.
 *
 * This program is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Affero General Public License
 * as published by the Free Software Foundation; either version 3
 * of the License, or (at your option) any later version.
 *
 * This program 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 General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, see<http://www.gnu.org/licenses/>.
 */
package org.exoplatform.ecm.webui.component.explorer.popup.actions;

import org.exoplatform.ecm.jcr.model.Preference;
import org.exoplatform.ecm.webui.component.explorer.UIJCRExplorer;
import org.exoplatform.ecm.webui.component.explorer.sidebar.UISideBar;
import org.exoplatform.ecm.webui.form.UIFormInputSetWithAction;
import org.exoplatform.ecm.webui.utils.Utils;
import org.exoplatform.services.cms.folksonomy.NewFolksonomyService;
import org.exoplatform.services.jcr.core.ManageableRepository;
import org.exoplatform.services.jcr.ext.common.SessionProvider;
import org.exoplatform.services.jcr.ext.hierarchy.NodeHierarchyCreator;
import org.exoplatform.services.wcm.utils.WCMCoreUtils;
import org.exoplatform.web.application.ApplicationMessage;
import org.exoplatform.web.application.RequestContext;
import org.exoplatform.webui.application.WebuiRequestContext;
import org.exoplatform.webui.config.annotation.ComponentConfig;
import org.exoplatform.webui.config.annotation.EventConfig;
import org.exoplatform.webui.core.UIApplication;
import org.exoplatform.webui.core.lifecycle.UIFormLifecycle;
import org.exoplatform.webui.core.model.SelectItemOption;
import org.exoplatform.webui.event.Event;
import org.exoplatform.webui.event.Event.Phase;
import org.exoplatform.webui.event.EventListener;
import org.exoplatform.webui.form.*;

import javax.jcr.Node;
import java.util.*;

/**
 * Created by The eXo Platform SARL Author : Dang Van Minh
 * minh.dang@exoplatform.com Jan 12, 2007 11:56:51 AM
 */
@ComponentConfig(lifecycle = UIFormLifecycle.class, template = "system:/groovy/webui/form/UIForm.gtmpl",
events = {
  @EventConfig(listeners = UITaggingForm.AddTagActionListener.class),
  @EventConfig(listeners = UITaggingForm.EditActionListener.class, phase = Phase.DECODE),
  @EventConfig(listeners = UITaggingForm.RemoveActionListener.class, phase = Phase.DECODE),
  @EventConfig(listeners = UITaggingForm.CancelActionListener.class, phase = Phase.DECODE),
  @EventConfig(listeners = UITaggingForm.ChangeActionListener.class, phase = Phase.DECODE) })
public class UITaggingForm extends UIForm {

  final static public String  TAG_NAME_LIST          = "tagNameList";

  final static public String  TAG_NAMES              = "names";

  final static public String  TAG_SCOPES             = "tagScopes";

  final static public String  LINKED_TAGS            = "linked";

  final static public String  LINKED_TAGS_SET        = "tagSet";

  private static final String USER_FOLKSONOMY_ALIAS  = "userPrivateFolksonomy";

  private static final String GROUP_FOLKSONOMY_ALIAS = "folksonomy";

  private static final String GROUPS_ALIAS           = "groupsPath";

  final static public String  PUBLIC_TAG_NODE_PATH   = "exoPublicTagNode";

  public UITaggingForm() throws Exception {
    UIFormInputSetWithActionForTaggingForm uiInputSet = new UIFormInputSetWithActionForTaggingForm(LINKED_TAGS_SET);
    uiInputSet.addUIFormInput(new UIFormStringInput(TAG_NAMES, TAG_NAMES, null));
    uiInputSet.addUIFormInput(new UIFormTextAreaInput(TAG_NAME_LIST, TAG_NAME_LIST, null));

    RequestContext context = RequestContext.getCurrentInstance();
    ResourceBundle res = context.getApplicationResourceBundle();
    List<SelectItemOption<String>> tagScopes = new ArrayList<SelectItemOption<String>>();
    /* Only Public tag enabled in PLF 4
    tagScopes.add(new SelectItemOption<String>(res.getString("UITaggingForm.label." + Utils.PRIVATE),
                                            Utils.PRIVATE));
     */
    tagScopes.add(new SelectItemOption<String>(res.getString("UITaggingForm.label." + Utils.PUBLIC),
        Utils.PUBLIC));
    /*
     * Disable Group and Site tag tagScopes.add(new
     * SelectItemOption<String>(res.getString("UITaggingForm.label." +
     * Utils.GROUP), Utils.GROUP)); tagScopes.add(new
     * SelectItemOption<String>(res.getString("UITaggingForm.label." +
     * Utils.SITE), Utils.SITE));
     */
    UIFormSelectBox box = new UIFormSelectBox(TAG_SCOPES, TAG_SCOPES, tagScopes);
    box.setOnChange("Change");
    uiInputSet.addUIFormInput(box);
    box.setSelectedValues(new String[] { Utils.PUBLIC });
    box.setRendered(false);

    uiInputSet.addUIFormInput(new UIFormInputInfo(LINKED_TAGS, LINKED_TAGS, null));
    uiInputSet.setIntroduction(TAG_NAMES, "UITaggingForm.introduction.tagName");
    addUIComponentInput(uiInputSet);
    uiInputSet.setIsView(false);
    super.setActions(new String[] {"Cancel" });
  }

  public void activate() throws Exception {

    String workspace = WCMCoreUtils.getRepository().getConfiguration().getDefaultWorkspaceName();
    NewFolksonomyService folksonomyService = WCMCoreUtils.getService(NewFolksonomyService.class);

    StringBuilder linkedTags = new StringBuilder();
    Set<String> linkedTagSet = new HashSet<String>();
    String tagScope = this.getUIFormSelectBox(TAG_SCOPES).getValue();

    Node currentNode = getAncestorOfType(UIJCRExplorer.class).getCurrentNode();
    getAncestorOfType(UIJCRExplorer.class).setTagScope(getIntValue(tagScope));
    for (Node tag : folksonomyService.getLinkedTagsOfDocumentByScope(getIntValue(tagScope),
                                                                     getStrValue(tagScope,
                                                                                 currentNode),
                                                                                 currentNode,
                                                                                 workspace)) {
      linkedTagSet.add(tag.getName());
    }

    List<String> linkedTagList = new ArrayList<String>(linkedTagSet);
    Collections.sort(linkedTagList);

    for (String tagName : linkedTagList) {
      if (linkedTags.length() > 0)
        linkedTags = linkedTags.append(",");
      linkedTags.append(tagName);
    }

    UIFormInputSetWithAction uiLinkedInput = getChildById(LINKED_TAGS_SET);
    uiLinkedInput.setInfoField(LINKED_TAGS, linkedTags.toString());
    //check if current user can remove tag
    NewFolksonomyService newFolksonomyService = WCMCoreUtils.getService(NewFolksonomyService.class);
    List<String> memberships = Utils.getMemberships();
    String[] actionsForTags = newFolksonomyService.canEditTag(this.getIntValue(tagScope), memberships) ?
                  new String[] {"Edit", "Remove"} : null;
    uiLinkedInput.setActionInfo(LINKED_TAGS, actionsForTags);
    uiLinkedInput.setIsShowOnly(true);
    uiLinkedInput.setIsDeleteOnly(false);
  }

  public void deActivate() throws Exception {
  }

  public int getIntValue(String scope) {
    if (Utils.PUBLIC.equals(scope))
      return NewFolksonomyService.PUBLIC;
    else if (Utils.GROUP.equals(scope))
      return NewFolksonomyService.GROUP;
    else if (Utils.PRIVATE.equals(scope))
      return NewFolksonomyService.PRIVATE;
    return NewFolksonomyService.SITE;
  }

  public List<String> getAllTagNames() throws Exception {
    String workspace = WCMCoreUtils.getRepository().getConfiguration().getDefaultWorkspaceName();
    NewFolksonomyService folksonomyService = getApplicationComponent(NewFolksonomyService.class);

    String tagScope = this.getUIFormSelectBox(TAG_SCOPES).getValue();
    Node currentNode = getAncestorOfType(UIJCRExplorer.class).getCurrentNode();

    return folksonomyService.getAllTagNames(workspace,
                                            getIntValue(tagScope),
                                            getStrValue(tagScope, currentNode));
  }

  @Override
  public void processRender(WebuiRequestContext context) throws Exception {
    //    context.getJavascriptManager().importJavascript("eXo.ecm.ECMUtils",
    //                                                    "/ecm-wcm-extension/javascript/");

    context.getJavascriptManager().require("SHARED/ecm-utils", "ecmutil").
    addScripts("ecmutil.ECMUtils.disableAutocomplete('UITaggingForm');");
    super.processRender(context);
  }

  private String getStrValue(String scope, Node node) throws Exception {
    StringBuilder ret = new StringBuilder();
    if (Utils.PRIVATE.equals(scope))
      ret.append(node.getSession().getUserID());
    else if (Utils.GROUP.equals(scope)) {
      for (String group : Utils.getGroups())
        ret.append(group).append(';');
      ret.deleteCharAt(ret.length() - 1);
    } else if (Utils.PUBLIC.equals(scope)) {
      NodeHierarchyCreator nodeHierarchyCreator = getApplicationComponent(NodeHierarchyCreator.class);
      ret.append(nodeHierarchyCreator.getJcrPath(PUBLIC_TAG_NODE_PATH));
    }
    return ret.toString();
  }

  static public class AddTagActionListener extends EventListener<UITaggingForm> {
    public void execute(Event<UITaggingForm> event) throws Exception {
      UITaggingForm uiForm = event.getSource();
      UIApplication uiApp = uiForm.getAncestorOfType(UIApplication.class);
      String workspace = uiForm.getAncestorOfType(UIJCRExplorer.class)
          .getRepository()
          .getConfiguration()
          .getDefaultWorkspaceName();
      String tagName = uiForm.getUIStringInput(TAG_NAMES).getValue();
      NewFolksonomyService newFolksonomyService = uiForm.getApplicationComponent(NewFolksonomyService.class);
      UIJCRExplorer uiExplorer = uiForm.getAncestorOfType(UIJCRExplorer.class);
      Node currentNode = uiExplorer.getCurrentNode();
      uiExplorer.addLockToken(currentNode);
      if (tagName == null || tagName.trim().length() == 0) {
        uiApp.addMessage(new ApplicationMessage("UITaggingForm.msg.tag-name-empty",
                                                null,
                                                ApplicationMessage.WARNING));
	      event.getRequestContext().addUIComponentToUpdateByAjax(uiForm);
        return;
      }
      String[] tagNames;
      tagNames = tagName.split(",");
      List<String> listTagNames = new ArrayList<String>(tagNames.length);
      List<String> listTagNamesClone = new ArrayList<String>(tagNames.length);

      if (tagName.contains(",")) {
        for (String tName : tagNames) {
          listTagNames.add(tName.trim());
          listTagNamesClone.add(tName.trim());
        }
        for (String tag : listTagNames) {
          listTagNamesClone.remove(tag);
          if (listTagNamesClone.contains(tag)) {
            continue;
          }
          listTagNamesClone.add(tag);
        }
      } else {
        listTagNames.add(tagName.trim());
        listTagNamesClone.add(tagName.trim());
      }
      for (String t : listTagNames) {
        if (t.trim().length() == 0) {
          listTagNamesClone.remove(t);
          continue;
        }
        if (t.trim().length() > 30) {
          listTagNamesClone.remove(t);
          continue;
        }
        for (String filterChar : Utils.SPECIALCHARACTER) {
          if (t.contains(filterChar)) {
            listTagNamesClone.remove(t);
          }
        }
      }
      if(listTagNamesClone.size() == 0) {
	      uiApp.addMessage(new ApplicationMessage("UITaggingForm.msg.tagName-empty-or-invalid",
		      null,
		      ApplicationMessage.WARNING));
	      event.getRequestContext().addUIComponentToUpdateByAjax(uiForm);
	      return;
      }
      String tagScope = uiForm.getUIFormSelectBox(TAG_SCOPES).getValue();
      List<Node> tagList = newFolksonomyService.getLinkedTagsOfDocumentByScope(uiForm.getIntValue(tagScope),
                                                                               uiForm.getStrValue(tagScope,
                                                                               currentNode),
                                                                               uiExplorer.getCurrentNode(),
                                                                               workspace);
      for (Node tag : tagList) {
        for (String t : listTagNames) {
          if (t.equals(tag.getName())) {
            listTagNamesClone.remove(t);
          }
        }
      }
      if(listTagNamesClone.size() > 0)
        addTagToNode(tagScope, currentNode, listTagNamesClone.toArray(new String[listTagNamesClone.size()]), uiForm);
      uiForm.activate();

      Preference preferences = uiExplorer.getPreference();
      if (preferences.isShowSideBar()) {
        UISideBar uiSideBar = uiExplorer.findFirstComponentOfType(UISideBar.class);
        event.getRequestContext().addUIComponentToUpdateByAjax(uiSideBar);
      }
      uiForm.getUIStringInput(TAG_NAMES).setValue(null);
      event.getRequestContext().addUIComponentToUpdateByAjax(uiForm);
    }

    private void addTagToNode(String scope,
                              Node currentNode,
                              String[] tagNames,
                              UITaggingForm uiForm) throws Exception {

      NewFolksonomyService newFolksonomyService = uiForm.getApplicationComponent(NewFolksonomyService.class);
      ManageableRepository manageableRepo = WCMCoreUtils.getRepository();
      NodeHierarchyCreator nodeHierarchyCreator = uiForm.getApplicationComponent(NodeHierarchyCreator.class);
      String workspace = manageableRepo.getConfiguration().getDefaultWorkspaceName();
      List<String> groups = Utils.getGroups();
      String[] roles = groups.toArray(new String[groups.size()]);
      String publicTagNodePath = nodeHierarchyCreator.getJcrPath(PUBLIC_TAG_NODE_PATH);

      if (Utils.PUBLIC.equals(scope))
        newFolksonomyService.addPublicTag(publicTagNodePath,
                                          tagNames,
                                          currentNode,
                                          workspace);
      // else if (SITE.equals(scope))
      // newFolksonomyService.addSiteTag(siteName, treePath, tagNames,
      // currentNode, repository, workspace);
      else if (Utils.GROUP.equals(scope))
        newFolksonomyService.addGroupsTag(tagNames, currentNode, workspace, roles);
      else if (Utils.PRIVATE.equals(scope)) {
        String userName = currentNode.getSession().getUserID();
        newFolksonomyService.addPrivateTag(tagNames, currentNode, workspace, userName);
      }
    }
  }

  static public class CancelActionListener extends EventListener<UITaggingForm> {
    public void execute(Event<UITaggingForm> event) throws Exception {
      UIJCRExplorer uiExplorer = event.getSource().getAncestorOfType(UIJCRExplorer.class);
      uiExplorer.cancelAction();
    }
  }

  static public class RemoveActionListener extends EventListener<UITaggingForm> {
    public void execute(Event<UITaggingForm> event) throws Exception {
      UITaggingForm uiForm = event.getSource();
      UIJCRExplorer uiExplorer = uiForm.getAncestorOfType(UIJCRExplorer.class);
      NewFolksonomyService newFolksonomyService = uiForm.getApplicationComponent(NewFolksonomyService.class);
      String tagScope = uiForm.getUIFormSelectBox(TAG_SCOPES).getValue();
      List<String> memberships = Utils.getMemberships();
      if (!newFolksonomyService.canEditTag(uiForm.getIntValue(tagScope), memberships)) {
        UIApplication uiApp = uiForm.getAncestorOfType(UIApplication.class);
        uiApp.addMessage(new ApplicationMessage("UIPopupMenu.msg.editTagAccessDenied",
                                                null,
                                                ApplicationMessage.WARNING));

        return;
      }

      Node currentNode = uiExplorer.getCurrentNode();
      String tagName = event.getRequestContext().getRequestParameter(OBJECTID);
      UIApplication uiApp = uiForm.getAncestorOfType(UIApplication.class);
      if (!Utils.isNameValid(tagName, Utils.SPECIALCHARACTER)) {
        uiApp.addMessage(new ApplicationMessage("UITaggingForm.msg.tagName-invalid",
                                                null,
                                                ApplicationMessage.WARNING));

        return;
      }
      removeTagFromNode(tagScope, currentNode, tagName, uiForm);
      uiForm.activate();

      Preference preferences = uiExplorer.getPreference();
      if (preferences.isShowSideBar()) {
        UISideBar uiSideBar = uiExplorer.findFirstComponentOfType(UISideBar.class);
        event.getRequestContext().addUIComponentToUpdateByAjax(uiSideBar);
      }
      event.getRequestContext().addUIComponentToUpdateByAjax(uiForm);
    }

    public void removeTagFromNode(String scope,
                                  Node currentNode,
                                  String tagName,
                                  UITaggingForm uiForm) throws Exception {

      NewFolksonomyService newFolksonomyService = uiForm.getApplicationComponent(NewFolksonomyService.class);
      NodeHierarchyCreator nodeHierarchyCreator = uiForm.getApplicationComponent(NodeHierarchyCreator.class);
      String userName = currentNode.getSession().getUserID();
      String workspace = WCMCoreUtils.getRepository().getConfiguration().getDefaultWorkspaceName();

      String tagPath;
      if (Utils.PUBLIC.equals(scope)) {
        tagPath = newFolksonomyService.getDataDistributionType().getDataNode(
                                                                             (Node)(WCMCoreUtils.getUserSessionProvider().getSession(workspace, WCMCoreUtils.getRepository()).getItem(
                                                                                                                                                                                      nodeHierarchyCreator.getJcrPath(PUBLIC_TAG_NODE_PATH))),
                                                                                                                                                                                      tagName).getPath();
        newFolksonomyService.removeTagOfDocument(tagPath, currentNode, workspace);
      } else if (Utils.PRIVATE.equals(scope)) {
        Node userFolksonomyNode = getUserFolksonomyFolder(userName, uiForm);
        tagPath = newFolksonomyService.getDataDistributionType().getDataNode(userFolksonomyNode, tagName).getPath();
        newFolksonomyService.removeTagOfDocument(tagPath, currentNode, workspace);
      } else if (Utils.GROUP.equals(scope)) {
        String groupsPath = nodeHierarchyCreator.getJcrPath(GROUPS_ALIAS);
        String folksonomyPath = nodeHierarchyCreator.getJcrPath(GROUP_FOLKSONOMY_ALIAS);
        Node groupsNode = getNode(workspace, groupsPath);
        for (String role : Utils.getGroups()) {
          tagPath = newFolksonomyService.getDataDistributionType().getDataNode(groupsNode.getNode(role).getNode(folksonomyPath), 
                                                                               tagName).getPath();
          newFolksonomyService.removeTagOfDocument(tagPath, currentNode, workspace);
        }
      }
    }

    private Node getNode(String workspace, String path) throws Exception {
      SessionProvider sessionProvider = WCMCoreUtils.getUserSessionProvider();
      return (Node) sessionProvider.getSession(workspace, WCMCoreUtils.getRepository()).getItem(path);
    }

    private Node getUserFolksonomyFolder(String userName, UITaggingForm uiForm) throws Exception {
      NodeHierarchyCreator nodeHierarchyCreator = uiForm.getApplicationComponent(NodeHierarchyCreator.class);
      Node userNode = nodeHierarchyCreator.getUserNode(WCMCoreUtils.getUserSessionProvider(), userName);
      String folksonomyPath = nodeHierarchyCreator.getJcrPath(USER_FOLKSONOMY_ALIAS);
      return userNode.getNode(folksonomyPath);
    }
  }

  static public class ChangeActionListener extends EventListener<UITaggingForm> {
    public void execute(Event<UITaggingForm> event) throws Exception {
      UITaggingForm uiForm = event.getSource();
      uiForm.activate();
    }
  }

  static public class EditActionListener extends EventListener<UITaggingForm> {
    public void execute(Event<UITaggingForm> event) throws Exception {
      UITaggingForm uiForm = event.getSource();
      NewFolksonomyService newFolksonomyService = uiForm.getApplicationComponent(NewFolksonomyService.class);
      String tagScope = uiForm.getUIFormSelectBox(TAG_SCOPES).getValue();
      List<String> memberships = Utils.getMemberships();
      if (!newFolksonomyService.canEditTag(uiForm.getIntValue(tagScope), memberships)) {
        UIApplication uiApp = uiForm.getAncestorOfType(UIApplication.class);
        uiApp.addMessage(new ApplicationMessage("UIPopupMenu.msg.editTagAccessDenied",
                                                null,
                                                ApplicationMessage.WARNING));

        return;
      }
      ((UITaggingFormContainer) uiForm.getParent()).edit(event);
    }
  }

}
