/* ======================================================================
   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.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.nio.MappedByteBuffer;
import java.nio.channels.FileChannel;
import java.nio.channels.FileChannel.MapMode;
import java.nio.charset.Charset;
import java.nio.charset.CharsetDecoder;
import java.security.acl.NotOwnerException;

import org.apache.log4j.Logger;
import org.bodington.server.events.Event;
import org.bodington.server.events.HomeContainerEvent;
import org.bodington.server.realm.AclEntry;
import org.bodington.server.realm.Group;
import org.bodington.server.realm.PassPhrase;
import org.bodington.server.realm.Permission;
import org.bodington.server.realm.User;
import org.bodington.server.realm.WebAuthUser;
import org.bodington.server.resources.HomeResource;
import org.bodington.server.resources.Resource;
import org.bodington.server.resources.ResourceTreeManager;
import org.bodington.server.resources.UploadedFileSummary;
import org.bodington.servlet.FacilityList;

public class HomeContainerSessionImpl extends BuildingSessionImpl implements HomeContainerSession
{

    private static Logger log = Logger.getLogger(HomeContainerSessionImpl.class);
    
    public static final String QUOTA_FILES = "homecontainer.quota.files";
    public static final String QUOTA_RESOURCES = "homecontainer.quota.resources";
    private final int files_quota;
    private final int resources_quota;
    
    public HomeContainerSessionImpl()
    {
        super();
        files_quota = BuildingContext.getProperty(QUOTA_FILES, 10000000, true);
        resources_quota = BuildingContext.getProperty(QUOTA_RESOURCES, 3000, true);
    }

    public boolean hasHome(User user) throws BuildingServerException
    {
        return findHome(user) != null;
    }

    /**
     * Creates a home container in this resource for a user. At the moment
     * the strings in the resource are hard coded.
     */
    public void createHome(User user) throws BuildingServerException
    {
        if (!getResource().checkPermission(Permission.CREATE))
            throw new PermissionDeniedException(Permission.CREATE);
        if (hasHome(user))
            throw new BuildingServerException("Resource already exists for: "+ user);
        
        String url = mapUserToURL(user);
        
        
        HomeResource resource = newResource();
        resource.setName(url);
        resource.setTitle("");
        resource.setDescription("Space for "+ user);
        resource.setIntroduction("This is your space");
        resource.setUserId(user.getUserId());

        ResourceTreeManager.getInstance().addResource(getResource(), resource);
        AclEntry aclEntry = resource.getAcl().getOwnerGroupAclEntry();
        aclEntry.addPermission(Permission.ADMINISTER);
        aclEntry.save();

        
        Event event = new HomeContainerEvent(getResource(),
            (User) BuildingContext.getContext().getUser(), user,
            HomeContainerEvent.SIGNUP);
        event.save();
        
        BuildingSession resourceSession = BuildingSessionManagerImpl.getSession(resource);
        resourceSession.getUploadedFileSession().createQuota(files_quota);
        resourceSession.createQuota(resources_quota);
        resourceSession.updateBasicMetadata();
        
        createDefaultResources(resource);
        
    }

    private void createDefaultResources(HomeResource resource) throws BuildingServerException
    {
        // Create some default resources.
        
        // The Public Resource
        Resource publicResource = new Resource();
        publicResource.setHttpFacilityNo(FacilityList.getFacilities().get("suite").id.intValue());
        publicResource.setName("public");
        publicResource.setTitle("Public Space");
        publicResource.setDescription("Space for content accessible by everyone");
        publicResource.setIntroduction("Public Space");
        ResourceTreeManager.getInstance().addResource(resource, publicResource);
        BuildingSessionManagerImpl.getSession(publicResource).updateBasicMetadata();
        
        Group group = Group.findGroupByName("public");
        AclEntry aclEntry=new AclEntry();
        aclEntry.setPrincipal( group );
        aclEntry.addPermission(Permission.SEE);
        aclEntry.addPermission(Permission.VIEW);
        try
        {
            publicResource.getAcl().addEntry( aclEntry );
        }
        catch (NotOwnerException e)
        {
            log.warn("Failed to add public acl", e);
        }
        publicResource.getAcl().save();
        
        Resource privateResource = new Resource();
        privateResource.setHttpFacilityNo(FacilityList.getFacilities().get("suite").id.intValue());
        privateResource.setName("private");
        privateResource.setTitle("Private Space");
        privateResource.setDescription("Space for content accessible by only you");
        privateResource.setIntroduction("Private Space");
        ResourceTreeManager.getInstance().addResource(resource, privateResource);
        BuildingSessionManagerImpl.getSession(privateResource).updateBasicMetadata();
    }

    public Resource findHome(User user) throws BuildingServerException
    {
        Resource resource = HomeResource.findHomeResource(user, getResource());
        // TODO We should check if the resource is deleted.
        return resource;
    }
    
    private String mapUserToURL(User user)
    {
        String name = null;
        try
        {
            WebAuthUser waUser = WebAuthUser.findWebAuthUser( user );
            if (waUser != null)
                name = waUser.getUserName();
            else
            {
                PassPhrase passPhrase = PassPhrase.findPassPhrase(user);
                if (passPhrase != null)
                    name = passPhrase.getUserName();
            }
        }
        catch (BuildingServerException e)
        {
            log.error("Problem finding WebAuthUser for: "+ user, e);
        }
        return name;
    }
    
    public String getTerms(User user) throws BuildingServerException
    {
        UploadedFileSummary fileSummary = getUploadedFileSession().getFileSummary("/terms.html");
        if (fileSummary != null)
        {
            File file = getUploadedFileSession().getFile(fileSummary.getPrimaryKey());
            FileInputStream input = null;
            try
            {
                input = new FileInputStream(file);
                FileChannel channel = input.getChannel();
                MappedByteBuffer buffer = 
                    channel.map(MapMode.READ_ONLY, 0, (int)channel.size());
                CharsetDecoder decoder = Charset.forName("UTF-8").newDecoder();
                return decoder.decode(buffer).toString();
            }
            catch (FileNotFoundException e)
            {
                log.error("Could not file uploaded file: "+ file);
            }
            catch (IOException e)
            {
                log.error("Failed to read file: "+ file, e);
            }
            finally
            {
                if (input != null)
                {
                    try
                    {
                        input.close();
                    }
                    catch (IOException ioe)
                    {
                        log.error("Problem closing stream on file: "+ file, ioe);
                    }
                }
            }
            throw new BuildingServerException("Failed to load terms.");
        }
        else
            log.info("Couldn't find terms and conditions file");
        return null;
    }
    
    public boolean canCreateHome(User user) throws BuildingServerException
    {
       return !hasHome(user) &&
           getResource().checkPermission(user, Permission.CREATE) &&
           mapUserToURL(user) != null;
    }
    
    private HomeResource newResource()
    {
        HomeResource resource = new HomeResource();
        resource.setHttpFacilityNo(FacilityList.getFacilities().get("home").id.intValue());
        return resource;
    }
    

}
