/**
 *
 * Magnolia SimpleMedia Module (http://www.openmindlab.com/lab/products/media.html)
 * Copyright (C)2008 - 2010, Openmind S.r.l. http://www.openmindonline.it
 *
 *  This program is free software: you can redistribute it and/or modify
 *  it under the terms of the GNU General Public License as published by
 *  the Free Software Foundation, either version 3 of the License, or
 *  (at your option) any later version.
 *
 *  This program 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 General Public License for more details.
 *
 *  You should have received a copy of the GNU General Public License
 *  along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */

package net.sourceforge.openutils.mgnlmedia.media.pages;

import info.magnolia.cms.core.Content;
import info.magnolia.cms.core.HierarchyManager;
import info.magnolia.cms.core.ItemType;
import info.magnolia.cms.core.Path;
import info.magnolia.cms.core.SystemProperty;
import info.magnolia.cms.core.search.Query;
import info.magnolia.cms.core.search.QueryManager;
import info.magnolia.cms.core.search.QueryResult;
import info.magnolia.cms.exchange.ActivationManagerFactory;
import info.magnolia.cms.exchange.ExchangeException;
import info.magnolia.cms.security.Permission;
import info.magnolia.cms.util.AlertUtil;
import info.magnolia.cms.util.ExclusiveWrite;
import info.magnolia.commands.CommandsManager;
import info.magnolia.context.Context;
import info.magnolia.context.MgnlContext;
import info.magnolia.module.admininterface.commands.ActivationCommand;

import java.io.IOException;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.Map;

import javax.jcr.RepositoryException;
import javax.servlet.ServletException;
import javax.servlet.http.Cookie;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import net.sourceforge.openutils.mgnlmedia.media.configuration.MediaConfigurationManager;
import net.sourceforge.openutils.mgnlmedia.media.configuration.MediaTypeConfiguration;
import net.sourceforge.openutils.mgnlmedia.media.lifecycle.MediaModule;
import net.sourceforge.openutils.mgnlmedia.media.tags.el.MediaEl;

import org.apache.commons.chain.Command;
import org.apache.commons.collections.CollectionUtils;
import org.apache.commons.lang.StringUtils;
import org.apache.jackrabbit.util.ISO9075;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.google.common.base.Function;
import com.google.common.collect.Collections2;


/**
 * Page that renders the folder view
 * @author molaschi
 * @version $Id: MediaFolderViewPage.java 2376 2010-05-09 17:29:08Z fgiust $
 */
public class MediaFolderViewPage extends MessagesTemplatedMVCHandler
{

    /**
     *
     */
    public static final String SORT_BY_FILENAME = "filename";

    /**
     *
     */
    public static final String SORT_BY_MODIFICATIONDATE = "modificationdate";

    /**
     *
     */
    public static final String SORT_BY_USAGE = "usage";

    /**
     * Logger.
     */
    private Logger log = LoggerFactory.getLogger(MediaFolderViewPage.class);

    private String path;

    private String node;

    private String dest;

    private String type;

    private String sorting;

    private boolean selectMedia;

    private String actMediaHandle;

    private String mediaType;

    private Collection<MediaBean> medias;

    private Collection<MediaTypeConfiguration> types;

    private Map<String, Integer> numberOfMedia;

    private boolean writable;

    private boolean canPublish;

    private boolean develop;

    private String search;

