/* ======================================================================
   Parts Copyright 2006 University of Leeds, Oxford University, University of the Highlands and Islands.

   Licensed under the Apache License, Version 2.0 (the "License");
   you may not use this file except in compliance with the License.
   You may obtain a copy of the License at

       http://www.apache.org/licenses/LICENSE-2.0

   Unless required by applicable law or agreed to in writing, software
   distributed under the License is distributed on an "AS IS" BASIS,
   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
   See the License for the specific language governing permissions and
   limitations under the License.

====================================================================== */

package org.bodington.server;

import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.OutputStream;
import java.io.PrintWriter;
import java.net.URL;
import java.util.zip.ZipFile;

import org.bodington.database.PrimaryKey;
import org.bodington.server.realm.AclEntry;
import org.bodington.server.realm.Group;
import org.bodington.server.realm.Permission;
import org.bodington.server.realm.User;
import org.bodington.server.resources.UploadedFileSummary;
import org.bodington.util.FileMover;

public class UploadedFileSessionTest extends BuildingServerTest
{
    public static File createTempDir(String prefix, String suffix)
        throws IOException
    {
        File tempDir = File.createTempFile("test",null);
        tempDir.delete();
        tempDir.mkdir();
        return tempDir;
    }
    
    public static void cleanTempDir(File file)
        throws IOException
    {
        if (file.isDirectory())
        {
            File files[] = file.listFiles();
            for (int count = 0; count < files.length; count++)
                cleanTempDir(files[count]);
        }
        file.delete();
    }

    UploadedFileSession session;
    File publishDir;
    File generatedDir;
    protected User otherUser;
    
    public void setUp() throws Exception
    {
        super.setUp();
        
        publishDir = createTempDir("test", null);
        
        BuildingContext.getContext().getProperties().setProperty(
            "webpublish.filestore", publishDir.getAbsolutePath());
        
        generatedDir = createTempDir("test", null);
        
        BuildingContext.getContext().getProperties().setProperty(
            "filegeneration.filestore", generatedDir.getAbsolutePath());
                
        // Need to set to user so permission checking works.
        BuildingContext.getContext().setUser(user);
        
        Group group = new Group();
        group.setDescription("Description");
        group.setName("group");
        group.setResourceId(resource.getPrimaryKey());
        group.save();
        group.addMember(user);
        group.save();
        
        AclEntry entry = new AclEntry();
        entry.setGroup(group);
        entry.addPermission(Permission.SYSADMIN);
        entry.addPermission(Permission.UPLOAD);
        entry.setAcl(acl);
        entry.save();
        
        acl.addEntry(entry);
        acl.save();
        
        
        otherUser = new User();
        otherUser.setZone(zone);
        otherUser.setName("Test");
        otherUser.setSurname("User");
        otherUser.setInitials("B");
        otherUser.save();
        
        Group otherGroup = new Group();
        otherGroup.setDescription("Description");
        otherGroup.setName("group");
        otherGroup.setResourceId(resource.getPrimaryKey());
        otherGroup.save();
        otherGroup.addMember(otherUser);
        otherGroup.save();
        
        AclEntry otherEntry = new AclEntry();
        otherEntry.setGroup(otherGroup);
        otherEntry.addPermission(Permission.SEE);
        otherEntry.addPermission(Permission.VIEW);
        otherEntry.addPermission(Permission.MANAGE);
        otherEntry.addPermission(Permission.UPLOAD);
        otherEntry.setAcl(acl);
        otherEntry.save();
        
        acl.addEntry(otherEntry);
        acl.save();
        
        BuildingSession buildingSession = new BuildingSessionImpl();
        buildingSession.init(user.getPrimaryKey(), resource.getPrimaryKey());
        session = new UploadedFileSessionImpl(buildingSession);
        
    }
    
    public void tearDown() throws Exception
    {
        super.tearDown();
        cleanTempDir(publishDir);
    }
    
    /*
     * Test method for 'org.bodington.server.UploadedFileSession.copyFile(String, String, String)'
     */
    public void testCopyFile()
    {

    }

    /*
     * Test method for 'org.bodington.server.UploadedFileSession.transferFile(String, String, String)'
     */
    public void testTransferFile() throws Exception
    {
        // Find the test file
        URL url = getClass().getResource("text.txt");
        assertNotNull(url);
 
        // Must copy the file into a temp location as transfer file deletes
        // the orginal.
        File source = new File(url.getFile());
        File temp = copyToTemp(source);
        UploadedFileSummary summary = session.transferFile(temp
            .getAbsolutePath(), "/file.txt", "text/plain");
        assertEquals(session.getFileSummary("/file.txt"), summary);
        
        FileMover.copyFile(source, temp);
        session.transferFile(temp.getAbsolutePath(), "/folder/file.txt", "text/plain");
        assertNotNull(session.getFileSummary("/folder/file.txt"));
        
        FileMover.copyFile(source, temp);
        session.transferFile(temp.getAbsolutePath(), "/1/2/3/4/5/file.txt", "text/plain");
        assertNotNull(session.getFileSummary("/1/2/3/4/5/file.txt"));
        assertNotNull(session.getFileSummary("/1/2/3/"));
        assertNull(session.getFileSummary("/1/2/3/4/5/6/"));
        
        FileMover.copyFile(source, temp);
        try
        {
            session.transferFile(temp.getAbsolutePath(), "/file.txt/file.txt", "text/plain");
            fail("Exception Expected: Can't create a file as a child of a file");
        }
        catch (BuildingServerException bse)
        {}
    }

