/* ======================================================================
   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.resources;

import java.security.Principal;

import java.io.ByteArrayInputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.util.Enumeration;
import java.util.zip.ZipEntry;
import java.util.zip.ZipOutputStream;

import javax.servlet.ServletException;
import javax.servlet.http.HttpServletResponse;

import org.apache.log4j.Logger;

import org.bodington.server.BuildingContext;
import org.bodington.server.BuildingServerException;
import org.bodington.server.BuildingSession;
import org.bodington.server.BuildingSessionManagerImpl;
import org.bodington.server.events.ExportEvent;
import org.bodington.server.realm.Acl;
import org.bodington.server.realm.AclEntry;
import org.bodington.server.realm.Permission;
import org.bodington.servlet.FacilityList;
import org.bodington.servlet.Request;
import org.bodington.servlet.Response;
import org.bodington.servlet.facilities.Facility;

/**
 * Helper functions that don't need to be in resource because they clutter the API
 * or make resource depend on packages it shouldn't.
 * @author buckett
 */
public class ResourceUtils
{
	private static Logger log = Logger.getLogger(ResourceUtils.class);
	 
    /**
     * Attempt to work out the URL for uploaded files.
     * Looks at the property <code>webpublish.webaddress</code>.
     * If it starts with a "/" then use it, otherwise prefix it with the context.
     * It doesn't at the moment support using a completly different URL.
     * @param resource The resource to get the path for.
     * @param request The request through which the call is being made.
     * @return A the path.
     * @throws BuildingServerException
     */
    public static String getWebPublishAddress(Resource resource, Request request)
    {
        String path = BuildingContext.getProperty( "webpublish.webaddress", "files/");
        if (!path.startsWith("/"))
        {
            path = request.getContextPath()+ "/" + path;
        }
        
        return path + resource.getResourceId().toString() + "/";
    }
    /**
     * Attempt to work out the URL for generated files.
     * Looks at the property <code>filegeneration.webaddress</code>.
     * If it starts with a "/" then use it, otherwise prefix it with the context.
     * It doesn't at the moment support using a completly different URL.
     * @param resource The resource to get the path for.
     * @param request The request through which the call is being made.
     * @return A the path.
     * @throws BuildingServerException
     */
    public static String getGeneratedFileAddress(Resource resource, Request request)
    {
        String path = BuildingContext.getProperty( "filegeneration.webaddress", "generated/");
        if (!path.startsWith("/"))
        {
            path = request.getContextPath()+ "/" + path;
        }
        return path + resource.getEncodedGeneratedFolder("/")+ "/";
    }
    
    /**
     * Attempt to grant some permissions to a Resource. This is often 
     * used by facilities to grant additional permissions on a resource straight after
     * it has been created. This method doesn't save the ACL after is has been
     * updated and it is up to the calling code.
     * @param resource The resource to add the permissions to.
     * @param grant An array of permissions to be added
     * @return True if the permissions were sucessfully added.
     */
    public static boolean grantPermissions(Resource resource, Permission[] grant)
    {
        try
        {
            Acl acl = resource.getAcl();
            if (acl != null)
            {
                AclEntry aclEntry = acl.getOwnerGroupAclEntry();
                if (aclEntry != null)
                {
                    for(int count = 0; count < grant.length; count++)
                    {
                        aclEntry.addPermission(grant[count]);
                    }
                    return true;
                }
            }
            log.warn("Resource doesn't have default ACLs: "+ resource);
        }
        catch (BuildingServerException bse)
        {
            log.warn("Could not get ACL details for: " + resource);
        }
        return false;
    }
    
    /**
     * Check to see if the current user has <em>any</em> of the supplied
     * permissions on supplied resource.
     * @param resource The Resource to perform the permission checks against.
     * @param check An array of permissions to check. If the array is empty 
     * false will be returned.
     * @return <code>true</code> if the current user has any of the permissions
     * on the supplied resource.
     */
    public static boolean anyPermission(Resource resource, Permission[] check)
    {
        for (int i = 0; i < check.length; i++)
            if (resource.checkPermission(check[i]))
                return true;
        return false;
    }
    
    /**
     * Check to see if any of the principals have the requested permission on the
     * supplied resource.
     * @param resource The resource to perform the check against.
     * @param principal An array of principals to check. Typically this will be groups.
     * @param permission The permission to test for.
     * @return <code>true</code> if the permission is had by any of the principals
     * on the resource, otherwise <code>false</code>.
     */
    public static boolean anyPrincipal(Resource resource, Principal principal[], Permission permission)
    {
        for (int i = 0; i < principal.length; i++)
            if (resource.checkPermission( principal[i], permission ))
                return true;
        return false;
    }
    
    public static Facility getFacility(Resource resource)
    {
        return FacilityList.getFacilities().get(new Integer(resource.getHttpFacilityNo()));
    }
    
    /**
     * Walk the Resource tree starting from current node and add Uploaded Files to a Zip archive.
     * An XML manifest file is included if requested. Recursion is also optional.
     * Response contains error if method is called from a containing resource that is not a room
     * or suite of rooms. Exceptions encountered when attempting to add a file to the Zip archive
     * will be handled by adding an 'error' ZipEntry.
     * @param request The request.
     * @param response The response that zipOutputStream writes to.
     * @throws ServletException
     * @throws IOException
     */
    public static void sendWebDocumentContentPackages( Request request, HttpServletResponse response) throws ServletException, IOException
    {
    	Resource resource = request.getResource();
    	if (resource.getResourceType() == Resource.RESOURCE_SUITE ||
    			resource.getResourceType() == Resource.RESOURCE_ROOM ||
    			resource.getResourceType() == Resource.RESOURCE_EASYBUILDER ||
    			resource.getResourceType() == Resource.RESOURCE_HOME ||
    			resource.getResourceType() == Resource.RESOURCE_MEDIADOCUMENT ||
    			resource.getResourceType() == Resource.RESOURCE_DOCUMENT) {

    		response.setContentType( "application/octet-stream" );
    		response.setHeader("Content-Disposition","attachment; filename=\"" + resource.getName() +".zip\"");

    		ContentZipper zipper;
    		if (request.getParameter("includeManifest") != null)
    			zipper = new ContentPackager();
    		else zipper = new ContentZipper();
    		
    		if (request.getParameter("recursive") != null)
    			zipper.setRecursive( true );

    		if (request.getParameter("includeDescriptions") != null)
    			zipper.setIncludeDescriptions( true );

    		if (request.getParameter("includeIntroductions") != null)
    			zipper.setIncludeIntroductions(true);
    		
    		if (request.getParameter("includeQuickLinks") != null)
    			zipper.setIncludeQuickLinks(true);
    		
    		zipper.packageResource( resource, response.getOutputStream() );
    		
    	} else {
    		response.sendError( Response.SC_INTERNAL_SERVER_ERROR, "Incorrect resource - the command can only be run from within a Room, Suite, Easybuilder, MyWebLearn space, or Structured and Web Documents." );
    	}
    }
}