package com.atlassian.confluence.extra.masterdetail;

import com.atlassian.renderer.v2.macro.MacroException;
import com.atlassian.renderer.v2.RenderMode;
import com.atlassian.renderer.v2.RenderUtils;
import com.atlassian.renderer.RenderContext;
import com.atlassian.confluence.util.LabelUtil;
import com.atlassian.confluence.util.GeneralUtil;
import com.atlassian.confluence.util.VelocityUtils;
import com.atlassian.confluence.renderer.PageContext;
import com.atlassian.confluence.renderer.radeox.macros.MacroUtils;
import com.atlassian.confluence.core.SpaceContentEntityObject;
import com.atlassian.confluence.core.ContentEntityObject;
import com.atlassian.confluence.labels.Label;
import com.atlassian.confluence.labels.Namespace;
import com.opensymphony.util.TextUtils;

import java.util.*;

import org.apache.velocity.VelocityContext;
import org.apache.velocity.exception.ResourceNotFoundException;

public class DetailsMacro extends AbstractMasterDetailsMacro
{

    public boolean isInline()
    {
        return false;
    }

    public boolean hasBody()
    {
        return true;
    }

    public RenderMode getBodyRenderMode()
    {
        return RenderMode.NO_RENDER;
    }

    public String execute(Map parameters, String body, RenderContext renderContext) throws MacroException
    {
        String labelName = (String) parameters.get("label");

        if (!TextUtils.stringSet(labelName))
            return RenderUtils.blockError("No 'label' value supplied for the details macro.", renderErrorBody(body, renderContext));

        if (!LabelUtil.isValidLabelName(labelName))
            return RenderUtils.blockError(GeneralUtil.escapeXml(labelName) + " is not a valid label name", renderErrorBody(body, renderContext));

        ContentEntityObject owner = ((PageContext)renderContext).getEntity();

        if (!(owner instanceof SpaceContentEntityObject))
            return RenderUtils.blockError("Details can only be used on pages or blog posts", renderErrorBody(body, renderContext));


        // ensure the page has the label so we can find it later
        if (!isAlreadyLabeled(owner, labelName))
            labelManager.addLabel(owner, new Label(labelName));

        List details = toDetails(body, renderContext);
        if (details.size() > 0)
            contentPropertyManager.setTextProperty(owner, "masterdetail." + labelName, body);
        else
            contentPropertyManager.setTextProperty(owner, "masterdetail." + labelName, null);

        if (!"true".equals(parameters.get("hidden")))
            return renderDetailsPanel(details, body, renderContext);
        else
            return "";
    }

    private String renderDetailsPanel(List details, String body, RenderContext renderContext)
    {
        Map contextMap = MacroUtils.defaultVelocityContext();
        contextMap.put("details", details);

        try
        {
            return VelocityUtils.getRenderedTemplateWithoutSwallowingErrors("/templates/extra/masterdetail/details.vm", new VelocityContext(contextMap));
        }
        catch (ResourceNotFoundException e)
        {
            return RenderUtils.blockError("System error: could not find template /templates/extra/masterdetail/details.vm", renderErrorBody(body, renderContext));
        }
        catch (Exception e)
        {
            return RenderUtils.blockError("Unknown error drawing details " + e.getMessage(), renderErrorBody(body, renderContext));
        }
    }

    private boolean isAlreadyLabeled(ContentEntityObject owner, String labelName)
    {
        boolean isAlreadyLabeled = false;
        for (Iterator it = owner.getLabels().iterator(); it.hasNext();)
        {
            Label label = (Label) it.next();
            if (Namespace.GLOBAL.equals(label.getNamespace()) && labelName.equals(label.getName()))
                isAlreadyLabeled = true;
        }
        return isAlreadyLabeled;
    }

}
