/*
 * Copyright (C) 2003-2008 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.wcm.webui.scv;

import org.apache.commons.lang3.StringUtils;
import org.exoplatform.ecm.utils.lock.LockUtil;
import org.exoplatform.portal.application.PortalRequestContext;
import org.exoplatform.portal.mop.SiteType;
import org.exoplatform.portal.webui.application.UIPortlet;
import org.exoplatform.portal.webui.util.Util;
import org.exoplatform.services.cms.templates.TemplateService;
import org.exoplatform.services.ecm.publication.PublicationService;
import org.exoplatform.services.jcr.core.ManageableRepository;
import org.exoplatform.services.jcr.util.Text;
import org.exoplatform.services.log.ExoLogger;
import org.exoplatform.services.log.Log;
import org.exoplatform.services.wcm.publication.WCMComposer;
import org.exoplatform.services.wcm.utils.WCMCoreUtils;
import org.exoplatform.wcm.webui.Utils;
import org.exoplatform.wcm.webui.reader.ContentReader;
import org.exoplatform.web.url.navigation.NavigationResource;
import org.exoplatform.web.url.navigation.NodeURL;
import org.exoplatform.webui.application.WebuiRequestContext;
import org.exoplatform.webui.application.portlet.PortletRequestContext;
import org.exoplatform.webui.config.annotation.ComponentConfig;
import org.exoplatform.webui.config.annotation.EventConfig;
import org.exoplatform.webui.core.UIContainer;
import org.exoplatform.webui.core.lifecycle.Lifecycle;
import org.exoplatform.webui.event.Event;
import org.exoplatform.webui.event.EventListener;

import javax.jcr.Node;
import javax.jcr.RepositoryException;
import javax.jcr.ValueFormatException;
import javax.portlet.PortletPreferences;
import java.io.UnsupportedEncodingException;
import java.net.URLDecoder;
import java.text.SimpleDateFormat;
import java.util.*;

/**
 * Author : Do Ngoc Anh *
 * Email: anh.do@exoplatform.com *
 * May 14, 2008
 */
@ComponentConfig(
    lifecycle=Lifecycle.class,
    template="app:/groovy/SingleContentViewer/UIPresentationContainer.gtmpl",
    events = {
        @EventConfig(listeners=UIPresentationContainer.PreferencesActionListener.class),
        @EventConfig(listeners=UIPresentationContainer.FastPublishActionListener.class)
    }
)
public class UIPresentationContainer extends UIContainer{
  public final static String PARAMETER_REGX       = "(.*)/(.*)";
  private static final Log         LOG            = ExoLogger.getLogger(UIPresentationContainer.class.getName());

  private boolean isPrint = false;
  private PortletPreferences portletPreferences;
  private String contentParameter = null;
  /**
   * Instantiates a new uI presentation container.
   *
   * @throws Exception the exception
   */
  public UIPresentationContainer() throws Exception{
    PortletRequestContext portletRequestContext = WebuiRequestContext.getCurrentInstance();
    addChild(UIPresentation.class, null, UIPresentation.class.getSimpleName() + portletRequestContext.getWindowId());

    portletPreferences = portletRequestContext.getRequest().getPreferences();
  }

  /**
   * Gets the bar info show.
   *
   * @return the value for info bar setting
   *
   * @throws Exception the exception
   */
  public boolean isShowInfoBar() throws Exception {
    if (UIPortlet.getCurrentUIPortlet().getShowInfoBar())
      return true;
    return false;
  }

  /**
   * Gets the title.
   *
   * @param node the node
   *
   * @return the title
   *
   * @throws Exception the exception
   */
  public String getTitle(Node node) throws Exception {
    String title = null;
    if (node.hasProperty("exo:title")) {
      title = node.getProperty("exo:title").getValue().getString().trim();
    }
    if (title == null || title.equals("")) {
      if (node.hasNode("jcr:content")) {
        Node content = node.getNode("jcr:content");
        if (content.hasProperty("dc:title")) {
          try {
            title = content.getProperty("dc:title").getValues()[0].getString().trim();
          } catch (ValueFormatException e) {
            title = null;
          }
          catch (IllegalStateException e) {
            title = null;
          }
          catch (RepositoryException e) {
            title = null;
          }
        }
      }
    }
    if (title == null || title.equals("")) {
      title = Utils.getRealNode(node).getName();
    }
    return ContentReader.getXSSCompatibilityContent(title);
  }

