package com.horstmann.labrat;

import org.apache.tools.ant.*;
import java.util.*;
import java.io.*;

public class Report implements BuildListener
// TODO: SubBuildListener
{
    public void buildFinished(BuildEvent event)
    {
        finished = true;
        Project p = event.getProject();
        report = createReport(p);
        String filename = p.getProperty("report.file");
        if (filename == null)
            p.log(report.toString());
        else
            try
            {
                reportFile = new File(filename);
                PrintStream out = new PrintStream(reportFile);
                out.append(report);
                out.close();
                p.log("Report saved in " + reportFile.getCanonicalPath());
            }
            catch (IOException ex)
            {
                p.log("" + ex);
            }
    }

    public StringBuilder createReport(Project p)
    {
        double total = 0;
        for (Detail d : details)
        {
            String name = d.target.getName();
            String score;
            if (d.failed)
                score = p.getProperty(name + ".fail.score");
            else
                score = p.getProperty(name + ".pass.score");
            if (score == null)
                score = "0";
            p.setProperty(name + ".score", score);
            try
            {
                total += Double.parseDouble(score);
            }
            catch (NumberFormatException ex)
            {
            }
        }
        p.setProperty("report.score", "" + total);
        StringBuilder r = new StringBuilder();
        int section = 1;
        while (true)
        {
            int length = r.length();
            appendSection(r, p, "" + section);
            if (length == r.length()) // nothing added
                return r;
            section++;
        }
    }

    public void appendSection(StringBuilder r, Project p, String section)
    {
        append(r, p, "report." + section + ".start");
        double total = 0;
        for (Detail d : details)
        {
            String name = d.target.getName();
            p.setProperty("target.name", name);
            p.setProperty(name + ".message", "" + d.message);
            p.setProperty("target.message", "" + d.message);
            p.setProperty(name + ".exception", d.exception == null ? "" : "" + d.exception);
            p.setProperty("target.exception", d.exception == null ? "" : "" + d.exception);
            String score;
            if (d.failed)
            {
                score = p.getProperty(name + ".fail.score");
                p.setProperty("target.status", "fail");
            }
            else
            {
                score = p.getProperty(name + ".pass.score");
                p.setProperty("target.status", "pass");
            }
            if (score == null)
                score = "0";
            p.setProperty("target.score", score);
            p.setProperty(name + ".score", score);
            try
            {
                total += Double.parseDouble(score);
            }
            catch (NumberFormatException ex)
            {
            }
            append(r, p, "report." + section + ".target");
            append(r, p, name + "." + section + ".start");
            if (d.failed)
                append(r, p, name + "." + section + ".fail", "report."
                        + section + ".fail");
            else
                append(r, p, name + "." + section + ".pass", "report."
                        + section + ".pass");
            append(r, p, name + ".end");
        }
        p.setProperty("target.score", "" + total);
        append(r, p, "report." + section + ".end");
    }

    private static void append(StringBuilder r, Project p, String name)
    {
        String value = p.getProperty(name);
        if (value != null)
        {
            r.append(p.replaceProperties(value));
            r.append('\n');
        }
    }

    private static void append(StringBuilder r, Project p, String primary,
            String fallback)
    {
        String value = p.getProperty(primary);
        if (value != null)
        {
            r.append(p.replaceProperties(value));
            r.append('\n');
        }
        else
            append(r, p, fallback);
    }

    public void buildStarted(BuildEvent event)
    {
        report = null;
        reportFile = null;
        details = new ArrayList<Detail>();
    }

    public void messageLogged(BuildEvent event)
    {
        if (finished)
            return;
        if (currentDetail != null && currentDetail.target == event.getTarget()
                && event.getPriority() <= Project.MSG_INFO)
        {
            String message = event.getMessage();
            if (message != null)
            {
                currentDetail.message.append(message);
                currentDetail.message.append('\n');
            }
        }
    }

    public void targetFinished(BuildEvent event)
    {
        if (currentDetail == null) return;
        Target target = event.getTarget();
        assert target == currentDetail.target;
        currentDetail.exception = event.getException();
        if (currentDetail.exception != null)
            currentDetail.failed = true;
        String fail = target.getProject().getProperty(target.getName() + ".fail");
        if (fail != null) 
        {
           currentDetail.failed = true; // fail and continue 
           currentDetail.message.append(fail);
           currentDetail.message.append('\n');
        }
        String message = event.getMessage();
        if (message != null)
        {
            currentDetail.message.append(message);
            currentDetail.message.append('\n');
        }
        currentDetail = null;
    }

    public void targetStarted(BuildEvent event)
    {
        Target target = event.getTarget();
        String name = target.getName();
        Project project = target.getProject();
        
        // don't report on targets that have no score
        
        if (project.getProperty(name + ".pass.score") == null && 
                project.getProperty(name + ".fail.score") == null) return;
        
        // don't report on targets that are skipped
        
        String ifCondition = target.getIf();
        if (ifCondition != null && project.getProperty(ifCondition) == null)
            return;
        String unlessCondition = target.getUnless();
        if (unlessCondition != null && project.getProperty(unlessCondition) != null)
            return;
        
        currentDetail = new Detail(target);
        details.add(currentDetail);
    }

    public void taskFinished(BuildEvent event)
    {
        /*
         * Task task = event.getTask(); if (task instanceof UnknownElement) {
         * RuntimeConfigurable wrapper = ((UnknownElement) task).getWrapper();
         * Object proxy = wrapper.getProxy(); if
         * (proxy.getClass().getName().equals(Check.class.getName())) { log("" +
         * wrapper.getAttributeMap()); log("" + event.getException()); } }
         */
    }

    public void taskStarted(BuildEvent event)
    {
    }

    public File getReportFile()
    {
        return reportFile;
    }

    private boolean finished;
    private Detail currentDetail;
    private ArrayList<Detail> details;
    private File reportFile;
    private StringBuilder report;

    private static class Detail
    {
        public Detail(Target target)
        {
            this.target = target;
        }

        public Target target;
        public StringBuilder message = new StringBuilder();
        public boolean failed;
        public Throwable exception;

        public String toString()
        {
            return getClass().getName() + "[target=" + target.getName()
                    + ",message=" + message + ",failed=" + failed
                    + ",exception=" + exception + "]";
        }
    }
}
