/***************************************************************************
 * Copyright (C) 2003-2009 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.connector.collaboration;

import java.text.DateFormat;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Calendar;
import java.util.Collections;
import java.util.Date;
import java.util.List;

import javax.jcr.Node;
import javax.jcr.NodeIterator;
import javax.jcr.RepositoryException;
import javax.jcr.Session;
import javax.jcr.query.Query;
import javax.jcr.query.QueryManager;
import javax.jcr.query.QueryResult;
import javax.ws.rs.GET;
import javax.ws.rs.Path;
import javax.ws.rs.PathParam;
import javax.ws.rs.QueryParam;
import javax.ws.rs.core.MediaType;
import javax.ws.rs.core.Response;

import org.apache.commons.lang3.StringUtils;
import org.exoplatform.ecm.utils.comparator.PropertyValueComparator;
import org.exoplatform.services.cms.drives.DriveData;
import org.exoplatform.services.cms.drives.ManageDriveService;
import org.exoplatform.services.cms.folksonomy.NewFolksonomyService;
import org.exoplatform.services.cms.templates.TemplateService;
import org.exoplatform.services.jcr.RepositoryService;
import org.exoplatform.services.jcr.core.ManageableRepository;
import org.exoplatform.services.jcr.ext.common.SessionProvider;
import org.exoplatform.services.jcr.impl.core.query.QueryImpl;
import org.exoplatform.services.log.ExoLogger;
import org.exoplatform.services.log.Log;
import org.exoplatform.services.rest.resource.ResourceContainer;
import org.exoplatform.services.wcm.utils.WCMCoreUtils;

/**
 * Returns the latest edited documents.
 *
 * @LevelAPI Provisional
 *
 * @anchor GetEditedDocumentRESTService
 */

@Path("/presentation/document/edit/")
public class GetEditedDocumentRESTService implements ResourceContainer {

  /** The Constant LAST_MODIFIED_PROPERTY. */
  private static final String LAST_MODIFIED_PROPERTY = "Last-Modified";

  /** The Constant IF_MODIFIED_SINCE_DATE_FORMAT. */
  private static final String IF_MODIFIED_SINCE_DATE_FORMAT = "EEE, dd MMM yyyy HH:mm:ss z";

  private RepositoryService   repositoryService;

  private TemplateService     templateService;

  private NewFolksonomyService   newFolksonomyService;

  private ManageDriveService   manageDriveService;

  private static final String DATE_MODIFIED   = "exo:dateModified";

  private static final String JCR_PRIMARYTYPE = "jcr:primaryType";

  private static final String NT_BASE         = "nt:base";

  private static final String EXO_OWNER       = "exo:owner";

  private static final int    NO_PER_PAGE     = 5;

  private static final String QUERY_STATEMENT = "SELECT * FROM $0 WHERE $1 ORDER BY $2 DESC";

  private static final String GADGET          = "gadgets";

  private boolean             show_gadget     = false;

  private static final Log LOG = ExoLogger.getLogger(GetEditedDocumentRESTService.class.getName());


  public GetEditedDocumentRESTService(RepositoryService repositoryService,
      TemplateService templateService, NewFolksonomyService newFolksonomyService, ManageDriveService manageDriveService) {
    this.repositoryService = repositoryService;
    this.templateService = templateService;
    this.newFolksonomyService = newFolksonomyService;
    this.manageDriveService = manageDriveService;
  }

  /**
   * Returns the latest edited documents.
   * 
   * @param repository The repository name.
   * @param showItems Returns the number of items in each page.
   * @param showGadgetWs Shows the gadget workspace or not.
   * @return Response inputstream.
   * @throws Exception
   * 
   * @anchor GetEditedDocumentRESTService.getLastEditedDoc
   */
  @Path("/{repository}/")
  @GET
  public Response getLastEditedDoc(@PathParam("repository") String repository,
      @QueryParam("showItems") String showItems, @QueryParam("showGadgetWs") String showGadgetWs) throws Exception {
    List<Node> lstLastEditedNode = getLastEditedNode(showItems, showGadgetWs);
    List<DocumentNode> lstDocNode = getDocumentData(lstLastEditedNode);
    ListEditDocumentNode listEditDocumentNode = new ListEditDocumentNode();
    listEditDocumentNode.setLstDocNode(lstDocNode);

    DateFormat dateFormat = new SimpleDateFormat(IF_MODIFIED_SINCE_DATE_FORMAT);
    return Response.ok(listEditDocumentNode, new MediaType("application", "json"))
                   .header(LAST_MODIFIED_PROPERTY, dateFormat.format(new Date()))
                   .build();
  }