  public boolean isPrinting() {
    return this.isPrint;
  }

  public boolean isShowTitle() {
    return Boolean.parseBoolean(portletPreferences.getValue(UISingleContentViewerPortlet.SHOW_TITLE, "false"));
  }
  public boolean isShowDate() {
    return Boolean.parseBoolean(portletPreferences.getValue(UISingleContentViewerPortlet.SHOW_DATE, "false"));
  }
  public boolean isShowOptionBar() {
    return Boolean.parseBoolean(portletPreferences.getValue(UISingleContentViewerPortlet.SHOW_OPTIONBAR, "false"));
  }

  public boolean isContextual() {
    return Boolean.parseBoolean(portletPreferences.getValue(UISingleContentViewerPortlet.CONTEXTUAL_MODE, "false"));
  }

  public String getCurrentState() throws Exception {
    UIPresentation presentation = getChild(UIPresentation.class);
    Node node = presentation.getOriginalNode();
    PublicationService publicationService = WCMCoreUtils.getService(PublicationService.class);
    if(node == null || !publicationService.isNodeEnrolledInLifecycle(node)) return StringUtils.EMPTY;
    return publicationService.getCurrentState(node);
  }

  /**
   * Gets the created date.
   *
   * @param node the node
   *
   * @return the created date
   *
   * @throws Exception the exception
   */
  public String getCreatedDate(Node node) throws Exception {
    if (node.hasProperty("exo:dateCreated")) {
      Calendar calendar = node.getProperty("exo:dateCreated").getValue().getDate();
      return new SimpleDateFormat("dd.MM.yyyy '|' hh'h'mm").format(calendar.getTime());
    }
    return null;
  }

  /**
   * Gets the node.
   *
   * @return the node
   */
  public Node getNodeView() {
    UIPresentation presentation = getChild(UIPresentation.class);
    try {
      Node viewNode;
    //Check for the saved parameter
    viewNode = getParameterizedNode();
    if (viewNode!= null) {
      if (viewNode.isNodeType("nt:frozenNode")) {
            try {
              String nodeUUID = viewNode.getProperty("jcr:frozenUuid").getString();
              presentation.setOriginalNode(viewNode.getSession().getNodeByUUID(nodeUUID));
              presentation.setNode(viewNode);
            } catch (Exception ex) {
              return viewNode;
            }
      }
      return viewNode;
    }
    PortletRequestContext portletRequestContext = WebuiRequestContext.getCurrentInstance();
    portletPreferences = portletRequestContext.getRequest().getPreferences();
    String repository = portletPreferences.getValue(UISingleContentViewerPortlet.REPOSITORY, null);
    String workspace = portletPreferences.getValue(UISingleContentViewerPortlet.WORKSPACE, null);
    String nodeIdentifier = portletPreferences.getValue(UISingleContentViewerPortlet.IDENTIFIER, null);
    String sharedCache = portletPreferences.getValue(UISingleContentViewerPortlet.ENABLE_CACHE, "true");
    sharedCache = "true".equals(sharedCache) ? WCMComposer.VISIBILITY_PUBLIC:WCMComposer.VISIBILITY_USER;
    viewNode = Utils.getRealNode(repository, workspace, nodeIdentifier, false, sharedCache);
    if (viewNode!=null) {
        boolean isDocumentType = false;
        if (viewNode.isNodeType("nt:frozenNode")) isDocumentType = true;
        // check node is a document node
        TemplateService templateService = getApplicationComponent(TemplateService.class);
        List<String> documentTypes = templateService.getDocumentTemplates();
        for (String documentType : documentTypes) {
          if (viewNode.isNodeType(documentType)) {
            isDocumentType = true;
            break;
          }
        }
        if (!isDocumentType) return null;
        if (viewNode != null && viewNode.isNodeType("nt:frozenNode")) {
          String nodeUUID = viewNode.getProperty("jcr:frozenUuid").getString();
          presentation.setOriginalNode(viewNode.getSession().getNodeByUUID(nodeUUID));
          presentation.setNode(viewNode);
        } else {
          presentation.setOriginalNode(viewNode);
          presentation.setNode(viewNode);
        }
    }
      return viewNode;
    } catch (Exception e) {
      return null;
    }
  }
  /**
   * Gets the node.
   *
   * @return the node
   *
   * @throws Exception the exception
   */
  public Node getParameterizedNode() throws Exception {
    PortletRequestContext portletRequestContext = WebuiRequestContext.getCurrentInstance();
    PortletPreferences preferences = portletRequestContext.getRequest().getPreferences();
    String sharedCache = preferences.getValue(UISingleContentViewerPortlet.ENABLE_CACHE, "false");
    sharedCache = "true".equals(sharedCache) ? WCMComposer.VISIBILITY_PUBLIC:WCMComposer.VISIBILITY_USER;

    PortalRequestContext preq = Util.getPortalRequestContext();
    if (!preq.useAjax()) {
      contentParameter = getRequestParameters();
    }

    if (contentParameter == null) return null;
    UIPresentation presentation = getChild(UIPresentation.class);
    Node nodeView = Utils.getViewableNodeByComposer(null, null, contentParameter, null, sharedCache);
    if (nodeView!=null) {
      boolean isDocumentType = false;
      if (nodeView.isNodeType("nt:frozenNode")) isDocumentType = true;
      // check node is a document node
      if (!isDocumentType) {
        TemplateService templateService = getApplicationComponent(TemplateService.class);
        List<String> documentTypes = templateService.getDocumentTemplates();
        for (String documentType : documentTypes) {
          if (nodeView.isNodeType(documentType)) {
            isDocumentType = true;
            break;
          }
        }
      }
      if (!isDocumentType) return null;
      if (nodeView != null && nodeView.isNodeType("nt:frozenNode")) {
        String nodeUUID = nodeView.getProperty("jcr:frozenUuid").getString();
        presentation.setOriginalNode(nodeView.getSession().getNodeByUUID(nodeUUID));
        presentation.setNode(nodeView);
      } else {
        presentation.setOriginalNode(nodeView);
        presentation.setNode(nodeView);
      }
      isPrint = Boolean.parseBoolean(Util.getPortalRequestContext().getRequestParameter("isPrint"));
    }
    return nodeView;
  }

