package dataQualityWebSite;

import java.text.DateFormat;
import java.text.DecimalFormat;
import java.text.SimpleDateFormat;
import java.util.Calendar;
import java.util.ListIterator;


public class SVGChart 
{
    //  This needs refactoring:
	//	 - remove references to Tier 1 and Tier 2 (these go back to T-Mobile days)
    //	 - remove lower amber and red sections
	//   - the odd tweaks here and there can't really be necessary
    //   - try to remove confusion - it can't be that hard.	
	
	
	private DashboardFile svgChartFile;
	private Calendar today;
	private DateFormat dateFormat;
	private DateFormat longDateFormat;
	
	private double daysBetween (Calendar c1, Calendar c2)
	{
		long milliSec;
		
		milliSec = c2.getTimeInMillis() - c1.getTimeInMillis(); 
		
		return (double)milliSec / (1000 * 60 * 60 * 24);
	}

	private Calendar firstOfMonth(Calendar cIn)
	{
		Calendar cOut;
	
		cOut = cIn;
		cOut.set(Calendar.DATE, 1);
		return cOut;
	}
	
	private int[] calculateRectangle
	(
		long top,
		long bottom,
		double minY,
		double maxY,
		double yScale,
		boolean wholeChartZero
	)
	{
		
		long height;
		int intHeight;
		int intTop;
		int[] coordinates = {100,0,580,0};      // x, y, width, height
		
		if (top > maxY) 
			top = Math.round(maxY);
		if (top < minY)
			top = Math.round(minY);
		if (bottom > maxY)
			bottom = Math.round(maxY);
		if (bottom < minY)
			bottom = Math.round(minY);
		height = top - bottom;
		
		top -= minY;		// Allow for minY not being zero
		
		intTop = (int) Math.round(top * yScale);
		intHeight = (int) Math.round(height * yScale);
		
		if(wholeChartZero)
			intHeight -= 5;

		coordinates[1] = 350 - intTop;
		coordinates[3] = intHeight;
				
		return coordinates;
	}

	private String colouredRectangleSvg
	(
		int x,
		int y,
		int width,
		int height,
		String colour
	)
	{
		String rgbString;
		String rgbRed = "rgb(255,0,0);opacity:0.2";
		String rgbAmber = "rgb(255,200,0);opacity:0.2";
		String rgbGreen = "rgb(0,255,0);opacity:0.2";
		
		if (height <= 0)
			return "";
		else
		{
			if (colour.equals("red"))
				rgbString = new String(rgbRed);
			else if (colour.equals("amber"))
				rgbString = new String(rgbAmber);
			else
				rgbString = new String(rgbGreen);
			return
			(
					"    <rect x=\"100\" y=\"" + y + 
					"\" width=\"" + width + 
					"\" height=\"" + height +
					"\" style=\"fill:" + rgbString + "\"/>"
	        );			
		}
	}
	
	
	