  private List<Node> getLastEditedNode(String noOfItem, String showGadgetWs) throws Exception{
    if (showGadgetWs != null && showGadgetWs.length() > 0) {
      show_gadget = Boolean.parseBoolean(showGadgetWs);
    }
    ArrayList<Node>  lstNode = new ArrayList<Node>();
    StringBuffer bf = new StringBuffer(1024);
    List<String> lstNodeType = templateService.getDocumentTemplates();
    if (lstNodeType != null) {
      for (String nodeType : lstNodeType) {
        bf.append("(").append(JCR_PRIMARYTYPE).append("=").append("'").append(nodeType).append("'")
            .append(")").append(" OR ");
      }
    }

    if (bf.length() == 1) return null;
    bf.delete(bf.lastIndexOf("OR") - 1, bf.length());
    if (noOfItem == null || noOfItem.trim().length() == 0) noOfItem = String.valueOf(NO_PER_PAGE);
    String queryStatement = StringUtils.replace(QUERY_STATEMENT, "$0", NT_BASE);
    queryStatement = StringUtils.replace(queryStatement, "$1", bf.toString());
    queryStatement = StringUtils.replace(queryStatement, "$2", DATE_MODIFIED);
    ManageableRepository manageableRepository = repositoryService.getCurrentRepository();
    try {
      String[] workspaces = manageableRepository.getWorkspaceNames();
      List<String> lstWorkspace = new ArrayList<String>();
      //Arrays.asList() return fixed size list;
      lstWorkspace.addAll(Arrays.asList(workspaces));
      if (!show_gadget && lstWorkspace.contains(GADGET)) {
        lstWorkspace.remove(GADGET);
      }
      SessionProvider provider = WCMCoreUtils.createAnonimProvider();
      QueryImpl query = null;
      Session session = null;
      QueryResult queryResult = null;
      QueryManager queryManager = null;
      for (String workspace : lstWorkspace) {
        session = provider.getSession(workspace, manageableRepository);
        queryManager = session.getWorkspace().getQueryManager();
        query = (QueryImpl) queryManager.createQuery(queryStatement, Query.SQL);
        query.setLimit(Integer.parseInt(noOfItem));
        query.setOffset(0);
        queryResult = query.execute();
        puttoList(lstNode, queryResult.getNodes());
        session.logout();
      }
    } catch (RepositoryException e) {
      if (LOG.isErrorEnabled()) {
        LOG.error("Exception when execute SQL " + queryStatement, e);
      }
    }
    return lstNode;
  }

  private void puttoList(List<Node> lstNode, NodeIterator nodeIter) {
    if (nodeIter != null) {
      while (nodeIter.hasNext()) {
        lstNode.add(nodeIter.nextNode());
      }
    }
  }

  private List<DocumentNode> getDocumentData(List<Node> lstNode) throws Exception {
    return getDocumentData(lstNode, String.valueOf(NO_PER_PAGE));
  }

  private String getDateFormat(Calendar date) {
    return String.valueOf(date.getTimeInMillis());
  }

  private List<DocumentNode> getDocumentData(List<Node> lstNode, String noOfItem) throws Exception {
    if (lstNode == null || lstNode.size() == 0) return null;
    List<DocumentNode> lstDocNode = new ArrayList<DocumentNode>();
    DocumentNode docNode = null;
    StringBuilder tags = null;

    Collections.sort(lstNode, new PropertyValueComparator(DATE_MODIFIED, PropertyValueComparator.DESCENDING_ORDER));
    ManageableRepository manageableRepository = repositoryService.getCurrentRepository();
    List<DriveData> lstDrive = manageDriveService.getAllDrives();
    for (Node node : lstNode) {
      docNode = new DocumentNode();
      docNode.setName(node.getName());
      docNode.setPath(node.getPath());
      docNode.setLastAuthor(node.getProperty(EXO_OWNER).getString());
      docNode.setLstAuthor(node.getProperty(EXO_OWNER).getString());
      docNode.setDateEdited(getDateFormat(node.getProperty(DATE_MODIFIED).getDate()));
      tags = new StringBuilder(1024);

      List<Node> tagList = newFolksonomyService.getLinkedTagsOfDocumentByScope(NewFolksonomyService.PUBLIC,
                                                                               "",
                                                                               node,
                                                                               manageableRepository.getConfiguration()
                                                                                                   .getDefaultWorkspaceName());
      for(Node tag : tagList) {
        tags.append(tag.getName()).append(", ");
      }

      if (tags.lastIndexOf(",") > 0) {
        tags.delete(tags.lastIndexOf(","), tags.length());
      }

      docNode.setTags(tags.toString());
      docNode.setDriveName(getDriveName(lstDrive, node));
      if (lstDocNode.size() < Integer.parseInt(noOfItem))  lstDocNode.add(docNode);
    }
    return lstDocNode;
  }

  private String getDriveName(List<DriveData> lstDrive, Node node) throws RepositoryException{
    String driveName = "";
    for (DriveData drive : lstDrive) {
      if (node.getSession().getWorkspace().getName().equals(drive.getWorkspace())
          && node.getPath().contains(drive.getHomePath()) && drive.getHomePath().equals("/")) {
        driveName = drive.getName();
        break;
      }
    }
    return driveName;
  }

  public class DocumentNode {

    private String nodeName_;

    private String nodePath_;

    private String driveName_;

    private String   dateEdited_;

    private String tags;

    private String   lastAuthor;

    private String lstAuthor;

    public String getTags() {
      return tags;
    }

    public void setTags(String tags) {
      this.tags = tags;
    }

    public String getLastAuthor() {
      return lastAuthor;
    }

    public void setLastAuthor(String lastAuthor) {
      this.lastAuthor = lastAuthor;
    }

    public String getLstAuthor() {
      return lstAuthor;
    }

    public void setLstAuthor(String lstAuthor) {
      this.lstAuthor = lstAuthor;
    }

    public void setName(String nodeName) {
      nodeName_ = nodeName;
    }

    public String getName() {
      return nodeName_;
    }

    public void setPath(String nodePath) {
      nodePath_ = nodePath;
    }

    public String getPath() {
      return nodePath_;
    }

    public void setDriveName(String driveName) {
      driveName_ = driveName;
    }

    public String getDriveName() {
      return driveName_;
    }

    public String getDateEdited() {
      return dateEdited_;
    }

    public void setDateEdited(String dateEdited_) {
      this.dateEdited_ = dateEdited_;
    }
  }

  public class ListEditDocumentNode {

    private List<DocumentNode> lstDocNode;

    public List<DocumentNode> getLstDocNode() {
      return lstDocNode;
    }

    public void setLstDocNode(List<DocumentNode> lstDocNode) {
      this.lstDocNode = lstDocNode;
    }
  }

}
