/*
 * Licensed to the Apache Software Foundation (ASF) under one or more
 * contributor license agreements.  See the NOTICE file distributed with
 * this work for additional information regarding copyright ownership.
 * The ASF licenses this file to You 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.apache.jetspeed.page;

import java.security.Principal;
import java.util.Iterator;

import javax.security.auth.Subject;

import org.apache.jetspeed.om.common.SecurityConstraints;
import org.apache.jetspeed.om.folder.Folder;
import org.apache.jetspeed.om.folder.FolderNotFoundException;
import org.apache.jetspeed.om.page.DynamicPage;
import org.apache.jetspeed.om.page.FragmentDefinition;
import org.apache.jetspeed.om.page.Link;
import org.apache.jetspeed.om.page.Page;
import org.apache.jetspeed.om.page.PageTemplate;
import org.apache.jetspeed.page.document.NodeException;
import org.apache.jetspeed.security.Role;
import org.apache.jetspeed.security.SubjectHelper;
import org.apache.jetspeed.security.User;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

/**
 * PageManagerUtils
 * 
 * @author <a href="mailto:taylor@apache.org">David Sean Taylor</a>
 * @author <a href="mailto:firevelocity@gmail.com">Vivek Kumar</a>
 * @version $Id: $
 */
public class PageManagerUtils
{
    protected static Logger log = LoggerFactory.getLogger(PageManagerUtils.class);    
    
    /**
     * Creates a user's home page from the roles of the current user.
     * The use case: when a portal is setup to use shared pages, but then
     * the user attempts to customize. At this point, we create the new page(s) for the user.
     * 
     * @param subject
     */
    public static void createUserHomePagesFromRoles(PageManager pageManager, Subject subject)
    throws NodeException
    {
        Principal principal = SubjectHelper.getBestPrincipal(subject, User.class); 
        if (principal == null)
        {
            String errorMessage = "Could not create user home for null principal";
            log.error(errorMessage);
            throw new NodeException(errorMessage);
        }
        try
        {
            String userName = principal.getName();            
            // get user home
            Folder newUserFolder;
            if (pageManager.userFolderExists(userName))
            {
                newUserFolder = pageManager.getUserFolder(userName);
            }
            else
            {
                newUserFolder = pageManager.newFolder(Folder.USER_FOLDER + userName);
                SecurityConstraints constraints = pageManager.newSecurityConstraints();
                newUserFolder.setSecurityConstraints(constraints);
                newUserFolder.getSecurityConstraints().setOwner(userName);
                pageManager.updateFolder(newUserFolder);                
            }            
            // for each role for a user, deep copy the folder contents for that role 
            // into the user's home
            // TODO: this algorithm could actually merge pages on dups
            Iterator roles = SubjectHelper.getPrincipals(subject, Role.class).iterator();
            while (roles.hasNext())
            {                            
                Role role = (Role)roles.next();
                if (pageManager.folderExists(Folder.ROLE_FOLDER + role.getName()))
                {
                    Folder roleFolder = pageManager.getFolder(Folder.ROLE_FOLDER + role.getName());                    
                    deepMergeFolder(pageManager, roleFolder, Folder.USER_FOLDER + newUserFolder.getName(), userName, role.getName());
                }
            }
        }
        catch (Exception e)
        {
            String errorMessage = "createUserHomePagesFromRoles failed: " + e.getMessage();
            log.error(errorMessage, e);
            throw new NodeException(errorMessage, e);
        }
    }

