/*
 * 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.services.jcr.webdav.command;

import org.exoplatform.common.http.HTTPStatus;
import org.exoplatform.services.jcr.access.PermissionType;
import org.exoplatform.services.jcr.core.CredentialsImpl;
import org.exoplatform.services.jcr.core.ExtendedNode;
import org.exoplatform.services.jcr.core.nodetype.ExtendedNodeTypeManager;
import org.exoplatform.services.jcr.core.nodetype.NodeTypeDataManager;
import org.exoplatform.services.jcr.impl.core.version.VersionImpl;
import org.exoplatform.services.jcr.webdav.BaseStandaloneTest;
import org.exoplatform.services.jcr.webdav.WebDavConst;
import org.exoplatform.services.jcr.webdav.WebDavConstants.WebDAVMethods;
import org.exoplatform.services.jcr.webdav.util.TextUtil;
import org.exoplatform.services.jcr.webdav.utils.TestUtils;
import org.exoplatform.services.rest.ExtHttpHeaders;
import org.exoplatform.services.rest.ext.provider.XSLTStreamingOutput;
import org.exoplatform.services.rest.impl.ContainerResponse;
import org.exoplatform.services.rest.impl.MultivaluedMapImpl;
import org.exoplatform.services.security.IdentityConstants;

import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.Reader;
import java.io.StringWriter;
import java.net.URLDecoder;
import java.text.SimpleDateFormat;
import java.util.Calendar;
import java.util.Locale;

import javax.jcr.Node;
import javax.jcr.Session;
import javax.ws.rs.core.HttpHeaders;
import javax.ws.rs.core.MultivaluedMap;

/**
 * Created by The eXo Platform SAS Author : Dmytro Katayev
 * work.visor.ck@gmail.com Aug 13, 2008
 */
public class TestGet extends BaseStandaloneTest
{

   private String path = TestUtils.getFileName();

   private String fileContent = TestUtils.getFileContent();

   @Override
   public void setUp() throws Exception
   {
      super.setUp();
      session.getRootNode().addNode(TextUtil.relativizePath(path));
      InputStream inputStream = new ByteArrayInputStream(fileContent.getBytes());
      TestUtils.addContent(session, path, inputStream, defaultFileNodeType, "");
      path = path + "[2]";
   }

   public void testSimpleGet() throws Exception
   {
      testSimpleGet(getPathWS());
   }

   public void testSimpleGetWithFakePathWS() throws Exception
   {
      testSimpleGet(getFakePathWS());
   }

   private void testSimpleGet(String pathWs) throws Exception
   {
      ContainerResponse response = serviceWithEscape(WebDAVMethods.GET, pathWs + path, "", null, null);
      assertEquals(HTTPStatus.OK, response.getStatus());
      FileInputStream content = (FileInputStream)response.getEntity();
      Reader r = new InputStreamReader(content);
      StringWriter sw = new StringWriter();
      char[] buffer = new char[1024];
      for (int n; (n = r.read(buffer)) != -1;)
      {
         sw.write(buffer, 0, n);
      }
      String str = sw.toString();
      assertEquals(fileContent, str);
   }

   public void testNotFoundGet() throws Exception
   {
      ContainerResponse response = serviceWithEscape(WebDAVMethods.GET, getPathWS() + "/not-found" + path, "", null, null);
      assertEquals(HTTPStatus.NOT_FOUND, response.getStatus());
   }

   /**
    * Details can be found here: https://jira.jboss.org/browse/EXOJCR-956
    * @throws Exception
    */
   public void testMissingJcrLastModifiedProperty() throws Exception
   {
      File file = new File("src/test/resources/rh_nodetype.xml");
      assertTrue("src/test/resources/rh_nodetype.xml not found", file.exists());
      FileInputStream fis = new FileInputStream(file);

      session.getWorkspace().getNamespaceRegistry().registerNamespace("rh", "www.vn.vnn");
      session.getWorkspace().getNodeTypesHolder().registerNodeTypes(fis, ExtendedNodeTypeManager.IGNORE_IF_EXISTS,
         NodeTypeDataManager.TEXT_XML);

      Node podcast = session.getRootNode().addNode("podcast", "rh:podcast");

      Node nodeToAdd = podcast.addNode("rh:podcastFile", "nt:file");
      Node contentNodeOfNodeToAdd = nodeToAdd.addNode("jcr:content", "nt:resource");
      contentNodeOfNodeToAdd.setProperty("jcr:data", new FileInputStream("src/test/resources/test.txt"));
      contentNodeOfNodeToAdd.setProperty("jcr:mimeType", "text/plain");
      contentNodeOfNodeToAdd.setProperty("jcr:lastModified", Calendar.getInstance());
      session.save();

      podcast.addMixin("mix:versionable");
      session.save();

      VersionImpl v = (VersionImpl)podcast.checkin();
      session.save();

      podcast.checkout();
      session.save();

      String path =
         getPathWS() + "/jcr:system/jcr:versionStorage/" + v.getContainingHistory().getIdentifier()
            + "/1/jcr:frozenNode/rh:podcastFile";

      ContainerResponse response = serviceWithEscape(WebDAVMethods.GET, path, "", null, null);
      assertEquals("Successful result expected (200), but actual is: " + response.getStatus(), 200, response
         .getStatus());

   }