    private File copyToTemp(File source) throws IOException
    {
        File temp = File.createTempFile("test", null);
        temp.delete();

        FileMover.copyFile(source, temp);
        return temp;
    }

    /*
     * Test method for 'org.bodington.server.UploadedFileSession.transferZipFile(ZipFile, String)'
     */
    public void testTransferZipFile() throws Exception
    {
        // test.zip contains file1.txt and file2.txt
        URL url = getClass().getResource("test.zip");
        assertNotNull(url);
        
        // Must copy the file into a temp location as transfer file deletes
        // the orginal.
        File source = new File(url.getFile());
        File temp = File.createTempFile("zip", null);
        temp.delete();

        ZipFile zip;
        
        // Unpacking at the root.
        FileMover.copyFile(source, temp);
        zip = new ZipFile(temp);
        session.transferZipFile(zip, "/");
        assertNotNull(session.getFileSummary("/file1.txt"));

        // Unpacking ontop.
        FileMover.copyFile(source, temp);
        zip = new ZipFile(temp);
        session.transferZipFile(zip, "/");
        assertNotNull(session.getFileSummary("/file1.txt"));
        
        // Now try with a file containsing directories.
        // test.zip contains file1.txt and file2.txt
        url = getClass().getResource("testdirs.zip");
        assertNotNull(url);
        source = new File(url.getFile());
        
        FileMover.copyFile(source, temp);
        zip = new ZipFile(temp);
        session.transferZipFile(zip, "/");
        assertNotNull(session.getFileSummary("/dir1/file.txt"));
        assertNotNull(session.getFileSummary("/dir2/file.txt"));
        
        
    }

    /*
     * Test method for 'org.bodington.server.UploadedFileSession.createFolder(String)'
     */
    public void testCreateFolder() throws Exception
    {
        // Try creating the root folder.
        UploadedFileSummary root = session.createFolder("/");
        assertNotNull(root);
        assertEquals(session.getFileSummary((String)null), root);
        
        // Try creating root folder again.
        root = session.createFolder("/");
        assertEquals(session.getFileSummary((String)null), root);
        
        // Check that null is the same as "/"
        assertEquals(root, session.createFolder(null));
        
        // Try to create a child folder.
        UploadedFileSummary child = session.createFolder("/child");
        assertNotNull(child);
        assertEquals(session.getFileSummary("/child"), child);
        
        // Delete the child folder and then see if it reapears.
        session.deleteFile("/child");
        assertEquals(child, session.createFolder("/child"));
        assertEquals(child, session.getFileSummary("/child"));
        
        // Check that we can't replace a file with a folder
        assertNotNull(session.copyFile(getClass().getResource("text.txt")
            .getFile(), "/file", "text/plain"));
        
        // Check that we don't need to leading slash
        assertNotNull(session.createFolder("noslash"));
        assertNotNull(session.getFileSummary("/noslash"));
        
        assertNotNull(session.createFolder("/extraslash/"));
        assertNotNull(session.getFileSummary("/extraslash"));
        
        try
        {
            session.createFolder("/file");
            fail("Expected Exception: A file exists here");
        }
        catch (BuildingServerException bse)
        {}
        
        // Check we can't create folders inside files.
        try
        {
            session.createFolder("/file/folder");
            fail("Expected Exception: You can't create folders in files.");
        }
        catch (BuildingServerException bse)
        {}
        
        // Check we can't create folders inside deleted folders.
        session.deleteFile("/child");
        try
        {
            session.createFolder("/child/folder");
            fail("Expected Exception: You can't create folders in deleted folders.");
        }
        catch (BuildingServerException bse)
        {}

    }
    
    public void testCreateFolderWithoutRoot() throws Exception
    {
        // Check that the root folder is automatically created.
        session.createFolder("/folder");
        assertTrue(session.getFileSummary("/").isFolder());
        assertTrue(session.getFileSummary("/folder").isFolder());
    }
    
    public void testCreateFolderWithoutRootNested() throws Exception
    {
        // Make sure that code for automatically creating the root folder doesn't 
        // create nested folders as well.
        try
        {
            session.createFolder("/nested/folder/creation");
            fail("Expected Exception: Nested folders shouldn't be created automatically.");
        }
        catch (Exception e)
        {}
    }