    /**
     * Deep merges from a source folder into a destination path for the given owner.
     * The unique name is used in conflict resolution for name collisions.
     * Example: deep merge a given role folder 'X' into /_user/david
     *          uniqueName = 'X'
     *          owner = 'david'
     *          destinationPath = '_user/david'
     *          
     * @param srcFolder
     * @param destinationPath
     * @param owner
     * @param uniqueName
     * @throws NodeException
     */
    public static void deepMergeFolder(PageManager pageManager, Folder srcFolder, String destinationPath, String owner, String uniqueName)
    throws NodeException
    {        
        Iterator pages = srcFolder.getPages().iterator();
        while (pages.hasNext())
        {
            Page srcPage = (Page)pages.next();
            String path = concatenatePaths(destinationPath, srcPage.getName());
            if (!pageManager.pageExists(path))
            {
                Page dstPage = pageManager.copyPage(srcPage, path);
                pageManager.updatePage(dstPage);
            }
        }     
        Iterator pageTemplates = srcFolder.getPageTemplates().iterator();
        while (pageTemplates.hasNext())
        {
            PageTemplate srcPageTemplate = (PageTemplate)pageTemplates.next();
            String path = concatenatePaths(destinationPath, srcPageTemplate.getName());
            if (!pageManager.pageTemplateExists(path))
            {
                PageTemplate dstPageTemplate = pageManager.copyPageTemplate(srcPageTemplate, path);
                pageManager.updatePageTemplate(dstPageTemplate);
            }
        }     
        Iterator dynamicPages = srcFolder.getDynamicPages().iterator();
        while (dynamicPages.hasNext())
        {
            DynamicPage srcDynamicPage = (DynamicPage)dynamicPages.next();
            String path = concatenatePaths(destinationPath, srcDynamicPage.getName());
            if (!pageManager.dynamicPageExists(path))
            {
                DynamicPage dstDynamicPage = pageManager.copyDynamicPage(srcDynamicPage, path);
                pageManager.updateDynamicPage(dstDynamicPage);
            }
        }     
        Iterator fragmentDefinitions = srcFolder.getFragmentDefinitions().iterator();
        while (fragmentDefinitions.hasNext())
        {
            FragmentDefinition srcFragmentDefinition = (FragmentDefinition)fragmentDefinitions.next();
            String path = concatenatePaths(destinationPath, srcFragmentDefinition.getName());
            if (!pageManager.fragmentDefinitionExists(path))
            {
                FragmentDefinition dstFragmentDefinition = pageManager.copyFragmentDefinition(srcFragmentDefinition, path);
                pageManager.updateFragmentDefinition(dstFragmentDefinition);
            }
        }
        
        Iterator links = srcFolder.getLinks().iterator();
        while (links.hasNext())
        {
            Link srcLink = (Link)links.next();
            String path = concatenatePaths(destinationPath, srcLink.getName());
            if (!pageManager.linkExists(path))
            {
                Link dstLink = pageManager.copyLink(srcLink, path);
                pageManager.updateLink(dstLink);
            }
        }
        
        Iterator folders = srcFolder.getFolders().iterator();
        while (folders.hasNext())
        {
            Folder folder = (Folder)folders.next();
            String newPath = concatenatePaths(destinationPath, folder.getName());
            if (!pageManager.folderExists(newPath))
            {
                Folder dstFolder = pageManager.copyFolder(folder, newPath);
                pageManager.updateFolder(dstFolder);
            }
            deepMergeFolder(pageManager, folder, newPath, null, uniqueName);
        }                
    }

    public static String concatenatePaths(String base, String path)
    {
        String result = "";
        if (base == null)
        {
            if (path == null)
            {
                return result;
            }
            return path;
        }
        else
        {
            if (path == null)
            {
                return base;
            }
        }
        if (base.endsWith(Folder.PATH_SEPARATOR)) 
        {
            if (path.startsWith(Folder.PATH_SEPARATOR))
            {
                result = base.concat(path.substring(1));
                return result;
            }
        
        }
        else
        {
            if (!path.startsWith(Folder.PATH_SEPARATOR)) 
            {
                result = base.concat(Folder.PATH_SEPARATOR).concat(path);
                return result;
            }
        }
        return base.concat(path);
    }
    