   /**
    * Tests if date passed through header If-modified-since of GET method parsed correctly.
    * Details can be found here: http://jira.exoplatform.org/browse/JCR-1470
    * @throws Exception
    */
   public void testIfModifiedSinceDateParsing() throws Exception
   {
      session.getRootNode().addNode("node");
      Node fileNode = session.getRootNode().addNode("node", "nt:file");
      fileNode.addMixin("mix:versionable");

      Node contentNode = fileNode.addNode("jcr:content", "nt:resource");
      contentNode.setProperty("jcr:mimeType", "text/plain");

      Calendar creationDate = Calendar.getInstance();
      Calendar IfModifiedSince = Calendar.getInstance();
      IfModifiedSince.add(Calendar.HOUR, -2);
      creationDate.add(Calendar.HOUR, -1);
      contentNode.setProperty("jcr:data", creationDate);
      contentNode.setProperty("jcr:lastModified", creationDate);

      session.save();

      fileNode.checkin();
      fileNode.checkout();

      String path =
         getPathWS() + "/" + fileNode.getName() + "[2]?time=" + IfModifiedSince.getTimeInMillis() + "&version=1";

      SimpleDateFormat dateFormat = new SimpleDateFormat(WebDavConst.DateFormat.IF_MODIFIED_SINCE_PATTERN, Locale.US);
      String ifModifiedSinceDate = dateFormat.format(IfModifiedSince.getTime());

      MultivaluedMap<String, String> headers = new MultivaluedMapImpl();
      headers.add(ExtHttpHeaders.IF_MODIFIED_SINCE, ifModifiedSinceDate);
      ContainerResponse response = serviceWithEscape(WebDAVMethods.GET, path, "", headers, null);
      assertEquals(HTTPStatus.OK, response.getStatus());

      headers.clear();

      IfModifiedSince.add(Calendar.HOUR, +4);
      ifModifiedSinceDate = dateFormat.format(IfModifiedSince.getTime());
      headers.add(ExtHttpHeaders.IF_MODIFIED_SINCE, ifModifiedSinceDate);
      response = serviceWithEscape(WebDAVMethods.GET, path, "", headers, null);
      assertEquals(HTTPStatus.NOT_MODIFIED, response.getStatus());
   }

   public void testXSLTParamsPassing() throws Exception
   {
      String strToTest = "/absolute/path/to/file";
      String folderName = TestUtils.getFolderName();
      TestUtils.addFolder(session, folderName, defaultFolderNodeType, "");
      ContainerResponse response = serviceWithEscape(WebDAVMethods.GET, getPathWS() + folderName, "", null, null);

      assertEquals(HTTPStatus.OK, response.getStatus());

      XSLTStreamingOutput XSLTout = (XSLTStreamingOutput)response.getEntity();
      ByteArrayOutputStream byteOut = new ByteArrayOutputStream();
      XSLTout.write(byteOut);

      assertTrue(byteOut.toString().contains(strToTest));

   }

   /**
    * We test for parent href to be contained in response.
    * We GET a collection and receive an html. Html should contain
    * a href to parent collection of requested collection. 
    * @throws Exception
    */
   public void testParentHrefForGetColRequest() throws Exception
   {
      String folderOne = TestUtils.getFolderName();
      String folderTwo = folderOne + TestUtils.getFolderName();

      // add collections
      TestUtils.addFolder(session, folderOne, defaultFolderNodeType, "");
      TestUtils.addFolder(session, folderTwo, defaultFolderNodeType, "");

      // get a sub-collection
      ContainerResponse response = serviceWithEscape(WebDAVMethods.GET, getPathWS() + folderTwo, "", null, null);
      assertEquals(HTTPStatus.OK, response.getStatus());

      // serialize response entity to string
      XSLTStreamingOutput XSLTout = (XSLTStreamingOutput)response.getEntity();
      ByteArrayOutputStream byteOut = new ByteArrayOutputStream();
      XSLTout.write(byteOut);

      assertTrue("Response should contain parent collection href", byteOut.toString().contains(folderOne));
   }

