/*
 * Copyright (C) 2003-2013 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 Affero General Public License for more details.
 *
 * You should have received a copy of the GNU Affero General Public License
 * along with this program. If not, see <http://www.gnu.org/licenses/>.
 */
package org.exoplatform.services.wcm.search.connector;

import java.net.URLDecoder;
import java.util.Collection;
import java.util.Iterator;
import java.util.List;

import javax.jcr.RepositoryException;
import javax.jcr.Session;

import org.apache.commons.lang.StringEscapeUtils;
import org.apache.commons.lang.StringUtils;

import org.exoplatform.commons.api.search.data.SearchContext;
import org.exoplatform.container.xml.InitParams;
import org.exoplatform.portal.config.DataStorage;
import org.exoplatform.portal.config.model.Application;
import org.exoplatform.portal.config.model.Container;
import org.exoplatform.portal.config.model.ModelObject;
import org.exoplatform.portal.mop.SiteKey;
import org.exoplatform.portal.mop.SiteType;
import org.exoplatform.portal.mop.navigation.*;
import org.exoplatform.services.cms.drives.DriveData;
import org.exoplatform.services.cms.impl.Utils;
import org.exoplatform.services.cms.templates.TemplateService;
import org.exoplatform.services.jcr.core.ManageableRepository;
import org.exoplatform.services.log.ExoLogger;
import org.exoplatform.services.log.Log;
import org.exoplatform.services.security.ConversationState;
import org.exoplatform.services.wcm.core.NodetypeConstant;
import org.exoplatform.services.wcm.search.ResultNode;
import org.exoplatform.services.wcm.utils.WCMCoreUtils;

/**
 * Documents are nodes whose nodetype was declared as a Document Type in ECM admin.
 */
public class DocumentSearchServiceConnector extends BaseContentSearchServiceConnector {

  private static final Log LOG = ExoLogger.getLogger(DocumentSearchServiceConnector.class.getName());
  
  public DocumentSearchServiceConnector(InitParams initParams) throws Exception {
    super(initParams);
  }

  /**
   * {@inheritDoc}
   */
  @Override
  protected String[] getSearchedDocTypes() {
    List<String> docTypes = null;
    try {
       docTypes = WCMCoreUtils.getService(TemplateService.class).getDocumentTemplates();
    } catch (Exception e) {
      if (LOG.isErrorEnabled()) {
        LOG.error(e);
      }
    }
    docTypes.remove(NodetypeConstant.NT_FILE);
    return docTypes.toArray(new String[]{});
  }
  
  protected String[] getNodeTypes() {
    return null;
  }
  
  /**
   * {@inheritDoc}
   * @throws RepositoryException 
   */
  @Override
  protected ResultNode filterNode(ResultNode node) throws RepositoryException {
    //do not accept nt:file
    return node.isNodeType(NodetypeConstant.NT_FILE) ? null : node;
  }
  
  /**
   * {@inheritDoc}
   * @throws RepositoryException 
   */
  @Override
  protected String getPath(ResultNode node, SearchContext context) throws Exception {
    String url = BaseSearchServiceConnector.NONE_NAGVIGATION;
    String handler = WCMCoreUtils.getPortalName();
    if(StringUtils.isNotEmpty(context.getSiteName())) {
      String siteType = StringUtils.isEmpty(context.getSiteType()) ? SiteType.PORTAL.toString() : context.getSiteType().toUpperCase();
      SiteKey siteKey = new SiteKey(SiteType.valueOf(siteType), context.getSiteName());

      DriveData driveData = documentService.getDriveOfNode(node.getPath(), ConversationState.getCurrent().getIdentity().getUserId(), Utils.getMemberships());

      if (StringUtils.isNotBlank(siteKey.getName())) {
        String pageName = getPageName(siteKey);
        if (StringUtils.isNotBlank(pageName)) {
          siteKey = SiteKey.portal(context.getSiteName() != null ? context.getSiteName() :
                  BaseSearchServiceConnector.DEFAULT_SITENAME);
          pageName = getPageName(siteKey);
        }
        try {
          url = "/" + handler + context.handler(handler).
                  lang("").
                  siteName(siteKey.getName()).
                  siteType(SiteType.PORTAL.getName()).
                  path(pageName + "?path=" + driveData.getName() + "/" + node.getPath()).renderLink();
        } catch (Exception e) {
          LOG.debug("The current user does not have the needed permission to get the requested document");
          return null;
        }
      }
    }

    return URLDecoder.decode(url, "UTF-8");
  }

