/**
 * Copyright (c) PMDReports, Ben Northrop 2007
 */
package org.pmdreports.report;

import java.io.IOException;
import java.io.Writer;
import java.util.Collection;
import java.util.Date;

import org.pmdreports.model.Statistics;

/**
 * An abstract Graph for others to descend from.
 * 
 * @author Ben Northrop, Dec 17, 2007 2:04:26 PM
 */
public abstract class AbstractGraph extends AbstractReport {

	// DATA MEMBERS
	private long height;

	private long width;

	private long positiveHeight;

	private long negativeHeight;

	private long minLOC;

	private long maxLOC;

	private long initialLOC;

	private double minViolations;

	private double maxViolations;

	private double initialViolations;

	// METHODS
	protected void writeNoGraph(Writer writer) throws IOException {
		writer
				.write("Need more than one data point to generate trend graph.  Run PMDReports again tomorrow after changes have been made to see trend.");
	}

	protected void initDimensions(long initialLOC, double initialViolations) {
		this.initialLOC = initialLOC;
		this.initialViolations = initialViolations;
	}

	protected void initDimensions(int width, int height, long maxLOC,
			double maxViolations) {
		this.maxLOC = maxLOC;
		this.maxViolations = maxViolations;
		this.width = width;
		this.height = height - 2;
		this.positiveHeight = height;
		this.negativeHeight = 0;
	}

	protected void initDimensions(int width, int height, long locMin,
			long locMax, double violationsMin, double violationsMax) {
		this.width = width;
		this.height = height - 2;
		this.minLOC = locMin;
		this.maxLOC = locMax;
		this.minViolations = violationsMin;
		this.maxViolations = violationsMax;
		initHeights(height, violationsMin, violationsMax);
	}

	protected void initDimensions(int width, int height, long locMin,
			long locMax, long locInitial, double violationsMin,
			double violationsMax, double violationsInitial) {
		this.width = width;
		this.height = height - 2;
		this.minLOC = locMin;
		this.maxLOC = locMax;
		this.initialLOC = locInitial;
		this.minViolations = violationsMin;
		this.maxViolations = violationsMax;
		this.initialViolations = violationsInitial;
		initHeights(height, violationsMin, violationsMax);
	}

	/**
	 * Use the violations to initialize the y-axis...but make sure to use enough
	 * space in case there is no positive movement for violations (which would
	 * make the code size look like no gain).
	 */
	private void initHeights(int height, double violationsMin,
			double violationsMax) {
		this.positiveHeight = Math.abs(Math
				.round((violationsMax / (violationsMax + Math
						.abs(violationsMin)))
						* (this.height - 60))) + 30;
		this.negativeHeight = height - positiveHeight + 30;
	}

	protected void initDimensions(int width, int height,
			double violationsPerLineMax) {
		this.width = width;
		this.height = height - 2;
		this.maxViolations = violationsPerLineMax;
		this.positiveHeight = height;
		this.negativeHeight = 0;
	}

	/**
	 * Write out the graph bar.
	 */
	protected void writeLeftYAxis(Writer writer) throws IOException {
		int halfHeight = Math.round(this.positiveHeight / 2);
		writer.write("<td valign='top' align='right'>");
		writer.write("<table cellspacing='0' cellpadding='0'>");
		writer.write("<tr height='" + halfHeight + "'>");
		writer.write("<td valign='top' align='right'>&nbsp;");
		writer.write(ReportUtilities.DEFAULT_FORMATTER.format(maxLOC
				+ initialLOC));
		writer.write(" LOC&nbsp;");
		writer.write("</td>");
		writer.write("</tr>");
		writer.write("<tr height='" + halfHeight + "'>");
		writer.write("<td valign='bottom' align='right'>");
		writer.write(ReportUtilities.DEFAULT_FORMATTER.format(initialLOC));
		writer.write(" LOC&nbsp;</td>");
		writer.write("</tr>");
		writer.write("</table>");
		writer.write("</td>");
		writer.write("<td width='1' class='line'></td>");
	}

	/**
	 * Write out the graph bar.
	 */
	protected void writeRightYAxis(Writer writer) throws IOException {
		int halfHeight = Math.round(this.positiveHeight / 2);
		writer.write("<td width='1' class='line'></td>");
		writer.write("<td valign='top'>");
		writer.write("  <table cellspacing='0' cellpadding='0'>");
		writer.write("    <tr height='" + halfHeight + "'>");
		writer.write("      <td valign='top' align='left'>&nbsp;");
		writer.write(ReportUtilities.DEFAULT_FORMATTER.format(maxViolations
				+ initialViolations));
		writer.write(" violations");
		writer.write("      </td>");
		writer.write("    </tr>");
		writer.write("    <tr height='" + halfHeight + "'>");
		writer.write("      <td valign='bottom' align='left'>&nbsp;");
		writer.write(ReportUtilities.DEFAULT_FORMATTER
				.format(initialViolations));
		writer.write("      violations</td>");
		writer.write("    </tr>");
		writer.write("  </table>");
		writer.write("</td>");
	}

