package org.pmdreports.report;

import java.io.BufferedWriter;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.OutputStreamWriter;
import java.io.Writer;
import java.text.DateFormat;
import java.util.Collection;
import java.util.Date;
import java.util.List;

import org.apache.log4j.Logger;
import org.pmdreports.manager.ConfigManager;
import org.pmdreports.model.Ruleset;
import org.pmdreports.util.PMDReportsException;

/**
 * Base AbstractReport to generate a basic HTML report.
 * 
 * @author Ben Northrop Aug 22, 2007
 */
public abstract class AbstractReport implements Report
{

    // DATA MEMBERS
    private final static Logger LOG = Logger.getLogger(AbstractReport.class);

    /**
     * Writer used to generate report.
     */
    private Writer writer;

    private final ConfigManager configManager = ConfigManager.getInstance();

    // CONSTRUCTOR
    /**
     * Default constructor, writer is initialized.
     */
    public AbstractReport()
    {
        initWriter();
    }

    // ABSTRACT METHODS

    protected abstract void writeReport(Writer writer) throws IOException;

    // METHODS
    /**
     * Initialize the writer and output files.
     */
    private void initWriter()
    {
        final String filePath = configManager.getOutputDir() + getFileName();
        LOG.debug("Writing to report to: " + filePath);
        try {
            FileOutputStream outputFile = new FileOutputStream(filePath);
            OutputStreamWriter osWriter = new OutputStreamWriter(outputFile);
            writer = new BufferedWriter(osWriter);
        } catch (IOException ioe) {
            throw new PMDReportsException("Could not open writer: " + filePath,
                    ioe);
        }
    }

    /**
     * Generate the report - template method (GoF).
     */
    public void generate(Collection<Report> reports)
    {
        try {
            initWriter();
            writeTop(writer, getReportTitle());
            writer
                    .write("<table width='100%'><tr><td align='200' valign='top'>");
            writeReportLinks(writer, reports);
            writer.write("</td>");
            writer.write("<td width='20'></td>");
            writer.write("<td width='*' align='left' valign='top'>");
            writer
                    .write("<table width='100%'><tr><td align='left' valign='top'>");
            writeReport(writer);
            writer.write("</td></tr></table>");
            writer.write("</td></tr></table>");
            writeBottom(writer);

            writer.flush();
        } catch (IOException ioe) {
            throw new PMDReportsException("Could not write out page!", ioe);
        } finally {
            closeWriter();
        }
    }

    /**
     * Write out links to the other reports.
     */
    private void writeReportLinks(Writer writer, Collection<Report> reports)
            throws IOException
    {
        writer
                .write("<table width='200' cellspacing='0' cellpadding='0' class='reportLinks'>");
        writer.write("<tr>");
        writer.write("  <th>&nbsp;Reports</th>");
        writer.write("</tr>");
        // Project Reports
        writer.write("<tr><td><b>&nbsp;Project</b></td></tr>");
        for (Report report : reports) {
            if (report.isProjectReport()) {
                writeReportLink(writer, report);
            }
        }

        // Component Reports
        writer.write("<tr><td><b>&nbsp;Component</b></td></tr>");
        for (Report report : reports) {
            if (report.isComponentReport()) {
                writeReportLink(writer, report);
            }
        }
        writer.write("</table><br>");
    }

    /**
     * Write out the link for one of the reports.
     */
    private void writeReportLink(Writer writer, Report report)
            throws IOException
    {
        writer.write("  <tr><td width='' valign='top'");
        if (report.getFileName().equals(this.getFileName())) {
            writer.write("class='current'>&nbsp;&nbsp;&nbsp;");
        } else {
            writer.write(">&nbsp;&nbsp;&nbsp;<a href='" + report.getFileName()
                    + "'>");
        }
        writer.write(report.getReportTitle());
        writer.write("</a></td></tr>");
    }

    /**
     * Flush the writer and then close it.
     */
    private void closeWriter()
    {
        try {
            if (writer == null) {
                LOG.warn("Could not close null writer");
            } else {
                writer.close();
            }
        } catch (IOException ioe) {
            throw new PMDReportsException("Could not close file!", ioe);
        }

    }

    /**
     * Write out the top of the HTML portal screen.
     */
    private void writeTop(Writer writer, final String title) throws IOException
    {
        writer.write("<html>");
        writer.write("<head>");
        writer.write("<title>");
        writer.write("PMDReports: " + title);
        writer.write("</title>");
        writeStyle(writer);
        writer.write("</head>");
        writer.write("<body>");
        writer.write("<table width=\"100%\" class='title'>");
        writer.write("  <tr>");
        writer.write("   <td width=\"70%\" style=\"text-align: left;\">");
        writer.write("    <h1>&nbsp;PMD Reports</h1></td>");
        writer
                .write(" <td width=\"30%\" style=\"text-align: left;\">Generated on ");
        Date now = new Date();
        DateFormat df = DateFormat.getDateInstance(DateFormat.FULL);
        writer.write(df.format(now));
        writer.write("</td>");
        writer.write("</tr>");
        writer.write("</table><br>");
    }