  /**
   * Gets the request parameters.
   *
   * @return the request parameters
   */
  private String getRequestParameters() throws Exception {
    String parameters = null;
    if (!Boolean.parseBoolean(portletPreferences.getValue(UISingleContentViewerPortlet.CONTEXTUAL_MODE, "false"))) {
      return null;
    }
    try {
      parameters = URLDecoder.decode(StringUtils.substringAfter(Util.getPortalRequestContext()
                                                                    .getNodePath(),
                                                                Util.getUIPortal()
                                                                    .getSelectedUserNode()
                                                                    .getURI()
                                                                    + "/"), "UTF-8");
    } catch (UnsupportedEncodingException e) {
      return null;
    }
    String parameterName = portletPreferences.getValue(UISingleContentViewerPortlet.PARAMETER, "");
    if (!parameters.matches(PARAMETER_REGX)) {
      String path = Util.getPortalRequestContext().getRequestParameter(parameterName);
      if (path == null){
        return null;
      }
      parameters = Text.unescape(Util.getPortalRequestContext().getRequestParameter(parameterName));
      return parameters.substring(1);
    }
    return Text.unescape(parameters);
  }

  /**
   * Get the print's page URL
   *
   * @return <code>true</code> if the Quick Print is shown. Otherwise, <code>false</code>
   */
  public String getPrintUrl(Node node) throws RepositoryException{
    String printParameterName;
    Node tempNode = node;
    if (tempNode==null) {
      tempNode = getNodeView();
    }
    String strPath = tempNode.getPath();
    String repository = ((ManageableRepository)tempNode.getSession().getRepository()).getConfiguration().getName();
    String workspace = tempNode.getSession().getWorkspace().getName();
    String printPageUrl = portletPreferences.getValue(UISingleContentViewerPortlet.PRINT_PAGE, "");
    printParameterName = portletPreferences.getValue(UISingleContentViewerPortlet.PRINT_PARAMETER, "");

    String paramName = "/" + repository + "/" + workspace + strPath;
    NodeURL nodeURL = Util.getPortalRequestContext().createURL(NodeURL.TYPE);
    NavigationResource resource = new NavigationResource(SiteType.PORTAL,
                                                         Util.getPortalRequestContext()
                                                             .getPortalOwner(), printPageUrl);
    nodeURL.setResource(resource);
    nodeURL.setQueryParameterValue(printParameterName, paramName);
    nodeURL.setQueryParameterValue("isPrint", "true");
    nodeURL.setQueryParameterValue("noadminbar", "true");
    return nodeURL.toString();
  }