    /**
     * Deep copy a folder
     *  
     * @param pageManager target page manager
     * @param srcFolder source folder
     * @param destinationPath destination folder
     * @param owner user name of owner or null
     * @param copyIds flag indicating whether to copy ids
     */
    public static void deepCopyFolder(PageManager pageManager, Folder srcFolder, String destinationPath, String owner, boolean copyIds)
    throws NodeException
    {
        boolean found = true;
        try
        {
            pageManager.getFolder(destinationPath);
        }
        catch (FolderNotFoundException e)
        {
            found = false;
        }
        if (found)
        {
            throw new NodeException("Destination already exists");
        }
        Folder dstFolder = pageManager.copyFolder(srcFolder, destinationPath);
        if (owner != null)
        {
            SecurityConstraints constraints = dstFolder.getSecurityConstraints();
            if (constraints == null)
            {
                constraints = pageManager.newSecurityConstraints();
                dstFolder.setSecurityConstraints(constraints);
            }
            dstFolder.getSecurityConstraints().setOwner(owner);
        }
        pageManager.updateFolder(dstFolder);
        
        Iterator pages = srcFolder.getPages().iterator();
        while (pages.hasNext())
        {
            Page srcPage = (Page)pages.next();
            String path = PageManagerUtils.concatenatePaths(destinationPath, srcPage.getName());
            Page dstPage = pageManager.copyPage(srcPage, path, copyIds);
            pageManager.updatePage(dstPage);
        }
        Iterator pageTemplates = srcFolder.getPageTemplates().iterator();
        while (pageTemplates.hasNext())
        {
            PageTemplate srcPageTemplate = (PageTemplate)pageTemplates.next();
            String path = PageManagerUtils.concatenatePaths(destinationPath, srcPageTemplate.getName());
            PageTemplate dstPageTemplate = pageManager.copyPageTemplate(srcPageTemplate, path, copyIds);
            pageManager.updatePageTemplate(dstPageTemplate);
        }     
        Iterator dynamicPages = srcFolder.getDynamicPages().iterator();
        while (dynamicPages.hasNext())
        {
            DynamicPage srcDynamicPage = (DynamicPage)dynamicPages.next();
            String path = PageManagerUtils.concatenatePaths(destinationPath, srcDynamicPage.getName());
            DynamicPage dstDynamicPage = pageManager.copyDynamicPage(srcDynamicPage, path, copyIds);
            pageManager.updateDynamicPage(dstDynamicPage);
        }     
        Iterator fragmentDefinitions = srcFolder.getFragmentDefinitions().iterator();
        while (fragmentDefinitions.hasNext())
        {
            FragmentDefinition srcFragmentDefinition = (FragmentDefinition)fragmentDefinitions.next();
            String path = PageManagerUtils.concatenatePaths(destinationPath, srcFragmentDefinition.getName());
            FragmentDefinition dstFragmentDefinition = pageManager.copyFragmentDefinition(srcFragmentDefinition, path, copyIds);
            pageManager.updateFragmentDefinition(dstFragmentDefinition);
        }     
     
        Iterator links = srcFolder.getLinks().iterator();
        while (links.hasNext())
        {
            Link srcLink = (Link)links.next();
            String path = PageManagerUtils.concatenatePaths(destinationPath, srcLink.getName());
            Link dstLink = pageManager.copyLink(srcLink, path);
            pageManager.updateLink(dstLink);
        }
     
        Iterator folders = srcFolder.getFolders().iterator();
        while (folders.hasNext())
        {
            Folder folder = (Folder)folders.next();
            String newPath = concatenatePaths(destinationPath, folder.getName()); 
            deepCopyFolder(pageManager, folder, newPath, null, copyIds);
        }        
    }

