package com.atlassian.confluence.extra.attachments;

import com.atlassian.confluence.core.ContentEntityObject;
import com.atlassian.confluence.pages.Attachment;
import com.atlassian.confluence.pages.AttachmentManager;
import com.atlassian.confluence.renderer.PageContext;
import com.atlassian.confluence.renderer.radeox.macros.MacroUtils;
import com.atlassian.confluence.util.*;
import com.atlassian.confluence.user.AuthenticatedUserThreadLocal;
import com.atlassian.confluence.security.PermissionManager;
import com.atlassian.renderer.RenderContext;
import com.atlassian.renderer.v2.RenderMode;
import com.atlassian.renderer.v2.macro.BaseMacro;
import com.atlassian.renderer.v2.macro.MacroException;
import com.atlassian.user.User;
import com.opensymphony.util.TextUtils;
import com.opensymphony.webwork.ServletActionContext;
import org.apache.log4j.Category;

import javax.servlet.http.HttpServletRequest;
import java.util.*;
import java.util.regex.Pattern;

/**
 * Implements the {attachments} macro.
 */
public class AttachmentsMacro extends BaseMacro
{
    private static final Category log = Category.getInstance(AttachmentsMacro.class);
    private AttachmentManager attachmentManager;
    private PermissionManager permissionManager;

    public boolean isInline()
    {
        return false;
    }

    public boolean hasBody()
    {
        return false;
    }

    // irrelevant since we have no body
    public RenderMode getBodyRenderMode()
    {
        return RenderMode.NO_RENDER;
    }

    /**
     * Process a request to render an {attachments} macro
     *
     * @param parameters
     * @param body
     * @param renderContext
     * @return
     * @throws MacroException
     */
    public String execute(Map parameters, String body, RenderContext renderContext) throws MacroException
    {
        // retrieve a reference to the body object this macro is in
        ContentEntityObject contentObject = ((PageContext) renderContext).getEntity();
        String sortBy = (String) parameters.get("sortBy");

        // check if any sortBy parameters were set by the request if available.
        final HttpServletRequest request = ServletActionContext.getRequest();

        if (request != null && contentObject != null)
        {
            String requestSortBy = request.getParameter("sortBy");
            if (requestSortBy != null)
                sortBy = requestSortBy;
        }

        List filteredList = new ArrayList();
        // If the entities id == 0, the page object was newly created and is not persisted yet. Therefore does not have any attributes.
        if (contentObject.getId() != 0)
        {
            // the old versions are retrieved by the template, so we only want the latest versions in this list
//            List attachments = attachmentManager.getLatestVersionsOfAttachments(contentObject);
            List attachments = getLatestVersionsOfAttachments(contentObject, sortBy);
            String fileNamePatterns = (String) parameters.get("patterns"); // TODO what should this parameter be called?
            if (fileNamePatterns != null)
            {
                String[] patterns = fileNamePatterns.split(",");
                for (int i = 0; i < patterns.length; ++i)
                {
                    Pattern p = Pattern.compile(patterns[i].trim());
                    for (Iterator j = attachments.iterator(); j.hasNext();)
                    {
                        Attachment a = (Attachment) j.next();
                        if (p.matcher(a.getFileName()).matches())
                        {
                            filteredList.add(a);
                        }
                    }
                }
            }
            else
            {
                filteredList = attachments;
            }
        }

        User remoteUser = AuthenticatedUserThreadLocal.getUser();
        // now create a simple velocity context and render a template for the output
        Map contextMap = MacroUtils.defaultVelocityContext();
        contextMap.put("latestVersionsOfAttachments", filteredList);
        contextMap.put("hasAttachFilePermissions", Boolean.valueOf(permissionManager.hasCreatePermission(remoteUser, contentObject, Attachment.class)));
        contextMap.put("page", contentObject);

        // the reference to this macro allows the template to call getAttachmentDetails() (see below)
        contextMap.put("macro", this);

        contextMap.put("old", getBooleanParameter(parameters, "old", false));
        contextMap.put("upload", getBooleanParameter(parameters, "upload", false));
        contextMap.put("max", new Integer(5));
        contextMap.put("remoteUser", remoteUser);

        try
        {
            return VelocityUtils.getRenderedTemplate("templates/extra/attachments/attachmentsmacro.vm", contextMap);
        }
        catch (Exception e)
        {
            log.error("Error while trying to display Attachments!", e);
            throw new MacroException(e.getMessage());
        }
    }

    private Boolean getBooleanParameter(Map parameters, String name, boolean defaultValue)
    {
        String parameterValue = (String) parameters.get(name);
        if (parameterValue == null)
        {
            return Boolean.valueOf(defaultValue);
        }
        else
        {
            return Boolean.valueOf(parameterValue);
        }
    }

     public List getLatestVersionsOfAttachments(ContentEntityObject contentObject, String sortBy)
    {
        List latestVersionsOfAttachments = attachmentManager.getLatestVersionsOfAttachments(contentObject);

        if (TextUtils.stringSet(sortBy))
        {
            if ("date".equals(sortBy))
            {
                Collections.sort(latestVersionsOfAttachments, new AttachmentDateComparator());
                Collections.reverse(latestVersionsOfAttachments); // latest attachments first
            }
            else if ("size".equals(sortBy))
            {
                Collections.sort(latestVersionsOfAttachments, new AttachmentSizeComparator());
                Collections.reverse(latestVersionsOfAttachments); // latest attachments first
            }
            else if ("name".equals(sortBy))
            {
                Collections.sort(latestVersionsOfAttachments, new AttachmentNameComparator());
            }
        }
        return latestVersionsOfAttachments;
    }

    /**
     * Copied from ViewAttachmentsAction
     */
    public String[] getAttachmentDetails(Attachment attachment)
    {
        return new String[]{GeneralUtil.escapeXml(attachment.getFileName()), String.valueOf(attachment.getVersion())};
    }

    public void setAttachmentManager(AttachmentManager attachmentManager)
    {
        this.attachmentManager = attachmentManager;
    }

    public void setPermissionManager(PermissionManager permissionManager)
    {
        this.permissionManager = permissionManager;
    }
}