   /**
    * We test for parent href to be contained in response. We GET a collection,
    * which has non latin letters in its name, and receive an html like response. 
    * Response should contain href to parent collection of requested collection.
    * Details can be found here: https://issues.jboss.org/browse/EXOJCR-1379
    * @throws Exception
    */
   public void testParentHrefForGetColWithNonLatinNameRequest() throws Exception
   {
      // "%40" corresponds to '@' symbol
      String folderOne = TestUtils.getFolderName() + "%40";
      String folderTwo = folderOne + TestUtils.getFolderName() + "%40";

      ContainerResponse response;

      //add collections
      TestUtils.addFolder(session, URLDecoder.decode(folderOne, "UTF-8"), defaultFolderNodeType, "");
      TestUtils.addFolder(session, URLDecoder.decode(folderTwo, "UTF-8"), defaultFolderNodeType, "");

      //get a sub-collection
      response = service(WebDAVMethods.GET, getPathWS() + folderTwo, "", null, null);
      assertEquals(HTTPStatus.OK, response.getStatus());

      // serialize response entity to string
      XSLTStreamingOutput XSLTout = (XSLTStreamingOutput)response.getEntity();
      ByteArrayOutputStream byteOut = new ByteArrayOutputStream();
      XSLTout.write(byteOut);

      assertTrue("Response should contain parent collection href", byteOut.toString().contains(folderOne));
   }

   @Override
   protected String getRepositoryName()
   {
      return null;
   }
   
   
   /**
    *  Unit test for https://jira.exoplatform.org/browse/JCR-1832 
    */
   public void testGetFileFromFolder() throws Exception
   {
      ExtendedNode folderA = (ExtendedNode)session.getRootNode().addNode("folderA", "nt:folder");
      folderA.addMixin("exo:privilegeable");
      folderA.setPermission("john", PermissionType.ALL);
      folderA.removePermission(IdentityConstants.ANY);
      session.save();
      
      assertEquals(4, folderA.getACL().getPermissionEntries().size());
      assertEquals("john read", folderA.getACL().getPermissionEntries().get(0).getAsString());
      assertEquals("john add_node", folderA.getACL().getPermissionEntries().get(1).getAsString());
      assertEquals("john set_property", folderA.getACL().getPermissionEntries().get(2).getAsString());
      assertEquals("john remove", folderA.getACL().getPermissionEntries().get(3).getAsString());
      
      
      Session sessJohn = repository.login(new CredentialsImpl("john", "exo".toCharArray()), "ws");
      
      ExtendedNode folderB = (ExtendedNode)sessJohn.getRootNode().getNode("folderA").addNode("folderB", "nt:folder");
      
      folderB.addMixin("exo:privilegeable");
      folderB.setPermission("any", new String[]{"read"});
      sessJohn.save();
      
      assertEquals(5, folderB.getACL().getPermissionEntries().size());
      assertEquals("john read", folderB.getACL().getPermissionEntries().get(0).getAsString());
      assertEquals("john add_node", folderB.getACL().getPermissionEntries().get(1).getAsString());
      assertEquals("john set_property", folderB.getACL().getPermissionEntries().get(2).getAsString());
      assertEquals("john remove", folderB.getACL().getPermissionEntries().get(3).getAsString());
      assertEquals("any read", folderB.getACL().getPermissionEntries().get(4).getAsString());
      
      // Login as anonim. 
      // Tthis session use on server side, thank SessionProvider.
      repository.login(new CredentialsImpl(IdentityConstants.ANONIM, "exo".toCharArray()), "ws");

      String path = getPathWS() + "/folderA/folderB";

      ContainerResponse response = serviceWithEscape(WebDAVMethods.GET, path, "", null, null);
      assertEquals("Successful result expected (200), but actual is: " + response.getStatus(), 200, response.getStatus());
      
      XSLTStreamingOutput XSLTout = (XSLTStreamingOutput)response.getEntity();
      ByteArrayOutputStream byteOut = new ByteArrayOutputStream();
      XSLTout.write(byteOut);

      assertTrue("Response should contain parent collection href", byteOut.toString().contains("folderA"));

   }
   