	/**
	 * Write out the graph bar.
	 */
	protected void writeBar(Writer writer, double value, boolean isLOC)
			throws IOException {

		String css = isLOC ? "locBar" : "violationsBar";
		String unit = isLOC ? "Lines of Code" : "Violations";
		if (value >= 0) {
			double percent = value / (isLOC ? maxLOC : maxViolations);
			long barHeight = Math.abs(Math.round(percent * positiveHeight));
			long whiteHeight = positiveHeight - barHeight;
			writer.write("<td width='" + width
					+ "'><table cellspacing='0' cellpadding='0'");
			writer.write(" class='bar' width='" + width + "'>");
			writer.write("     <tr height='1'><td class='line'></td></tr>");
			if (whiteHeight != 0) {
				writer.write("<tr height='");
				writer.write(Long.toString(whiteHeight));
				writer.write("'><td width='" + this.width
						+ "' class='spacer'></td>");
				writer.write("</tr>");
			}
			if (barHeight != 0) {
				writer.write("<tr height='");
				writer.write(Long.toString(barHeight));
				writer.write("'><td title='"
						+ ReportUtilities.DEFAULT_FORMATTER.format(value) + " "
						+ unit + "' class='");
				writer.write(css);
				writer.write("'></td></tr>");
			}
			writer.write("<tr height='1'><td class='line'></td></tr>");
			writer.write("<tr height='" + negativeHeight + "'><td></td></tr>");
			writer.write("</table></td>");
		} else {
			double percent = value / (isLOC ? minLOC : minViolations);
			long barHeight = Math.abs(Math.round(percent * negativeHeight));
			long whiteHeight = negativeHeight - barHeight;

			writer.write("<td width='" + width
					+ "'><table  cellspacing='0' cellpadding='0'");
			writer.write("   class='bar' width='" + width + "'>");
			writer.write("    <tr height='1'><td class='line'></td></tr>");
			writer.write("    <tr height='" + positiveHeight + "'>");
			writer.write("      <td></td>");
			writer.write("    </tr>");
			writer.write("    <tr height='1'><td class='line'></td></tr>");
			if (barHeight != 0) {
				writer.write("    <tr height='" + Long.toString(barHeight)
						+ "'>");
				writer.write("      <td ");
				writer.write("         title='"
						+ ReportUtilities.DEFAULT_FORMATTER.format(value));
				writer.write(unit + "' ");
				writer.write("         class='" + css + "'");
				writer.write("      ></td>");
				writer.write("    </tr>");
			}
			if (whiteHeight != 0) {
				writer.write("    <tr height='" + Long.toString(whiteHeight)
						+ "'>");
				writer.write("      <td width='" + width + "' class='spacer'>");
				writer.write("      </td>");
				writer.write("    </tr>");
			}
			writer.write("  </table></td>");
		}

	}

	protected void writeSpacer(Writer writer) throws IOException {
		writer.write("<td width='10'>");
		writer.write(" <table cellspacing='0' cellpadding='0' class='spacer'>");
		writer.write("     <tr height='1'><td class='line'></td></tr>");
		writer.write("   <tr height='" + positiveHeight + "'>");
		writer.write("     <td></td>");
		writer.write("   </tr>");
		writer
				.write("   <tr height='1'><td width='10' class='line'></td></tr>");
		writer.write("   <tr height='" + negativeHeight + "'>");
		writer.write("     <td></td>");
		writer.write("   </tr>");
		writer.write("  </table>");
		writer.write("</td>");
	}

	protected void writeGraphTop(Writer writer, String title)
			throws IOException {

		writer.write("<table class='graph' cellpadding='0' cellspacing='0'>");
		writer.write("  <tr height='" + this.height + "'>");
		writer.write("    <td><table cellspacing='0' cellpadding='0'>");
		writer.write("      <tr><td colspan='1000' align='center'><h2>" + title
				+ "</h2></td></tr>");
	}

	protected void writeXAxisForDates(Writer writer, Collection<Date> dates,
			int numCols) throws IOException {
		writer.write("</tr>");
		writer.write("<tr height='1'><td></td><td colspan='" + numCols
				+ "' class='line'></td></tr>");
		writer.write("<tr><td colspan='2'></td>");
		int i = 1;
		for (Date date : dates) {
			writer.write("<td colspan='3' align='left'>");
			writer.write("&nbsp;" + ReportUtilities.DATE_FORMATTER.format(date)
					+ "&nbsp;");
			writer.write("</td>");
			i++;
		}
		writer.write("</tr>");
		writer.write("</table></td>");
		writer.write("</tr>");
		writer.write("<tr>");

		writer.write("</tr");
		writer.write("</table><br><br><br>");
	}

	protected void writeXAxis(Writer writer, Collection<Statistics> compStats,
			int numCols) throws IOException {
		writer.write("</tr>");
		writer.write("<tr height='1'><td></td><td colspan='" + numCols
				+ "' class='line'></td></tr>");
		writer.write("<tr><td colspan='2'></td>");
		int i = 1;
		for (Statistics stats : compStats) {
			writer.write("<td colspan='3' align='left'>");
			writer.write("&nbsp;"
					+ ReportUtilities.DATE_FORMATTER.format(stats
							.getDateCalculated()) + "&nbsp;");
			writer.write("</td>");
			i++;
		}
		writer.write("</tr>");
		writer.write("</table></td>");
		writer.write("</tr>");
		writer.write("<tr>");

		writer.write("</tr");
		writer.write("</table><br><br><br>");
	}
}