    /**
     * @param name
     * @param request
     * @param response
     */
    public MediaFolderViewPage(String name, HttpServletRequest request, HttpServletResponse response)
    {
        super(name, request, response);
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public void init()
    {
        super.init();

        if (mediaType != null && mediaType.length() > 0)
        {
            String[] mediaTypes = StringUtils.split(mediaType, ",");
            type = mediaTypes[0];
            types = new ArrayList<MediaTypeConfiguration>();
            for (String mt : mediaTypes)
            {
                types.add(MediaConfigurationManager.getInstance().getTypes().get(mt));
            }
        }
        else if (type == null)
        {
            type = MediaConfigurationManager.getInstance().getTypes().entrySet().iterator().next().getKey();
            types = MediaConfigurationManager.getInstance().getTypes().values();
        }
        else
        {
            types = MediaConfigurationManager.getInstance().getTypes().values();
        }

        if (!StringUtils.isBlank(path) || !StringUtils.isBlank(search))
        {
            fillNumberOfMediaPerType();
        }

        if (sorting == null)
        {
            sorting = SORT_BY_MODIFICATIONDATE;
        }
    }

    /**
     * Count medias for each media type
     */
    private void fillNumberOfMediaPerType()
    {
        numberOfMedia = new HashMap<String, Integer>();
        for (MediaTypeConfiguration mtc : types)
        {
            try
            {
                Collection<Content> c = MediaConfigurationManager.getInstance().find(
                    path,
                    mtc.getName(),
                    search,
                    StringUtils.isNotBlank(search));
                numberOfMedia.put(mtc.getName(), c.size());
            }
            catch (RepositoryException e)
            {
            }
        }
    }

    /**
     * {@inheritDoc}
     */
    @SuppressWarnings("unchecked")
    @Override
    public String show()
    {
        HierarchyManager hm = MgnlContext.getInstance().getHierarchyManager(MediaModule.REPO);
        final MediaTypeConfiguration mtc = MediaConfigurationManager.getInstance().getTypes().get(type);

        Collection<Content> mediasOfType = null;

        develop = SystemProperty.getBooleanProperty("magnolia.develop");

        if (!StringUtils.isBlank(path))
        {
            try
            {
                Content folder = hm.getContent(this.getPath());

                writable = folder.isGranted(Permission.WRITE);
                canPublish = writable && ActivationManagerFactory.getActivationManager().hasAnyActiveSubscriber();

                mediasOfType = MediaConfigurationManager.getInstance().getMediaNodes(folder, type, sorting);

            }
            catch (RepositoryException ex)
            {
                log.error("Exception caught", ex);
            }
        }
        else if (!StringUtils.isBlank(search))
        {
            try
            {
                writable = false;
                canPublish = false;

                mediasOfType = MediaConfigurationManager.getInstance().find(null, type, search, true);

            }
            catch (RepositoryException ex)
            {
                log.error("Exception caught", ex);
            }
        }

        if (mediasOfType != null)
        {
            medias = Collections2.transform(mediasOfType, new Function<Content, MediaBean>()
            {

                public MediaBean apply(Content media)
                {
                    MediaBean mb = new MediaBean();
                    mb.setContent(media);
                    mb.setMetaData(media.getMetaData());
                    mb.setHandle(media.getHandle());
                    mb.setFilename(mtc.getHandler().getFilename(media));
                    mb.setTitle(mtc.getHandler().getTitle(media));
                    mb.setThumbnailUrl(mtc.getHandler().getThumbnailUrl(media));
                    mb.setPreviewUrl(mtc.getHandler().getPreviewUrl(media));
                    mb.setDescription(mtc.getHandler().getDescription(media));
                    mb.setUuid(media.getUUID());

                    try
                    {
                        mb.setWritable(media.getParent().isGranted(Permission.WRITE));
                        mb.setCanPublish(!MediaEl.module().isSingleinstance()
                            && mb.isWritable()
                            && ActivationManagerFactory.getActivationManager().hasAnyActiveSubscriber());

                        mb.getUsedInWebPages().addAll(
                            MediaConfigurationManager.getInstance().getUsedInWebPages(media.getUUID()));
                    }
                    catch (RepositoryException ex)
                    {
                        log.error("Exception caught", ex);
                    }

                    mb.setMediaInfo(mtc.getHandler().getMediaInfo(media));
                    mb.setExternal(mtc.getHandler().isExternal(media));

                    return mb;
                }
            });
        }
        else
        {
            medias = CollectionUtils.EMPTY_COLLECTION;
        }

        return super.show();
    }

    /**
     * delete a node
     * @return view
     */
    public String delete()
    {
        HierarchyManager hm = MgnlContext.getInstance().getHierarchyManager(MediaModule.REPO);

        try
        {
            synchronized (ExclusiveWrite.getInstance())
            {
                hm.delete(this.node);
                hm.save();
            }
        }
        catch (RepositoryException ex)
        {
            log.error("Exception deleting node {} from repository media", node, ex);
        }

        return show();
    }

    /**
     * moves a node
     * @return view
     */
    public String move()
    {
        String nodeName = StringUtils.substringAfterLast(node, "/");
        String destinationNode = dest + "/" + nodeName;
        try
        {
            copyMoveNode(node, destinationNode, true);
        }
        catch (ExchangeException e)
        {
            log.error("Exception deactivating node", e);
            AlertUtil.setMessage("Problem during deactivation");
        }
        catch (RepositoryException e)
        {
            log.error("Exception moving node", e);
            AlertUtil.setMessage("Problem during moving");
        }
        return show();
    }

    /**
     * copy a node
     * @return view
     */
    public String copy()
    {
        String nodeName = StringUtils.substringAfterLast(node, "/");
        String destinationNode = dest + "/" + nodeName;
        try
        {
            copyMoveNode(node, destinationNode, false);
        }
        catch (ExchangeException e)
        {
            log.error("Exception deactivating node", e);
            AlertUtil.setMessage("Problem during deactivation");
        }
        catch (RepositoryException e)
        {
            log.error("Exception copying node", e);
            AlertUtil.setMessage("Problem during copying");
        }
        return show();
    }

    /**
     * Copy or move a node (from AdminTreeMVCHandler)
     * @param source source node
     * @param destination destination folder
     * @param move move or copy?
     * @return new content
     * @throws ExchangeException publication problem
     * @throws RepositoryException repository exception
     */
    public Content copyMoveNode(String source, String destination, boolean move) throws ExchangeException,
        RepositoryException
    {
        HierarchyManager hm = MgnlContext.getHierarchyManager(MediaModule.REPO);

        String goTo = destination;

        if (hm.isExist(destination))
        {
            String parentPath = StringUtils.substringBeforeLast(destination, "/"); //$NON-NLS-1$
            String label = StringUtils.substringAfterLast(destination, "/"); //$NON-NLS-1$
            label = Path.getUniqueLabel(hm, parentPath, label);
            goTo = parentPath + "/" + label; //$NON-NLS-1$
        }
        if (move)
        {
            if (destination.indexOf(source + "/") == 0)
            {
                // move source into destinatin not possible
                return null;
            }
            // TODO verify if it works anyway
            // this.deactivateNode(source);
            try
            {
                hm.moveTo(source, goTo);
            }
            catch (Exception e)
            {
                // try to move below node data
                return null;
            }
        }
        else
        {
            // copy
            hm.copyTo(source, goTo);
        }
        Content newContent = hm.getContent(destination);
        try
        {
            newContent.updateMetaData();
            newContent.getMetaData().setUnActivated();
        }
        catch (Exception e)
        {
            if (log.isDebugEnabled())
            {
                log.debug("Exception caught: " + e.getMessage(), e); //$NON-NLS-1$
            }
        }
        newContent.save();
        return newContent;
    }

    /**
     * Activates single media node
     * @return view
     */
    @SuppressWarnings("unchecked")
    public String activate()
    {
        Command cmd = CommandsManager.getInstance().getCommand(CommandsManager.DEFAULT_CATALOG, "activateMedia");

        ActivationCommand actCmd = (ActivationCommand) cmd;

        StringBuffer sb = new StringBuffer();
        sb.append(MediaConfigurationManager.MEDIA.getSystemName());
        sb.append(",");
        sb.append(ItemType.CONTENTNODE.getSystemName());
        actCmd.setItemTypes(sb.toString());

        Context context = MgnlContext.getInstance();
        context.put(Context.ATTRIBUTE_REPOSITORY, MediaModule.REPO);
        context.put(Context.ATTRIBUTE_PATH, this.node);
        context.put(Context.ATTRIBUTE_RECURSIVE, false);

        try
        {
            cmd.execute(context);
        }
        catch (Exception e)
        {
            AlertUtil.setMessage("Activation failed: " + e.getMessage());

            log.error("Exception activating media", e);
        }

        return this.show();
    }

    /**
     * Deactivates single media node
     * @return view
     */
    @SuppressWarnings("unchecked")
    public String deactivate()
    {
        Command cmd = CommandsManager.getInstance().getCommand(CommandsManager.DEFAULT_CATALOG, "deactivate");

        Context context = MgnlContext.getInstance();
        context.put(Context.ATTRIBUTE_REPOSITORY, MediaModule.REPO);
        context.put(Context.ATTRIBUTE_PATH, this.node);

        try
        {
            cmd.execute(context);
        }
        catch (Exception e)
        {
            AlertUtil.setMessage("Deactivation failed: " + e.getMessage());

            log.error("Exception activating media", e);
        }

        return this.show();
    }

    /**
     * Download media
     * @return nothing, but attach downloadable file to response
     */
    public String download()
    {

        HierarchyManager hm = MgnlContext.getHierarchyManager(MediaModule.REPO);

        Content media;
        try
        {
            media = hm.getContent(path);
        }
        catch (RepositoryException e)
        {
            log.error("Error downloading media " + path, e);
            return null;
        }

        MediaTypeConfiguration mtc = MediaConfigurationManager.getInstance().getMediaTypeConfigurationFromMedia(media);
        String url = mtc.getHandler().getUrl(media);

        String filename = mtc.getHandler().getFullFilename(media);

        this.response.addHeader("Content-Disposition", "attachment; filename=" + filename);
        try
        {
            this.request.getRequestDispatcher(url).forward(request, response);
        }
        catch (ServletException e)
        {
            log.error("Error downloading media " + path, e);
        }
        catch (IOException e)
        {
            log.error("Error downloading media " + path, e);
        }

        return null;
    }

    /**
     * Returns the path.
     * @return the path
     */
    public String getPath()
    {
        return path;
    }

    /**
     * Returns the medias.
     * @return the medias
     */
    public Collection<MediaBean> getMedias()
    {
        return medias;
    }

    /**
     * Sets the medias.
     * @param medias the medias to set
     */
    public void setMedias(Collection<MediaBean> medias)
    {
        this.medias = medias;
    }

    /**
     * Sets the path.
     * @param path the path to set
     */
    public void setPath(String path)
    {
        this.path = path;
    }

    /**
     * Returns the types.
     * @return the types
     */
    public Collection<MediaTypeConfiguration> getTypes()
    {
        return types;
    }

    /**
     * Sets the types.
     * @param types the types to set
     */
    public void setTypes(Collection<MediaTypeConfiguration> types)
    {
        this.types = types;
    }

    /**
     * Returns the type.
     * @return the type
     */
    public String getType()
    {
        return type;
    }

    /**
     * Sets the type.
     * @param type the type to set
     */
    public void setType(String type)
    {
        this.type = type;
    }

    /**
     * Returns the writable.
     * @return the writable
     */
    public boolean isWritable()
    {
        return writable;
    }

    /**
     * Sets the writable.
     * @param writable the writable to set
     */
    public void setWritable(boolean writable)
    {
        this.writable = writable;
    }

    /**
     * Returns the canPublish.
     * @return the canPublish
     */
    public boolean isCanPublish()
    {
        return canPublish;
    }

    /**
     * Sets the canPublish.
     * @param canPublish the canPublish to set
     */
    public void setCanPublish(boolean canPublish)
    {
        this.canPublish = canPublish;
    }

    /**
     * Returns the node.
     * @return the node
     */
    public String getNode()
    {
        return node;
    }

    /**
     * Sets the node.
     * @param node the node to set
     */
    public void setNode(String node)
    {
        this.node = node;
    }

    /**
     * Returns the dest.
     * @return the dest
     */
    public String getDest()
    {
        return dest;
    }

    /**
     * Sets the dest.
     * @param dest the dest to set
     */
    public void setDest(String dest)
    {
        this.dest = dest;
    }

    /**
     * Returns the selectMedia.
     * @return the selectMedia
     */
    public boolean isSelectMedia()
    {
        return selectMedia;
    }

    /**
     * Sets the selectMedia.
     * @param selectMedia the selectMedia to set
     */
    public void setSelectMedia(boolean selectMedia)
    {
        this.selectMedia = selectMedia;
    }

    /**
     * Returns the actMediaHandle.
     * @return the actMediaHandle
     */
    public String getActMediaHandle()
    {
        return actMediaHandle;
    }

    /**
     * Sets the actMediaHandle.
     * @param actMediaHandle the actMediaHandle to set
     */
    public void setActMediaHandle(String actMediaHandle)
    {
        this.actMediaHandle = actMediaHandle;
    }

    /**
     * Returns the mediaType.
     * @return the mediaType
     */
    public String getMediaType()
    {
        return mediaType;
    }

    /**
     * Sets the mediaType.
     * @param mediaType the mediaType to set
     */
    public void setMediaType(String mediaType)
    {
        this.mediaType = mediaType;
    }

    /**
     * Returns the numberOfMedia.
     * @return the numberOfMedia
     */
    public Map<String, Integer> getNumberOfMedia()
    {
        return numberOfMedia;
    }

    /**
     * Sets the numberOfMedia.
     * @param numberOfMedia the numberOfMedia to set
     */
    public void setNumberOfMedia(Map<String, Integer> numberOfMedia)
    {
        this.numberOfMedia = numberOfMedia;
    }

    /**
     * Returns the develop.
     * @return the develop
     */
    public boolean isDevelop()
    {
        return develop;
    }

    /**
     * Sets the develop.
     * @param develop the develop to set
     */
    public void setDevelop(boolean develop)
    {
        this.develop = develop;
    }

    /**
     * Returns the search.
     * @return the search
     */
    public String getSearch()
    {
        return search;
    }

    /**
     * Sets the search.
     * @param search the search to set
     */
    public void setSearch(String search)
    {
        this.search = search;
    }

    public boolean isSingleInstance()
    {
        return MediaEl.module().isSingleinstance();
    }

    public String getBgSelector()
    {
        for (Cookie cookie : request.getCookies())
        {
            if ("bgselector".equals(cookie.getName()))
            {
                return cookie.getValue();
            }
        }
        return "white";
    }

    public String getMessage(String key, String param)
    {
        return getMsgs().get(key, new String[]{param });
    }
}
