package com.atlassian.confluence.extra.graphviz;

import com.atlassian.renderer.v2.macro.MacroException;
import com.atlassian.renderer.v2.SubRenderer;
import com.atlassian.renderer.RenderContext;

import java.util.regex.Matcher;
import java.util.regex.Pattern;

public class GraphVizHelper
{
    private static int imageMapId = 0;

    /**
     * Take a String of dot commands and render a PNG version of the graph to a temp file, return HTML
     * to retrieve the file, or to report errors encountered processing the dot commands.
     *
     * @param dotCommands String of dot commands.
     * @param startLine   the number of lines at the start to ignore when printing error annotations.
     * @param endSkip     the number of lines at the end to ignore when printing error annotations.
     * @return String of HTML.
     * @throws MacroException when an error which can't usefully be explained to the user is encountered.
     */
    public static String generateGraph(String dotCommands, int startLine, int endSkip, SubRenderer subRenderer, RenderContext context) throws MacroException
    {
        String imageMap = "";
        String mapName = null;
        if (dotCommands.indexOf("URL=") != -1)
        {
            dotCommands = evaluateLinks(dotCommands, subRenderer, context);
            mapName = "spaceGraph" + imageMapId++;
            imageMap = generateImageMap(dotCommands);
            imageMap = imageMap.replaceAll("_anonymous_0", mapName) + "\n";
        }
        DotRunner dotRunner = new DotRunner("png", dotCommands, true);
        if (dotRunner.getExitCode() != 0)
        {
            String error = dotRunner.getError();
            if (error.indexOf("syntax error near line") != -1)
            {
                // parse the error message
                String[] bits = error.split(":");
                int lineNo = Integer.parseInt(bits[2]) - 1;
                String[] lines = dotCommands.split("\n");
                String message = "Syntax Error: " + bits[4] + "<br/>\n";
                for (int i = startLine; i < lines.length - endSkip; ++i)
                {
                    if (i == lineNo)
                    {
                        message += "<span style=\"background-color: #FF8080\">";
                    }
                    message += lines[i];
                    if (i == lineNo)
                    {
                        message += "</span>";
                    }
                    message += "</br>\n";
                }
                error = message;
            }
            return "<div class=errorBox>\n" + error + "\n</div>";
        }
        else
        {
            return imageMap + "<img src='"
                    + dotRunner.getOutputUrl() + "'" + (mapName != null ? " usemap=\"#" + mapName + "\"" : "") + "/>";
        }
    }

    private static String evaluateLinks(String dotCommands, SubRenderer subRenderer, RenderContext context)
    {
        Pattern linkPattern = Pattern.compile("URL=\"(\\[[^\\]]+\\])\"");
        Pattern hrefPattern = Pattern.compile("href=\"([^\"]+)\"");
        //Pattern titlePattern = Pattern.compile("title=\"([^\"]+)\"");
        Matcher m = linkPattern.matcher(dotCommands);
        StringBuffer sb = new StringBuffer();
        while (m.find())
        {
            String group = m.group(1);
            String linkHtml = subRenderer.render(group,context);
            Matcher m2 = hrefPattern.matcher(linkHtml);
            String replacement = group;
            if (m2.find())
            {
                replacement = "URL=\"" + m2.group(1) + "\"";
            }
            /*m2 = titlePattern.matcher(linkHtml);
            if (m2.find())
            {
                replacement += ", label=\"" + m2.group(1) + "\"";
            }*/
            m.appendReplacement(sb, replacement);
        }
        m.appendTail(sb);
        return sb.toString();  //To change body of created methods use File | Settings | File Templates.
    }

    private static String generateImageMap(String dotCommands) throws MacroException
    {
        DotRunner dotRunner = new DotRunner("cmapx", dotCommands, false);
        if (dotRunner.getExitCode() != 0)
        {
            throw new MacroException("Failed to create image map:" + dotRunner.getError());
        }
        else
        {
            return dotRunner.getOutput();
        }
    }
}