    /**
     * Deep merge a folder
     *  
     * @param pageManager target page manager
     * @param srcFolder source folder
     * @param destinationPath destination folder
     * @param owner user name of owner or null
     * @param copyIds flag indicating whether to copy ids on merge
     */
    public static void deepMergeFolder(PageManager pageManager, Folder srcFolder, String destinationPath, String owner, boolean copyIds)
    throws NodeException
    {
        boolean found = true;
        Folder dstFolder = null;
        try
        {
            dstFolder = pageManager.getFolder(destinationPath);
        }
        catch (FolderNotFoundException e)
        {
            found = false;
        }
        if (!found)
        {
            dstFolder = pageManager.copyFolder(srcFolder, destinationPath);
            if (owner != null)
            {
                SecurityConstraints constraints = dstFolder.getSecurityConstraints();
                if (constraints == null)
                {
                    constraints = pageManager.newSecurityConstraints();
                    dstFolder.setSecurityConstraints(constraints);
                }
                dstFolder.getSecurityConstraints().setOwner(owner);
            }
            pageManager.updateFolder(dstFolder);
        }
        Iterator pages = srcFolder.getPages().iterator();
        while (pages.hasNext())
        {
            Page srcPage = (Page)pages.next();
            String path = PageManagerUtils.concatenatePaths(destinationPath, srcPage.getName());
            if (!pageManager.pageExists(path))
            {
                Page dstPage = pageManager.copyPage(srcPage, path, copyIds);
                pageManager.updatePage(dstPage);
            }
        }
        Iterator pageTemplates = srcFolder.getPageTemplates().iterator();
        while (pageTemplates.hasNext())
        {
            PageTemplate srcPageTemplate = (PageTemplate)pageTemplates.next();
            String path = PageManagerUtils.concatenatePaths(destinationPath, srcPageTemplate.getName());
            if (!pageManager.pageTemplateExists(path))
            {
                PageTemplate dstPageTemplate = pageManager.copyPageTemplate(srcPageTemplate, path, copyIds);
                pageManager.updatePageTemplate(dstPageTemplate);
            }
        }     
        Iterator dynamicPages = srcFolder.getDynamicPages().iterator();
        while (dynamicPages.hasNext())
        {
            DynamicPage srcDynamicPage = (DynamicPage)dynamicPages.next();
            String path = PageManagerUtils.concatenatePaths(destinationPath, srcDynamicPage.getName());
            if (!pageManager.dynamicPageExists(path))
            {
                DynamicPage dstDynamicPage = pageManager.copyDynamicPage(srcDynamicPage, path, copyIds);
                pageManager.updateDynamicPage(dstDynamicPage);
            }
        }     
        Iterator fragmentDefinitions = srcFolder.getFragmentDefinitions().iterator();
        while (fragmentDefinitions.hasNext())
        {
            FragmentDefinition srcFragmentDefinition = (FragmentDefinition)fragmentDefinitions.next();
            String path = PageManagerUtils.concatenatePaths(destinationPath, srcFragmentDefinition.getName());
            if (!pageManager.fragmentDefinitionExists(path))
            {
                FragmentDefinition dstFragmentDefinition = pageManager.copyFragmentDefinition(srcFragmentDefinition, path, copyIds);
                pageManager.updateFragmentDefinition(dstFragmentDefinition);
            }
        }
     
        Iterator links = srcFolder.getLinks().iterator();
        while (links.hasNext())
        {
            Link srcLink = (Link)links.next();
            String path = PageManagerUtils.concatenatePaths(destinationPath, srcLink.getName());
            if (!pageManager.linkExists(path))
            {            
                Link dstLink = pageManager.copyLink(srcLink, path);
                pageManager.updateLink(dstLink);
            }
        }
     
        Iterator folders = srcFolder.getFolders().iterator();
        while (folders.hasNext())
        {
            Folder folder = (Folder)folders.next();
            String newPath = concatenatePaths(destinationPath, folder.getName()); 
            deepMergeFolder(pageManager, folder, newPath, null, copyIds);
        }        
    }
    
}