    /**
     * Helper to write out css style for the report.
     * 
     * TODO: externalize this!
     */
    protected static void writeStyle(Writer writer) throws IOException
    {
        writer.write("<style TYPE=\"text/css\">");
        writer
                .write("TD { color: black); font-family: verdana; font-size: small; }");
        writer
                .write("TABLE.title { width: 100%; color: white; border-style: solid; border-width: 0px 0px 0px 0px; border-color: #333333; background-color: #333333; }");
        writer.write("TABLE.title A { color: white; }");
        writer
                .write("TABLE.reportLinks { padding: 0px; width=100%; border-style: solid; border-width: 1px 1px 1px 1px; border-color: #333333; }");

        writer
                .write("TABLE.reportLinks TH { text-align: left; padding: 2px; font-size: medium; font-weight: bold; background-color: #333333; }");
        writer
                .write("TABLE.reportLinks TD { text-align: left; padding: 2px; font-size: small; font-weight: normal; }");
        writer.write("TABLE.reportLinks TR { height: 30px; }");
        writer
                .write("TABLE.reportLinks TD.current { color: white; padding: 0px; background-color: #cccccc; font-color: white; }");
        writer
                .write("TABLE.statistics {  border-width: 0px; border-color: #666666; border-spacing: 1px; padding: 1px; border-style: outset; }");
        writer
                .write("TABLE.statistics TD { padding: 2px 5px 2px 5px; font-family: verdana; font-size: small; vertical-align: middle; text-align: right; }");
        writer
                .write("TABLE.statistics TD.component { background-color: #333333; color: gainsboro; text-align: left; font-family: small; font-weight: ; font-style: ;}");
        writer
                .write("TABLE.statistics TD.normal {  background-color: #cccccc;}");
        writer
                .write("TABLE.statistics TH.componentTitle { background-color: white; color: #333333; text-align: left; font-size: medium; font-weight: bold; }");
        writer.write("TABLE.graph { padding: 0px; border-width: 0px;  }");
        writer.write("TABLE.graph TD { padding: 0px; border-width: 0px; }");

        writer
                .write("TABLE.bar { padding: 0px; border-width: 0px; background-color: #cccccc; }");
        writer.write("TABLE.bar TD { padding: 0px; border-width: 0px;  }");
        writer.write(".line {  background-color: black;   }");
        writer.write("A:link    { color: darkblue; text-decoration: none; }");
        writer.write("A:visited { color: darkblue; text-decoration: none; }");
        writer
                .write("A:hover   { color: darkblue; text-decoration: underline;  }");

        writer
                .write("A:link.columnHeader    { color: white; text-decoration: none; }");
        writer
                .write("A:visited.columnHeader { color: white; text-decoration: none; }");
        writer
                .write("A:hover.columnHeader   { color: white; text-decoration: underline;  }");
        writer
                .write("A:link.highValue { color: white; background-color: #C11B17;   }");
        writer
                .write("A:visited.highValue { color: white; background-color: #C11B17;   }");
        writer
                .write("A:hover.highValue { color: white; background-color: #C11B17; text-decoration: underline;   }");
        writer
                .write("A:link.lowValue { color: white; background-color: #347235;  }");
        writer
                .write("A:visited.lowValue { color: white; background-color: #347235;  }");
        writer
                .write("A:hover.lowValue { color: white; background-color: #347235; text-decoration: underline;  }");
        writer
                .write("A:link.normalValue { color: darkblue; background-color: #cccccc;  }");
        writer
                .write("A:visited.normalValue { color: darkblue; background-color: #cccccc;  }");
        writer
                .write("A:hover.normalValue { color: darkblue; background-color: #cccccc; text-decoration: underline; }");
        writer
                .write("H1 { color: white; font-family: verdana; font-size: large; }");
        writer
                .write(".title H1 A { color: white; font-family: verdana; font-size: large; }");
        writer
                .write("H2 { color: #333333; font-family: verdana; font-size: medium; font-weight: bold; }");
        writer
                .write("BODY { background-color: ; font-size: small;  font-family: verdana; margin-top: 1; padding-top: 9; }");
        writer.write("LI { padding: 2px; }");
        writer
                .write("TH { background-color:  #333333; color: white; font-family: verdana; font-size: small; font-weight: normal; text-align: right; padding: 5px 5px 5px 5px; }");
        writer
                .write(".highValue { color: white; background-color: #C11B17;   }");
        writer.write(".lowValue { color: white; background-color: #347235;  }");
        writer
                .write(".normalValue { color: black; background-color: #cccccc; }");
        writer.write(".locBar { background-color: #000066; }");
        writer.write(".violationsBar { background-color: #C11B17; }");
        writer.write(".spacer { background-color: #cccccc; }");
        writer.write("</style>");
    }

    /**
     * Write the bottom of the HTML portal screen.
     */
    protected static void writeBottom(Writer writer) throws IOException
    {
        writer.write("</table>");
        writer.write("</body></html>");
    }

}