  /**
   * Get the quick edit url
   *
   * @param node
   * @return
   */
  public String getQuickEditLink(Node node){
    Node tempNode = node;
    if (tempNode==null) {
      tempNode = getNodeView();
    }
    return Utils.getEditLink(tempNode, true, false);
  }

  public Node getOriginalNode() {
    UIPresentation presentation = getChild(UIPresentation.class);
    if (presentation == null)
      return null;
    try {
      return presentation.getOriginalNode();
    } catch (Exception e) {
      return null;
    }
  }

  public boolean isViewMode() {
    return Utils.getCurrentMode().equals(WCMComposer.MODE_LIVE);
  }

  public String getInlineEditingMsg() {
    StringBuffer sb = new StringBuffer();
    sb.append("new Array(");
    sb.append("'")
      .append(Text.escapeIllegalJcrChars(getResourceBundle("UIPresentationContainer.msg.internal-server-error")))
      .append("', '")
      .append(Text.escapeIllegalJcrChars(getResourceBundle("UIPresentationContainer.msg.empty-title-error")))
      .append("')");
    return sb.toString();
  }

  private String getResourceBundle(String key) {
    try {
      ResourceBundle rs = WebuiRequestContext.getCurrentInstance().getApplicationResourceBundle();
      return rs.getString(key);
    } catch(MissingResourceException e) {
      return key;
    }
  }


  /**
   * The listener interface for receiving preferencesAction events.
   * The class that is interested in processing a preferencesAction
   * event implements this interface, and the object created
   * with that class is registered with a component using the
   * component's <code>addPreferencesActionListener</code> method. When
   * the preferencesAction event occurs, that object's appropriate
   * method is invoked.
   */
  public static class PreferencesActionListener extends EventListener<UIPresentationContainer>{
    /* (non-Javadoc)
     * @see org.exoplatform.webui.event.EventListener#execute(org.exoplatform.webui.event.Event)
     */
    public void execute(Event<UIPresentationContainer> event) throws Exception {
      UIPresentationContainer presentationContainer = event.getSource();
      UISCVPreferences pcvConfigForm = presentationContainer.createUIComponent(UISCVPreferences.class, null, null);
      Utils.createPopupWindow(presentationContainer, pcvConfigForm, UISingleContentViewerPortlet.UIPreferencesPopupID, 600);
    }
  }

  public static class FastPublishActionListener extends EventListener<UIPresentationContainer> {

    /*
     * (non-Javadoc)
     * @see
     * org.exoplatform.webui.event.EventListener#execute(org.exoplatform.webui
     * .event.Event)
     */
    public void execute(Event<UIPresentationContainer> event) throws Exception {
      UIPresentationContainer uiContainer = event.getSource();
      PublicationService publicationService = WCMCoreUtils.getService(PublicationService.class);
      Node node = uiContainer.getNodeView();
      if (node.isLocked()) {
        node.getSession().addLockToken(LockUtil.getLockToken(node));
      }
      HashMap<String, String> context = new HashMap<String, String>();
      publicationService.changeState(node, "published", context);
      event.getRequestContext().getJavascriptManager().getRequireJS().addScripts("location.reload(true);");
    }

  }

  public String getInlineEditingField(Node orgNode, String propertyName, String defaultValue, String inputType,
      String idGenerator, String cssClass, boolean isGenericProperty, String... arguments) throws Exception {
    return org.exoplatform.ecm.webui.utils.Utils.getInlineEditingField(orgNode, propertyName, defaultValue,
                                                        inputType, idGenerator, cssClass, isGenericProperty, arguments);
  }
  public String getInlineEditingField(Node orgNode, String propertyName) throws Exception{
    return org.exoplatform.ecm.webui.utils.Utils.getInlineEditingField(orgNode, propertyName);
  }
}