	public SVGChart(SeriesList seriesList, FactorElement factorElement, Configuration configuration)
	{
		int monthsOnGraph;
		int i;
		Calendar nMonthsBefore;
		double maxChange;
		double minChange;
		double maxValue;
		double minValue;
		double mean;
		double standardDeviation;
		double maxY;
		double minY;
		int orderOfMagnitude;			// the highest power of 10 which is less than maxChange
		double yScale;
		double xScale;
		long top;
		long bottom;
		double tickIncrement;
		double tickY;
		ListIterator<SeriesElement> iterator;
		SeriesElement seriesElement;
		double change;
		double value;
		Calendar auditDate;
		long x;
		long y;
		boolean firstPoint;
		int monthIncrement;
		Calendar xOriginDate;
		String factorType;
		String chartTitle;
		boolean wholeChartZero;
		String svgChartFileName;
		int[] rectangleCoordinates;      // x, y, width, height
		int bottomOfRed;
		int bottomOfAmber;
		DecimalFormat integerFormat;
		
		
		integerFormat = new DecimalFormat("###,###,###,###,###");
		monthsOnGraph = factorElement.getMonthsOnGraph();
		factorType = new String(factorElement.getFactorType());
		
		svgChartFileName = new String
		(
			configuration.getHtmlFileLocation() + 
			configuration.getChartPrefix() + 
			factorElement.getFactorId() +
			".htm"
		);
						
		svgChartFile = new DashboardFile(svgChartFileName);
		
    	today = Calendar.getInstance();
    	dateFormat = new SimpleDateFormat("dd MMM yyyy");
        dateFormat.format(today.getTime());

        svgChartFile.PutLine("<!DOCTYPE html>");
        svgChartFile.PutLine("<html lang=\"en\">");
        svgChartFile.PutLine("<head>");
        svgChartFile.PutLine("  <title>" + factorElement.getFactorTitle() + "</title>");
        svgChartFile.PutLine("  <meta charset=\"UTF-8\">");
        
        svgChartFile.PutLine("  <style>");
        svgChartFile.PutLine("    .grid-container ");
        svgChartFile.PutLine("    {");
        svgChartFile.PutLine("      display: grid;");
        svgChartFile.PutLine("      grid-template-columns: 120px 120px 120px 120px;");
        svgChartFile.PutLine("      padding-top: 10px;");
        svgChartFile.PutLine("    }");
        svgChartFile.PutLine("  </style>");
        svgChartFile.PutLine("  <script>");
        svgChartFile.PutLine("   function goBack()");
        svgChartFile.PutLine("    {");
        svgChartFile.PutLine("      window.history.back();");
        svgChartFile.PutLine("    }");
        svgChartFile.PutLine("  </script>");
        
        svgChartFile.PutLine("</head>");
        svgChartFile.PutLine("<body>");
        svgChartFile.PutLine("<svg preserveAspectRatio=\"xMidYMid meet\" viewBox=\"0 0 720 400\">");        
        svgChartFile.PutLine("  <defs>");
        svgChartFile.PutLine("    <style type=\"text/css\">");
        svgChartFile.PutLine("      .xgrid, .ygrid");
        svgChartFile.PutLine("      {");
        svgChartFile.PutLine("        font-weight:normal;");
        svgChartFile.PutLine("        font-size:10px;");
        svgChartFile.PutLine("        font-family:Arial;");
        svgChartFile.PutLine("      }");
        svgChartFile.PutLine("      .xgrid {text-anchor:middle;}");
        svgChartFile.PutLine("      .ygrid {text-anchor:end;}");
        svgChartFile.PutLine("      .gridline");
        svgChartFile.PutLine("      {");
        svgChartFile.PutLine("        stroke:black;");
        svgChartFile.PutLine("        stroke-width:1;");
        svgChartFile.PutLine("      }");

        svgChartFile.PutLine("      .charttitlechanges");
		svgChartFile.PutLine("      {");
		svgChartFile.PutLine("        font-weight:bold;");
		svgChartFile.PutLine("        font-size:10px;");
		svgChartFile.PutLine("        font-family:Arial;");
		svgChartFile.PutLine("        background-color:LightSkyBlue;");
        svgChartFile.PutLine("      }");

        svgChartFile.PutLine("      .charttitlevalues");
		svgChartFile.PutLine("      {");
		svgChartFile.PutLine("        font-weight:bold;");
		svgChartFile.PutLine("        font-size:10px;");
		svgChartFile.PutLine("        font-family:Arial;");
		svgChartFile.PutLine("        background-color:Orange;");
        svgChartFile.PutLine("      }");
        
        svgChartFile.PutLine("      .tablelink");
		svgChartFile.PutLine("      {");
		svgChartFile.PutLine("        fill:teal;");
		svgChartFile.PutLine("        font-weight:normal;");
		svgChartFile.PutLine("        font-size:8px;");
		svgChartFile.PutLine("        font-family:Arial;");
		svgChartFile.PutLine("        text-decoration:underline;");
        svgChartFile.PutLine("      }");

        svgChartFile.PutLine("      .published");
		svgChartFile.PutLine("      {");
		svgChartFile.PutLine("        font-weight:normal;");
		svgChartFile.PutLine("        font-size:8px;");
		svgChartFile.PutLine("        font-family:Arial;");
		svgChartFile.PutLine("        font-style:italic;");
		svgChartFile.PutLine("      }");
        
        svgChartFile.PutLine("    </style>");
        svgChartFile.PutLine("  </defs>");
        
        if (factorType.equals("Trend"))
        {
            svgChartFile.PutLine("  <rect x=\"0\" y=\"0\" height=\"400\" width=\"720\" stroke=\"orange\" stroke-width=\"4\" fill=\"white\"/>");
        	chartTitle = new String("CHANGES for ");
            svgChartFile.PutLine
            (
            	"  <text class=\"charttitlechanges\" x=\"20\" y=\"25\">" + 
            	chartTitle + 
            	factorElement.getFactorTitle() +
            	"</text>"
            );
        }
        else
        {
            svgChartFile.PutLine("  <rect x=\"0\" y=\"0\" height=\"400\" width=\"720\" stroke=\"LightSkyBlue\" stroke-width=\"4\" fill=\"white\"/>");
        	chartTitle = new String("VALUES for ");
            svgChartFile.PutLine
            (
            	"  <text class=\"charttitlevalues\" x=\"20\" y=\"25\">" + 
            	chartTitle + 
            	factorElement.getFactorTitle() +
            	"</text>"
            );
        }
        
		maxChange = seriesList.getMaxChange();
		minChange = seriesList.getMinChange();
		maxValue = seriesList.getMaxValue();
		minValue = seriesList.getMinValue();

		if (factorType.equals("Trend"))
		{
			if ((minChange == 0) && (maxChange == 0))
				wholeChartZero = true;
			else
				wholeChartZero = false;
		}
		else
		{
			if ((minValue == 0) && (maxValue == 0))
				wholeChartZero = true;
			else
				wholeChartZero = false;
		}
		
		mean = seriesList.getMean();
		standardDeviation = seriesList.getStandardDeviation();

//
//      Tweaking Y axis
//		if (!factorType.equals("Trend"))
//		{
//			if (factorElement.getAmberLimit() > maxValue)
//			{
//				maxValue = factorElement.getAmberLimit();
//			}
//		}
//		
		
		double d;
		if (factorType.equals("Trend"))
			d = maxChange;
		else
			d = maxValue;
		for (orderOfMagnitude = 0; d > 1; orderOfMagnitude++)
			d /= 10;
		if (orderOfMagnitude > 0)
				orderOfMagnitude--;

		if (factorType.equals("Trend"))
		{
			minY = (Math.floor(minChange / Math.pow(10, orderOfMagnitude)) * Math.pow(10, orderOfMagnitude));
			maxY = (Math.ceil(maxChange / Math.pow(10, orderOfMagnitude)) * Math.pow(10, orderOfMagnitude));
		}
		else
		{
			minY = (Math.floor(minValue / Math.pow(10, orderOfMagnitude)) * Math.pow(10, orderOfMagnitude));
			maxY = (Math.ceil(maxValue / Math.pow(10, orderOfMagnitude)) * Math.pow(10, orderOfMagnitude));
		}
		
		if (maxY < 1)
			maxY = 1;
		
		if (minY == maxY)
			minY = 0;
		
		if (maxY < 10)
		{
			maxY = 10;
		}	

		
		yScale = 300 / (maxY - minY);
		
//
// Chart objects are measured from the top of the chart, in the opposite direction to the Y-axis.  
// Plot area starts from 50 (50 units down from the top of the graph) and is 300 units high.
// Rectangles (such as the red, amber and green shading) are specified by their top position and their height
// All of this can be confusing!
//
//
// Upper Red
//
// 		top is top of chart, always 50
// 		For Trends: bottom is (mean + (3 * SD)) or top of graph;
//		For Tier 1 and Tier 2 bottom is amber limit or top of graph;
//		if the bottom of the upper red area is above the top 
//		of the graph it is not shown at all. 
//
		top = Math.round(maxY);
		if (factorType.equals("Trend"))
			bottom = Math.round(mean + (3 * standardDeviation));
		else
			bottom = Math.round(factorElement.getAmberLimit());
		rectangleCoordinates = calculateRectangle(top, bottom, minY, maxY, yScale, wholeChartZero);
		bottomOfRed = rectangleCoordinates[1] + rectangleCoordinates[3];
		svgChartFile.PutLine(colouredRectangleSvg(rectangleCoordinates[0], rectangleCoordinates[1], rectangleCoordinates[2], rectangleCoordinates[3], "red"));
//		svgChartFile.PutLine(colouredRectangle(top, bottom, minY, maxY, yScale, "red", wholeChartZero));
		
//
// Upper Amber
//
		if (factorType.equals("Trend"))
		{		
			top = Math.round(mean + (3 * standardDeviation));
			bottom = Math.round(mean + (2 * standardDeviation));
		}
		else
		{
			top = Math.round(factorElement.getAmberLimit());
			bottom = Math.round(factorElement.getGreenLimit());
		}
		rectangleCoordinates = calculateRectangle(top, bottom, minY, maxY, yScale, wholeChartZero);
		if (rectangleCoordinates[1] != bottomOfRed)
			rectangleCoordinates[1] = bottomOfRed;
		bottomOfAmber = rectangleCoordinates[1] + rectangleCoordinates[3];
		svgChartFile.PutLine(colouredRectangleSvg(rectangleCoordinates[0], rectangleCoordinates[1], rectangleCoordinates[2], rectangleCoordinates[3], "amber"));
//		svgChartFile.PutLine(colouredRectangle(top, bottom, minY, maxY, yScale, "amber", wholeChartZero));

//
//  Green
//
		if (factorType.equals("Trend"))
		{		
			top = Math.round(mean + (2 * standardDeviation));
			bottom = Math.round(mean - (2 * standardDeviation));
		}
		else
		{
			top = Math.round(factorElement.getGreenLimit());
			bottom = 0;			
		}
		rectangleCoordinates = calculateRectangle(top, bottom, minY, maxY, yScale, wholeChartZero);
		if (rectangleCoordinates[1] != bottomOfAmber)
			rectangleCoordinates[1] = bottomOfAmber;
		svgChartFile.PutLine(colouredRectangleSvg(rectangleCoordinates[0], rectangleCoordinates[1], rectangleCoordinates[2], rectangleCoordinates[3], "green"));
//		svgChartFile.PutLine(colouredRectangle(top, bottom, minY, maxY, yScale, "green", wholeChartZero));

//
//	Lower Amber
//
//		if (factorType.equals("Trend"))
//		{		
//			top = Math.round(mean - (2 * standardDeviation));
//			bottom = Math.round(mean - (3 * standardDeviation));
//			svgChartFile.PutLine(colouredRectangle(top, bottom, minY, maxY, yScale, "amber", wholeChartZero));
//		}

//
//	 Lower Red
//
//		if (factorType.equals("Trend"))
//		{		
//			top = Math.round(mean - (3 * standardDeviation));
//			bottom = Math.round(minY);
//			svgChartFile.PutLine(colouredRectangle(top, bottom, minY, maxY, yScale, "red", wholeChartZero));
//		}

//
//    Special Case: If the plot is zero all the way along, then it looks silly because the whole chart is red.
//    In this case only, put a thin green rectangle just above the line.
//
		if (factorType.equals("Trend"))
		{
			if ((minChange == 0) && (maxChange == 0))
			{
				svgChartFile.PutLine
				(
				    "    <rect x=\"100\" y=\"345\" width=\"580\" height=\"5\" style=\"fill:rgb(0,255,0);opacity:0.2\"/>"
				);
			}
		}
		else
		{
			if ((minValue == 0) && (maxValue == 0))
			{
				svgChartFile.PutLine
				(
				    "    <rect x=\"100\" y=\"345\" width=\"580\" height=\"5\" style=\"fill:rgb(0,255,0);opacity:0.2\"/>"
				);
			}			
		}
//
//    End of special case
//
		
//         
//      Build X-axis
//

        svgChartFile.PutLine("   <g id=\"x_axis\">");
        svgChartFile.PutLine("      <line class=\"histframe\" x1=\"100\" x2=\"680\" y1=\"350\" y2=\"350\"/>");
        svgChartFile.PutLine("      <line class=\"gridline\" x1=\"100\" x2=\"100\" y1=\"350\" y2=\"360\"/>");

        xScale = (double)(580 * 12) / (double)(365 * monthsOnGraph);
        monthIncrement = (int) Math.ceil(monthsOnGraph / 6);
        xOriginDate = Calendar.getInstance();
        xOriginDate.add(Calendar.MONTH, ( 1 - monthsOnGraph));
        xOriginDate.set(Calendar.DATE, 1);
        nMonthsBefore = Calendar.getInstance();
        nMonthsBefore.add(Calendar.MONTH, ( 1 - monthsOnGraph));
        
        svgChartFile.PutLine
        ( 
            "      <text class=\"xgrid\" x=\"100\" y=\"370\">" +
            dateFormat.format(firstOfMonth(nMonthsBefore).getTime()) +
            "</text>"
    	);

        for (i = 0; i < monthsOnGraph; i += monthIncrement)
        {
           	nMonthsBefore.add(Calendar.MONTH, monthIncrement);

        	svgChartFile.PutLine
            ( 
            	"      <line class=\"gridline\" x1=\"" +
            	Math.round(100.0 + (daysBetween(xOriginDate, nMonthsBefore) * xScale)) +
            	"\" x2=\"" +
            	Math.round(100.0 + (daysBetween(xOriginDate, nMonthsBefore) * xScale)) +
            	"\" y1=\"350\" y2=\"360\"/>"
            	);
            
            svgChartFile.PutLine
            ( 
                "      <text class=\"xgrid\" x=\"" +
            	Math.round(100.0 + (daysBetween(xOriginDate, nMonthsBefore) * xScale)) +
                "\" y=\"370\">" +
                dateFormat.format(firstOfMonth(nMonthsBefore).getTime()) +
                "</text>"
        	);
        }

        svgChartFile.PutLine("   </g>");

        //
        //  Build Y-axis
        //
        
        svgChartFile.PutLine("   <g id=\"y_axis\">");
        svgChartFile.PutLine("      <line class=\"histframe\" x1=\"100\" x2=\"100\" y1=\"50\" y2=\"350\"/>");

        tickIncrement = Math.pow(10, orderOfMagnitude);
        
        for (tickY = minY; tickY <= maxY; tickY += tickIncrement)
        {
            svgChartFile.PutLine
            (      
	            "      <line class=\"gridline\" x1=\"90\" x2=\"100\" y1=\"" +
	            (350 - Math.round((tickY - minY) * yScale)) +
	            "\" y2=\"" +
	            (350 - Math.round((tickY - minY) * yScale)) +
	            "\"/>"
            );
            svgChartFile.PutLine
            (      
	            "      <text class=\"ygrid\" x=\"89\" y=\"" +
	            (354 - Math.round((tickY - minY) * yScale)) +
	            "\">" +
	            integerFormat.format(Math.round(tickY)) +
	            "</text>"
            );
        }

        svgChartFile.PutLine("   </g>");

        svgChartFile.PutLine("   <svg id=\"graphzone\" preserveAspectRatio=\"none\" x=\"100\" y=\"50\" width=\"600\" height=\"300\" viewBox=\"0 0 600 300\">");
        svgChartFile.PutLine("      <g style=\"stroke:black;stroke-width:2;fill:none;\">");

//
//      Plot the values
//
        firstPoint = true;
        
		for (iterator = seriesList.listIterator(); iterator.hasNext(); )
		{
			seriesElement = (SeriesElement) iterator.next();
			change = seriesElement.getChange();
			value = seriesElement.getValue();
			
			// Hack to stop negative values breaking the graph
//			if (value < 0)
//				value = 0;			
			// End of this part of the hack
			
			auditDate = seriesElement.getAuditDate();
			
			x = Math.round(daysBetween(xOriginDate, auditDate) * xScale);
			
			double rawY;
			

			if  (factorType.equals("Trend"))
				rawY = change;
			else
				rawY = value;
			
			y = Math.round(300.0 - ((rawY - minY) * yScale));
			
			if ((x > -2) && (x < 582))
			{
				if (firstPoint)
				{
					firstPoint = false;
					svgChartFile.PutLine("         <path d=\"M " + x + " " + y);
				}
				else
				{
					svgChartFile.PutLine("             L " + x + " " + y);
				}
			}
		}
           
        svgChartFile.PutLine("         \"/>");
        svgChartFile.PutLine("    </g>");
        svgChartFile.PutLine("  </svg>");

    	longDateFormat = new SimpleDateFormat("EEE, dd MMM yyyy HH:mm:ss");
    	
        svgChartFile.PutLine
        (
        	"  <text class=\"published\" x=\"100\" y=\"390\"> Chart published: " +
        	longDateFormat.format(today.getTime()) +
        	"</text>"
        );
        svgChartFile.PutLine("  </svg>");


        svgChartFile.PutLine("<div class=\"grid-container\">");
        svgChartFile.PutLine("  <div class=\"grid-item\">");
        svgChartFile.PutLine("    <form action=\"" + configuration.getDataTablePrefix() + factorElement.getFactorId() + ".htm\">");
        svgChartFile.PutLine("      <button type=\"submit\" style=\"padding:0px;margin:0px;width:0px;height:0px;border:none;\">");
        svgChartFile.PutLine("        <img");
        svgChartFile.PutLine("          src=\"" + configuration.getLinkToKpiImages() + "button_data_table.png\"");
        svgChartFile.PutLine("          style=\"display:inline; width:100px;\"");
        svgChartFile.PutLine("          alt=\"Go to data table\"");
        svgChartFile.PutLine("        >");
        svgChartFile.PutLine("      </button>");
        svgChartFile.PutLine("    </form>");
        svgChartFile.PutLine("  </div>");
        svgChartFile.PutLine("  <div class=\"grid-item\">");
        svgChartFile.PutLine("    <form action=" + configuration.getLinkToIssueTrackingNew() + ">");
        svgChartFile.PutLine("      <button type=\"submit\" style=\"padding:0px;margin:0px;width:0px;height:0px;border:none;\">");
        svgChartFile.PutLine("        <img");
        svgChartFile.PutLine("          src=\"" + configuration.getLinkToKpiImages() + "button_create_a_ticket.png\""); 
        svgChartFile.PutLine("          style=\"display:inline; width:100px;\"");
        svgChartFile.PutLine("          alt=\"Create a ticket\"");
        svgChartFile.PutLine("        >");
        svgChartFile.PutLine("      </button>");
        svgChartFile.PutLine("      <input type=\"hidden\" name=\"summary\" value=\"" + factorElement.getFactorTitle() + "\">");
        svgChartFile.PutLine("      <input type=\"hidden\" name=\"type\" value=\"defect\">");
        svgChartFile.PutLine("      <input type=\"hidden\" name=\"component\" value=\"Data Quality\">");
        svgChartFile.PutLine("    </form>");
        svgChartFile.PutLine("  </div>");
        svgChartFile.PutLine("  <div class=\"grid-item\">");
        svgChartFile.PutLine("  <button type=\"button\" onclick=\"goBack()\" style=\"padding:0px;margin:0px;width:0px;height:0px;border:none;\">");
        svgChartFile.PutLine("    <img");
        svgChartFile.PutLine("      src=\"" + configuration.getLinkToKpiImages() + "button_back_to_group.png\"");
        svgChartFile.PutLine("      style=\"display:inline; width:100px;\"");
        svgChartFile.PutLine("      alt=\"Go back to group\"");
        svgChartFile.PutLine("    >");
        svgChartFile.PutLine("  </button>");
        svgChartFile.PutLine("  </div>");
        svgChartFile.PutLine("  <div class=\"grid-item\">");
        svgChartFile.PutLine("    <form action=\"index.html\">");
        svgChartFile.PutLine("      <button type=\"submit\" style=\"padding:0px;margin:0px;width:0px;height:0px;border:none;\">");
        svgChartFile.PutLine("        <img");
        svgChartFile.PutLine("          src=\"" + configuration.getLinkToKpiImages() + "button_back_to_summary.png\""); 
        svgChartFile.PutLine("          style=\"display:inline; width:100px;\"");
        svgChartFile.PutLine("          alt=\"Go back to summary\"");
        svgChartFile.PutLine("        >");
        svgChartFile.PutLine("      </button>");
        svgChartFile.PutLine("    </form>");
        svgChartFile.PutLine("  </div>");
        svgChartFile.PutLine("</div>");
               
        svgChartFile.PutLine("</body>");
 		svgChartFile.Close();
	}
}