   public void testGetContentTypeWithEncoding() throws Exception
   {
      Node fileNode = session.getRootNode().addNode("node", "nt:file");
      Node contentNode = fileNode.addNode("jcr:content", "nt:resource");
      contentNode.setProperty("jcr:mimeType", "text/plain");
      contentNode.setProperty("jcr:encoding", "test-encoding");
      contentNode.setProperty("jcr:data", "There is no ignorance, there is knowledge.");
      contentNode.setProperty("jcr:lastModified", Calendar.getInstance());
      session.save();

      ContainerResponse response = service(WebDAVMethods.GET, getPathWS() + "/" + fileNode.getName(), "", null, null);

      assertEquals("text/plain; charset=test-encoding", response.getHttpHeaders().getFirst(HttpHeaders.CONTENT_TYPE)
         .toString());
   }

   public void testGetContentTypeWithNoEncoding() throws Exception
   {
      Node fileNode = session.getRootNode().addNode("node", "nt:file");
      Node contentNode = fileNode.addNode("jcr:content", "nt:resource");
      contentNode.setProperty("jcr:mimeType", "text/plain");
      contentNode.setProperty("jcr:encoding", "");
      contentNode.setProperty("jcr:data", "There is no passion, there is serenity.");
      contentNode.setProperty("jcr:lastModified", Calendar.getInstance());
      session.save();

      ContainerResponse response = service(WebDAVMethods.GET, getPathWS() + "/" + fileNode.getName(), "", null, null);

      assertEquals("text/plain", response.getHttpHeaders().getFirst(HttpHeaders.CONTENT_TYPE)
         .toString());

      fileNode = session.getRootNode().addNode("node2", "nt:file");
      contentNode = fileNode.addNode("jcr:content", "nt:resource");
      contentNode.setProperty("jcr:mimeType", "text/plain");
      contentNode.setProperty("jcr:data", "There is no passion, there is serenity.");
      contentNode.setProperty("jcr:lastModified", Calendar.getInstance());
      session.save();

      response = service(WebDAVMethods.GET, getPathWS() + "/" + fileNode.getName(), "", null, null);

      assertEquals("text/plain", response.getHttpHeaders().getFirst(HttpHeaders.CONTENT_TYPE).toString());
   }

   public void testETag() throws Exception
   {
      Node fileNodeI = session.getRootNode().addNode("node1", "nt:file");
      Node contentNode = fileNodeI.addNode("jcr:content", "nt:resource");
      contentNode.setProperty("jcr:mimeType", "text/plain");
      contentNode.setProperty("jcr:encoding", "");
      contentNode.setProperty("jcr:data", "test1");
      contentNode.setProperty("jcr:lastModified", Calendar.getInstance());
      session.save();

      Node fileNodeII = session.getRootNode().addNode("node2", "nt:file");
      contentNode = fileNodeII.addNode("jcr:content", "nt:resource");
      contentNode.setProperty("jcr:mimeType", "text/plain");
      contentNode.setProperty("jcr:encoding", "");
      contentNode.setProperty("jcr:data", "test2");
      contentNode.setProperty("jcr:lastModified", Calendar.getInstance());
      session.save();


      ContainerResponse response = service(WebDAVMethods.GET, getPathWS() + "/node1", "", null, null);
      assertEquals(HTTPStatus.OK, response.getStatus());

      String eTag = (String)response.getHttpHeaders().getFirst(HttpHeaders.ETAG);
      assertNotNull(eTag);

      MultivaluedMap<String, String> headers = new MultivaluedMapImpl();
      headers.add(ExtHttpHeaders.IF_NONE_MATCH, eTag);

      response = service(WebDAVMethods.GET, getPathWS() + "/node1", "", headers, null);

      assertEquals(HTTPStatus.NOT_MODIFIED, response.getStatus());

      String destAbsPath = fileNodeI.getPath();
      fileNodeI.remove();
      session.save();

      session.move(fileNodeII.getPath(), destAbsPath);
      session.save();

      response = service(WebDAVMethods.GET, getPathWS() + "/node1", "", headers, null);

      assertEquals(HTTPStatus.OK, response.getStatus());
      eTag = (String)response.getHttpHeaders().getFirst(HttpHeaders.ETAG);
      headers.clear();
      headers.add(ExtHttpHeaders.IF_NONE_MATCH, eTag);

      session.move(destAbsPath, destAbsPath + "_");
      session.save();

      response = service(WebDAVMethods.GET, getPathWS() + "/node1_", "", headers, null);
      assertEquals(HTTPStatus.NOT_MODIFIED, response.getStatus());
   }
}