  @Override
  protected String getPreviewUrl(ResultNode node, SearchContext context) throws Exception {
    String restContextName =  WCMCoreUtils.getRestContextName();

    Session session = node.getSession();
    String repositoryName = ((ManageableRepository) session.getRepository()).getConfiguration().getName();
    String workspaceName = node.getSession().getWorkspace().getName();

    StringBuffer downloadUrl = new StringBuffer();
    downloadUrl.append('/').append(restContextName).append("/jcr/").
            append(WCMCoreUtils.getRepository().getConfiguration().getName()).append('/').
            append(workspaceName).append(node.getPath());

    // get document author
    String authorUsername = null;
    if(node.hasProperty("exo:owner")) {
      authorUsername = node.getProperty("exo:owner").getString();
    }

    StringBuilder url = new StringBuilder("javascript:require(['SHARED/documentPreview'], function(documentPreview) {documentPreview.init({doc:{");
    if(node.isNodeType(NodetypeConstant.MIX_REFERENCEABLE)) {
      url.append("id:'").append(node.getUUID()).append("',");
    }

    String title = StringEscapeUtils.escapeHtml(getTitleResult(node)).replace("'", "\\'");
    url.append("fileType:'").append(getFileType(node)).append("',");
    url.append("title:'").append(title).append("',");
    url.append("path:'").append(node.getPath())
            .append("', repository:'").append(repositoryName)
            .append("', workspace:'").append(workspaceName)
            .append("', downloadUrl:'").append(downloadUrl.toString())
            .append("', openUrl:'").append(documentService.getLinkInDocumentsApp(node.getPath()))
            .append("'}");
    if(authorUsername != null) {
      url.append(",author:{username:'").append(authorUsername).append("'}");
    }
    //add void(0) to make firefox execute js
    url.append("})});void(0);");

    return url.toString();
  }

  private String getPageName(SiteKey siteKey) throws Exception {
    NavigationService navService = WCMCoreUtils.getService(NavigationService.class);
    NavigationContext nav = navService.loadNavigation(siteKey);
    NodeContext<NodeContext<?>> parentNodeCtx = navService.loadNode(NodeModel.SELF_MODEL, nav, Scope.ALL, null);
    if (parentNodeCtx.getSize() >= 1) {
      Collection<NodeContext<?>> children = parentNodeCtx.getNodes();
      if (siteKey.getType() == SiteType.GROUP) {
        children = parentNodeCtx.get(0).getNodes();
      }
      Iterator<NodeContext<?>> it = children.iterator();
      NodeContext<?> child = null;
      while (it.hasNext()) {
        child = it.next();
        if (hasPortlet(child, BaseSearchServiceConnector.PORTLET_NAME)) {
          return child.getName();
        }
      }
    }
    return "";
  }
  
  private boolean hasPortlet(NodeContext<?> pageCt, String plName) {
    if (plName == null) return false;
    DataStorage ds = WCMCoreUtils.getService(DataStorage.class);
    try {
      for (ModelObject mo : ds.getPage(pageCt.getState().getPageRef().format()).getChildren()) {
        if (containApp(mo, plName)) {
          return true;
        }
      } //of for
    } catch(Exception ex) {
      return false;
    }
    return false;
  }
  
  private boolean containApp(ModelObject mo, String plName) {
    DataStorage ds = WCMCoreUtils.getService(DataStorage.class);
    if (mo instanceof Application<?>) {
      try {
        if (ds.getId(((Application<?>)mo).getState()).contains(plName)) {
          return true;
        }
      } catch (Exception e) {
        return false;
      }
    } else if (mo instanceof Container) {
      for (ModelObject m : ((Container)mo).getChildren()) {
        if (containApp(m, plName)) {
          return true;
        }// of if
      }// of for
    } // of flse
    return false;
  }
  
}