    /*
     * Test method for 'org.bodington.server.UploadedFileSession.createFile(String, long, String)'
     */
    public void testCreateFile()
    {
        // Current unimplemented
        try
        {
            session.createFile("/file", 100, "text/plain");
            fail("Need to create a test.");
        }
        catch (Exception e)
        {}
    }

    /*
     * Test method for 'org.bodington.server.UploadedFileSession.checkCreateFolder(String)'
     */
    public void testCheckCreateFolder()
    {
        // Current unimplemented
        try
        {
            session.checkCreateFolder("/folder");
            fail("Need to create a test.");
        }
        catch (Exception e)
        {}
    }

    /*
     * Test method for 'org.bodington.server.UploadedFileSession.checkCreateFile(String, long)'
     */
    public void testCheckCreateFile()
    {
        // Current unimplemented
        try
        {
            session.checkCreateFile("/file", 100);
            fail("Need to create a test.");
        }
        catch (Exception e)
        {}
    }

    /*
     * Test method for 'org.bodington.server.UploadedFileSession.deleteFile(String)'
     */
    public void testDeleteFile() throws Exception
    {
        // Create the root.
        assertNotNull(session.createFolder("/"));
        
        // Try to delete a folder.
        assertNotNull(session.createFolder("/folder"));
        session.deleteFile("/folder");
        
        // Try to delete an already deleted folder.
        session.deleteFile("/folder");
        
        // Try to delete an file.
        assertNotNull(session.copyFile(getClass().getResource("text.txt")
            .getFile(), "/file", "text/plain"));
        session.deleteFile("/file");
        
        // Try to delete a file again.
        session.deleteFile("/file");
        
        // Try deleting a folder containing a file.
        assertNotNull(session.createFolder("/folder"));
        assertNotNull(session.copyFile(getClass().getResource("text.txt")
            .getFile(), "/folder/file", "text/plain"));
        session.deleteFile("/folder");
        
        // Try deleting the root folder.
        session.deleteFile("/");
        
        // Delete a file that doesn't exist.
        session.deleteFile("/does not exist");
        
    }

    /*
     * Test method for 'org.bodington.server.UploadedFileSession.renameFile(String, String)'
     */
    public void testRenameFile() throws Exception
    {
        
        // Sucessful rename.
        assertNotNull(session.createFolder("/"));
        assertNotNull(session.createFolder("/folder"));
        session.renameFile("/folder", "newfolder");
        assertNull(session.getFileSummary("/folder"));
        assertNotNull(session.getFileSummary("/newfolder"));
        
        // And back.
        session.renameFile("/newfolder", "folder");
        assertNull(session.getFileSummary("/newfolder"));
        assertNotNull(session.getFileSummary("/folder"));
        
        // Renaming non-existant.
        try
        {
            session.renameFile("/does not exist", "not here");
            fail("Excepted Exception: Source for rename does not exist");    
        }
        catch (Exception e)
        {}
        
        // Renaming to a file containing "/"
        try
        {
            session.renameFile("/folder", "/folder/new");
            fail("Expected Exception: Can't use rename to move files");
        }
        catch (Exception e)
        {}
        
        // Renaming to an existing file
        session.createFolder("/otherfolder");
        try
        {
            session.renameFile("/folder", "otherfolder");
            fail("Expected Exception; Rename shouldn't replace existing folders");
        }
        catch (Exception e)
        {}
        
        // Shouldn't be able to rename the root folder.
        try
        {
            session.renameFile("/", "broken");
            fail("Expected Exception: You can't rename the root folder.");
        }
        catch (Exception e)
        {}

    }

    /*
     * Test method for 'org.bodington.server.UploadedFileSession.undeleteFile(String, boolean)'
     */
    public void testUndeleteFile() throws Exception
    {
        // Check that undeleting works.
        session.createFolder("/");
        session.createFolder("/folder");
        session.deleteFile("/folder");
        assertTrue(session.getFileSummary("/folder").isDeleted());
        session.undeleteFile("/folder", false);
        assertFalse(session.getFileSummary("/folder").isDeleted());
        
        session.undeleteFile("/does not exist", false);
        
        // Test recursive undeleting
        session.deleteFile("/");
        assertTrue(session.getFileSummary("/folder").isDeleted());
        session.undeleteFile("/", false);
        assertTrue(session.getFileSummary("/folder").isDeleted());
        session.undeleteFile("/", true);
        assertFalse(session.getFileSummary("/folder").isDeleted());
        
    }

    /*
     * Test method for 'org.bodington.server.UploadedFileSession.writeToFile(String, long, byte[], int)'
     */
    public void testWriteToFile()
    {
        // Current unimplemented
        try
        {
            session.writeToFile("/", 0, null, 0);
            fail("Need to create a test.");
        }
        catch (Exception e)
        {}
    }

    /*
     * Test method for 'org.bodington.server.UploadedFileSession.compareWithFile(String, long, long, int)'
     */
    public void testCompareWithFile()
    {
        // Current unimplemented
        try
        {
            session.compareWithFile("/", 0, 0, 0);
            fail("Need to create a test.");
        }
        catch (Exception e)
        {}

    }

    /*
     * Test method for 'org.bodington.server.UploadedFileSession.readFromFile(String, long, byte[])'
     */
    public void testReadFromFile()
    {
        // Current unimplemented
        try
        {
            session.readFromFile("/", 0, null);
            fail("Need to create a test.");
        }
        catch (Exception e)
        {}
    }

    /*
     * Test method for 'org.bodington.server.UploadedFileSession.getRealURL(PrimaryKey)'
     */
    public void testGetRealURL()
    {

    }

    /*
     * Test method for 'org.bodington.server.UploadedFileSession.getPublishedURL(PrimaryKey)'
     */
    public void testGetPublishedURL()
    {

    }

    /*
     * Test method for 'org.bodington.server.UploadedFileSession.getFileMimeType(String)'
     */
    public void testGetFileMimeType()
    {

    }

    /*
     * Test method for 'org.bodington.server.UploadedFileSession.setFileMimeType(String, String)'
     */
    public void testSetFileMimeType()
    {

    }

    /*
     * Test method for 'org.bodington.server.UploadedFileSession.getFileSummary(String)'
     */
    public void testGetFileSummaryString() throws Exception
    {
        UploadedFileSummary summary = session.getFileSummary((String)null);
        assertNull(summary);
    }

    /*
     * Test method for 'org.bodington.server.UploadedFileSession.getFileSummary(String[])'
     */
    public void testGetFileSummaryStringArray() throws Exception
    {
        UploadedFileSummary summary = session.getFileSummary((String[])null);
        assertNull(summary);
    }

    /*
     * Test method for 'org.bodington.server.UploadedFileSession.getFileSummary(PrimaryKey)'
     */
    public void testGetFileSummaryPrimaryKey() throws Exception
    {
        UploadedFileSummary summary = session.getFileSummary((PrimaryKey)null);
        assertNull(summary);
    }

    /*
     * Test method for 'org.bodington.server.UploadedFileSession.getFile(PrimaryKey)'
     */
    public void testGetFile() throws Exception
    {
        // Check we can get the root directory
        assertNotNull(session.createFolder("/"));
        File file = session.getFile(session.getFileSummary("/").getPrimaryKey());
        assertTrue(file.exists());
        assertTrue(file.isDirectory());

    }

    /*
     * Test method for 'org.bodington.server.UploadedFileSession.getFileAndDescendentSummaries(String, boolean)'
     */
    public void testGetFileAndDescendentSummaries()
    {

    }

    /*
     * Test method for 'org.bodington.server.UploadedFileSession.getFileAndAncestorSummaries(String)'
     */
    public void testGetFileAndAncestorSummaries()
    {

    }

    /*
     * Test method for 'org.bodington.server.UploadedFileSession.getFileAndDescendentSummaryTree(String, boolean)'
     */
    public void testGetFileAndDescendentSummaryTree()
    {

    }
    
    public void testQuotas() throws Exception
    {
        URL url = getClass().getResource("text.txt");
        assertNotNull(url);
 
        // Must copy the file into a temp location as transfer file deletes
        // the orginal.
        File source = copyToTemp(new File(url.getFile()));
        long fileLength = source.length();
        long quotaLimit = fileLength + 1;
        
        session.setQuota(quotaLimit);
        
        // Switch to normal user.
        BuildingContext.getContext().setUser(otherUser);
        
        session.transferFile(source.getAbsolutePath(), "text1.txt", null);
        source = copyToTemp(new File(url.getFile()));
        assertEquals(fileLength, session.getQuota().getUsage());
        try {
            session.transferFile(source.getAbsolutePath(), "text2.txt", null);
            fail("We should have run out of quota.");
        } catch (QuotaExceededException qee) {}
        
        // Check that overwriting works.
        source = copyToTemp(new File(url.getFile()));
        session.transferFile(source.getAbsolutePath(), "text1.txt", null);
        assertEquals(fileLength, session.getQuota().getUsage());
        
        // Check overwritting with a smaller file.
        File small = File.createTempFile("small", null);
        PrintWriter out = new PrintWriter(new FileOutputStream(small));
        out.print("hello world");
        out.close();
        long smallLength = small.length();
        session.transferFile(small.getAbsolutePath(), "text1.txt", null);

        assertEquals(smallLength, session.getQuota().getUsage());
        
        
    }

}
