/*
 *                    BioJava development code
 *
 * This code may be freely distributed and modified under the
 * terms of the GNU Lesser General Public Licence.  This should
 * be distributed with the code.  If you do not have a copy,
 * see:
 *
 *      http://www.gnu.org/copyleft/lesser.html
 *
 * Copyright for this code is held jointly by the individual
 * authors.  These should be listed in @author doc comments.
 *
 * For more information on the BioJava project and its aims,
 * or to join the biojava-l mailing list, visit the home page
 * at:
 *
 *      http://www.biojava.org/
 *
 */
package org.biojava.nbio.survival.kaplanmeier.figure;

import org.biojava.nbio.survival.cox.*;

import javax.imageio.ImageIO;
import javax.swing.*;
import java.awt.*;
import java.awt.geom.AffineTransform;
import java.awt.image.BufferedImage;
import java.io.File;
import java.io.FileWriter;
import java.text.DecimalFormat;
import java.util.ArrayList;
import java.util.Collections;
import java.util.LinkedHashMap;

/**
 *
 * @author Scooter Willis <willishf at gmail dot com>
 */
public class KaplanMeierFigure extends JPanel {

	private static final long serialVersionUID = 1L;

	ArrayList<String> title = new ArrayList<String>();
	/**
	 *
	 */
	private int top;
	/**
	 *
	 */
	private int bottom;
	/**
	 *
	 */
	private int left;
	private int yaxisLabel = 20;
	/**
	 *
	 */
	private int right;
	int titleHeight;
	int xAxisLabelHeight;
	int labelWidth;
	double minTime = 0.0;
	double maxTime = 10.0;
	double minPercentage = 0.0;
	double maxPercentage = 1.0;
	FontMetrics fm;
	KMFigureInfo kmfi = new KMFigureInfo();
	LinkedHashMap<String, ArrayList<CensorStatus>> survivalData = new LinkedHashMap<String, ArrayList<CensorStatus>>();
	ArrayList<String> lineInfoList = new ArrayList<String>();
	SurvFitInfo sfi = new SurvFitInfo();
	private String fileName = "";
	private ArrayList<Double> xAxisTimeValues = new ArrayList<Double>();
	private ArrayList<Integer> xAxisTimeCoordinates = new ArrayList<Integer>();

	/**
	 *
	 */
	public KaplanMeierFigure() {
		super();
		setSize(500, 400);
		setBackground(Color.WHITE);
	}

	/**
	 * Get the name of the groups that are being plotted in the figure
	 *
	 * @return
	 */
	public ArrayList<String> getGroups() {
		return new ArrayList<String>(survivalData.keySet());
	}

	/**
	 * To get the median percentile for a particular group pass the value of
	 * .50.
	 *
	 * @param group
	 * @param percentile
	 * @return
	 */
	public Double getSurvivalTimePercentile(String group, double percentile) {

		StrataInfo si = sfi.getStrataInfoHashMap().get(group);
		ArrayList<Double> percentage = si.getSurv();
		Integer percentileIndex = null;
		for (int i = 0; i < percentage.size(); i++) {
			if (percentage.get(i) == percentile) {
				if (i + 1 < percentage.size()) {
					percentileIndex = i + 1;
				}
				break;
			} else if (percentage.get(i) < percentile) {
				percentileIndex = i;
				break;
			}
		}
		if (percentileIndex != null) {
			return si.getTime().get(percentileIndex);
		} else {
			return null;
		}
	}

	/**
	 *
	 * @param kmfi
	 */
	public void setKMFigureInfo(KMFigureInfo kmfi) {
		this.kmfi = kmfi;
		if (kmfi.width != null && kmfi.height != null) {
			this.setSize(kmfi.width, kmfi.height);
		}
	}

	public KMFigureInfo getKMFigureInfo() {
		return kmfi;
	}

	/**
	 *
	 * @param lineInfoList
	 */
	public void setFigureLineInfo(ArrayList<String> lineInfoList) {
		this.lineInfoList = lineInfoList;
		this.repaint();
	}

	/**
	 *
	 * @param title Title of figures
	 * @param ci
	 * @param strataVariable The column that based on value will do a figure
	 * line
	 * @param legendMap Map the value in the column to something readable
	 * @param useWeighted
	 * @throws Exception
	 */
	public void setCoxInfo(ArrayList<String> title, CoxInfo ci, String strataVariable, LinkedHashMap<String, String> legendMap, Boolean useWeighted) throws Exception {
		LinkedHashMap<String, ArrayList<CensorStatus>> survivalData = new LinkedHashMap<String, ArrayList<CensorStatus>>();
		ArrayList<SurvivalInfo> siList = ci.getSurvivalInfoList();
		int n = 0;
		int event = 0;
		for (SurvivalInfo si : siList) {
			String strata = si.getOriginalMetaData(strataVariable);
			String legend = legendMap.get(strata);
			if (legend == null) {

				legend = strata;
			}
			ArrayList<CensorStatus> censorStatusList = survivalData.get(legend);
			if (censorStatusList == null) {
				censorStatusList = new ArrayList<CensorStatus>();
				survivalData.put(legend, censorStatusList);
			}
			CensorStatus cs = new CensorStatus(strata, si.getTime(), si.getStatus() + "");
			cs.weight = si.getWeight();
			censorStatusList.add(cs);
			n++;
			if (si.getStatus() == 1) {
				event++;
			}
		}

		setSurvivalData(title, survivalData, useWeighted);
		CoxCoefficient cc = ci.getCoefficient(strataVariable);
		//DecimalFormat df = new DecimalFormat("#.##");
		String line1 = "HR=" + fmt(cc.getHazardRatio(), 2, 0) + " (CI:" + fmt(cc.getHazardRatioLoCI(), 2, 0) + "-" + fmt(cc.getHazardRatioHiCI(), 2, 0) + ")";
		String line2 = "p=" + fmt(cc.getPvalue(), 3, 0);
	   // String line2 = "logrank P=" + fmt(ci.getScoreLogrankTestpvalue(), 3, 0);
		String line3 = "n=" + n + " events=" + event;
//        System.out.println("setCoxInfo=" + cc.pvalue + " " + title);


		ArrayList<String> lines = new ArrayList<String>();
		lines.add(line1);
		lines.add(line2);
		lines.add(line3);
		setFigureLineInfo(lines);
	}

	/**
	 *
	 * @param d
	 * @param precision
	 * @param pad
	 * @return
	 */
	public static String fmt(Double d, int precision, int pad) {
		String value = "";
		DecimalFormat dfe = new DecimalFormat("0.00E0");
		String dpad = "0.";
		double p = 1.0;
		for (int i = 0; i < (precision); i++) {
			dpad = dpad + "0";
			p = p / 10.0;
		}
		DecimalFormat df = new DecimalFormat(dpad);
		if (Math.abs(d) >= p) {
			value = df.format(d);
		} else {
			value = dfe.format(d);
		}
		int length = value.length();
		int extra = pad - length;
		if (extra > 0) {
			for (int i = 0; i < extra; i++) {
				value = " " + value;
			}
		}
		return value;
	}

	/**
	 *
	 * @return
	 */
	public SurvFitInfo getSurvivalFitInfo() {
		return sfi;
	}

	/**
	 * Allow setting of points in the figure where weighted correction has been
	 * done and percentage has already been calculated.
	 *
	 * @param title
	 * @param sfi
	 * @param userSetMaxTime
	 */
	public void setSurvivalData(ArrayList<String> title, SurvFitInfo sfi, Double userSetMaxTime) {
		this.title = title;
		LinkedHashMap<String, StrataInfo> strataInfoHashMap = sfi.getStrataInfoHashMap();
		Double mTime = null;
		for (StrataInfo si : strataInfoHashMap.values()) {
			for (double t : si.getTime()) {
				if (mTime == null || t > mTime) {
					mTime = t;
				}
			}
		}

		int evenCheck = Math.round(mTime.floatValue());
		if (evenCheck % 2 == 1) {
			evenCheck = evenCheck + 1;
		}
		this.maxTime = evenCheck;

		if (userSetMaxTime != null && userSetMaxTime > maxTime) {
			this.maxTime = userSetMaxTime;
		}
		this.sfi = sfi;
		if (sfi.getStrataInfoHashMap().size() == 1) {
			return;
		}
		this.repaint();
	}

	/**
	 * The data will set the max time which will result in off time points for
	 * tick marks
	 *
	 * @param title
	 * @param survivalData
	 * @param useWeighted
	 * @throws Exception
	 */
	public void setSurvivalData(ArrayList<String> title, LinkedHashMap<String, ArrayList<CensorStatus>> survivalData, Boolean useWeighted) throws Exception {
		this.setSurvivalData(title, survivalData, null, useWeighted);
	}

	/**
	 *
	 * @param title
	 * @param survivalData
	 * @param userSetMaxTime
	 * @param useWeighted
	 * @throws Exception
	 */
	public void setSurvivalData(ArrayList<String> title, LinkedHashMap<String, ArrayList<CensorStatus>> survivalData, Double userSetMaxTime, Boolean useWeighted) throws Exception {
		this.title = title;
		this.survivalData = survivalData;
		Double mTime = null;
		ArrayList<String> labels = new ArrayList<String>(survivalData.keySet());
		Collections.sort(labels);
		for (String legend : labels) {
			ArrayList<CensorStatus> censorStatusList = survivalData.get(legend);
			for (CensorStatus cs : censorStatusList) {

				if (mTime == null || cs.time > mTime) {
					mTime = cs.time;
				}
			}
		}

		int evenCheck = Math.round(mTime.floatValue());
		if (evenCheck % 2 == 1) {
			evenCheck = evenCheck + 1;
		}
		this.maxTime = evenCheck;

		if (userSetMaxTime != null && userSetMaxTime > maxTime) {
			this.maxTime = userSetMaxTime;
		}

		//calculate percentages
		SurvFitKM survFitKM = new SurvFitKM();
		sfi = survFitKM.process(survivalData, useWeighted);
		this.repaint();
	}

	/**
	 * Save data from survival curve to text file
	 *
	 * @param fileName
	 * @throws Exception
	 */
	public void saveSurvivalData(String fileName) throws Exception {
		FileWriter fw = new FileWriter(fileName);
		fw.write("index\tTIME\tSTATUS\tGROUP\r\n");
		int index = 0;
		for (String group : survivalData.keySet()) {
			ArrayList<CensorStatus> sd = survivalData.get(group);
			for (CensorStatus cs : sd) {
				String line = index + "\t" + cs.time + "\t" + cs.censored + "\t" + cs.group + "\r\n";
				index++;
				fw.write(line);
			}
		}
		fw.close();
	}
	DecimalFormat df = new DecimalFormat("#.#");

	@Override
	public void paintComponent(Graphics g) // draw graphics in the panel
	{
		int width = getWidth();             // width of window in pixels
		int height = getHeight();           // height of window in pixels
		setFigureDimensions();
		g.setColor(Color.white);
		g.clearRect(0, 0, width, height);

		super.paintComponent(g);            // call superclass to make panel display correctly

		drawLegend(g);
		drawSurvivalCurves(g);
		drawFigureLineInfo(g);
		// Drawing code goes here
	}

	private void drawFigureLineInfo(Graphics g) {
		Graphics2D g2 = (Graphics2D) g;
		setRenderingHints(g2);
		g2.setColor(Color.BLACK);
		fm = getFontMetrics(getFont());
		int yoffset = fm.getHeight() * lineInfoList.size();

		int x = getTimeX(kmfi.figureLineInfoLowerPercentX * maxTime);
		int y = getPercentageY(kmfi.figureLineInfoLowerPercentY) - yoffset;

		for (String line : lineInfoList) {
			g2.drawString(line, x, y);
			y = y + fm.getHeight();
		}

	}

	private void drawSurvivalCurves(Graphics g) {
		Graphics2D g2 = (Graphics2D) g;
		setRenderingHints(g2);
		g2.setStroke(kmfi.kmStroke);


		int colorIndex = 0;
		ArrayList<String> labels = new ArrayList<String>(sfi.getStrataInfoHashMap().keySet());
		Collections.sort(labels);

		LinkedHashMap<String, StrataInfo> strataInfoHashMap = sfi.getStrataInfoHashMap();

		for (String legend : labels) {
			StrataInfo si = strataInfoHashMap.get(legend);
			g2.setColor(kmfi.legendColor[colorIndex]);
			colorIndex++;

			for (int i = 0; i < si.getSurv().size() - 1; i++) {
				double p0time = si.getTime().get(i);
				double p1time = si.getTime().get(i + 1);
				double p0percentage = si.getSurv().get(i);
				double p1percentage = si.getSurv().get(i + 1);
				if (i == 0) {
					g2.drawLine(getTimeX(0), getPercentageY(1), getTimeX(p0time), getPercentageY(1));
					g2.drawLine(getTimeX(p0time), getPercentageY(1), getTimeX(p0time), getPercentageY(p0percentage));
				}
				g2.drawLine(getTimeX(p0time), getPercentageY(p0percentage), getTimeX(p1time), getPercentageY(p0percentage));

				g2.drawLine(getTimeX(p1time), getPercentageY(p0percentage), getTimeX(p1time), getPercentageY(p1percentage));
				// if (si.getStatus().get(i) == 0) {
				if (i > 0 && si.getNcens().get(i) > 0) {
					g2.drawLine(getTimeX(p0time), getPercentageY(p0percentage) - 4, getTimeX(p0time), getPercentageY(p0percentage) + 4);
					g2.drawLine(getTimeX(p0time) - 4, getPercentageY(p0percentage), getTimeX(p0time) + 4, getPercentageY(p0percentage));
				}
			}


		}

		String maxString = "";
		for (String legend : labels) {
			if (legend.length() > maxString.length()) {
				maxString = legend;
			}
		}

		int offset = fm.stringWidth(maxString);
		int x = getTimeX(kmfi.legendUpperPercentX * maxTime) - offset;
		int y = getPercentageY(kmfi.legendUpperPercentY);

		colorIndex = 0;
		for (String legend : labels) {
			g2.setColor(kmfi.legendColor[colorIndex]);
			colorIndex++;
			g2.drawLine(x - 20, y - (fm.getHeight() / 3), x - 5, y - (fm.getHeight() / 3));
			g2.drawString(legend, x, y);
			y = y + fm.getHeight();
		}


	}

	/**
	 * Get the X coordinate based on a time value
	 *
	 * @param value
	 * @return
	 */
	private int getTimeX(double value) {
		double d = left + (((right - left) * value) / (maxTime - minTime));
		return (int) d;
	}

	/**
	 * Get the Y coordinate based on percent value 0.0-1.0
	 *
	 * @param value
	 * @return
	 */
	private int getPercentageY(double value) {
		value = 1.0 - value;
		double d = top + (((bottom - top) * value) / (maxPercentage - minPercentage));
		return (int) d;
	}

	/**
	 * @return the fileName
	 */
	public String getFileName() {
		return fileName;
	}

	/**
	 * @return the top
	 */
	public int getTop() {
		return top;
	}

	/**
	 * @return the bottom
	 */
	public int getBottom() {
		return bottom;
	}

	/**
	 * @return the left
	 */
	public int getLeft() {
		return left;
	}

	/**
	 * @return the right
	 */
	public int getRight() {
		return right;
	}

	/**
	 * @return the xAxisTimeValues
	 */
	public ArrayList<Double> getxAxisTimeValues() {
		return xAxisTimeValues;
	}

	/**
	 * @return the xAxisTimeValues
	 */
	public ArrayList<Integer> getxAxisTimeCoordinates() {
		return xAxisTimeCoordinates;
	}

	class PlotInfo {

		double time;
		double atRisk;
		double censored;
		double events;
		double percentage;

		@Override
		public String toString() {
			return time + "\t" + atRisk + "\t" + censored + "\t" + events + "\t" + (atRisk - events) + "\t" + percentage;
		}
	}

	/**
	 * Do higher quality rendering options
	 *
	 * @param g
	 */
	private void setRenderingHints(Graphics2D g) {
		RenderingHints rh = new RenderingHints(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
		rh.put(RenderingHints.KEY_DITHERING, RenderingHints.VALUE_DITHER_ENABLE);
		rh.put(RenderingHints.KEY_TEXT_ANTIALIASING, RenderingHints.VALUE_TEXT_ANTIALIAS_GASP);
		rh.put(RenderingHints.KEY_RENDERING, RenderingHints.VALUE_RENDER_QUALITY);

		g.setRenderingHints(rh);

	}

	/**
	 * Setup the axis, labels etc
	 *
	 * @param g
	 */
	private void drawLegend(Graphics g) {
		Graphics2D g2 = (Graphics2D) g;
		setRenderingHints(g2);
		g2.setColor(Color.BLACK);
		Font font = g2.getFont();
		Font f = new Font(font.getFontName(), Font.BOLD, font.getSize());
		g2.setFont(f);
		fm = getFontMetrics(f);
		int fontHeight = fm.getHeight();
		for (int i = 0; i < title.size(); i++) {
			if (fm.stringWidth(title.get(i)) > .8 * this.getWidth()) {
				f = new Font(font.getFontName(), Font.BOLD, 10);
				g2.setFont(f);
				fm = getFontMetrics(f);
			}
			g2.drawString(title.get(i), (getSize().width - fm.stringWidth(title.get(i))) / 2, ((i + 1) * fontHeight));
			// g2.setFont(font);
		}
		// draw the maxPercentage and minPercentage values
		String label = df.format(minPercentage);
		g2.drawString(label, left - 5 - (fm.stringWidth(label)), bottom + titleHeight / 6);
		g2.drawLine(left - 5, bottom, left, bottom);
		double d = minPercentage + kmfi.yaxisPercentIncrement;
		//double graphHeight = top - bottom;

		while (d < maxPercentage) {
			int yvalue = bottom - (int) (d * (bottom - top));
			label = df.format(d * 100);
			g2.drawString(label, left - 5 - (fm.stringWidth(label)), yvalue + titleHeight / 6); //

			g2.drawLine(left - 5, yvalue, left, yvalue);
			d = d + kmfi.yaxisPercentIncrement;
		}

		label = df.format(maxPercentage * 100);
		g2.drawString(label, left - 5 - (fm.stringWidth(label)), top + (titleHeight) / 6);
		g2.drawLine(left - 5, top, left, top);

		// Create a rotation transformation for the font.
		AffineTransform fontAT = new AffineTransform();


		// Derive a new font using a rotatation transform
		fontAT.rotate(270 * java.lang.Math.PI / 180);
		Font theDerivedFont = f.deriveFont(fontAT);

		// set the derived font in the Graphics2D context
		g2.setFont(theDerivedFont);

		// Render a string using the derived font
		int yaxisHeight = fm.stringWidth(kmfi.yAxisLegend);
		g2.drawString(kmfi.yAxisLegend, yaxisLabel, (bottom - (int) (.5 * (bottom - top))) + yaxisHeight / 2);

		// put the original font back
		g2.setFont(f);



		double timeDistance = maxTime - minTime;
		double timeIncrement = timeDistance * kmfi.xaxisPercentIncrement;
		double timeInt = (int) Math.floor(timeIncrement);
		if (timeInt < 1.0) {
			timeInt = 1.0;
		}
		adjustedPercentIncrement = timeInt / timeDistance;

		d = adjustedPercentIncrement; //kmfi.xaxisPercentIncrement;
		xAxisTimeValues.clear();
		xAxisTimeCoordinates.clear();

		//if we don't have time values then use percentage to set time. Not perfect but allows different tics
		if (kmfi.xAxisLabels.isEmpty()) {
			xAxisTimeValues.add(minTime);
			xAxisTimeCoordinates.add(left);
			while (d <= 1.0) {
				double xaxisTime = ((minTime * kmfi.timeScale) + d * ((maxTime - minTime) * kmfi.timeScale)); //
				xAxisTimeValues.add(xaxisTime);

				Integer coordinate = left + (int) (d * (right - left));
				xAxisTimeCoordinates.add(coordinate);
				//       System.out.println(d + " " + left + " " + right + " " + coordinate + " " + minTime + " " + maxTime);
				d = d + adjustedPercentIncrement; //kmfi.xaxisPercentIncrement;
			}
		} else {
			minTime = kmfi.xAxisLabels.get(0);
			maxTime = kmfi.xAxisLabels.get(kmfi.xAxisLabels.size() - 1);
			for (Double xaxisTime : kmfi.xAxisLabels) {
				xAxisTimeValues.add(xaxisTime);
				d = (xaxisTime - minTime) / (maxTime - minTime);
				Integer coordinate = left + (int) (d * (right - left));
				xAxisTimeCoordinates.add(coordinate);
			}
		}

		for (int i = 0; i < xAxisTimeValues.size(); i++) {
			Double xaxisTime = xAxisTimeValues.get(i);
			Integer xCoordinate = xAxisTimeCoordinates.get(i);
			label = df.format(xaxisTime);
			if (i == xAxisTimeValues.size() - 1) {
				g2.drawString(label, xCoordinate - (fm.stringWidth(label)), bottom + fm.getHeight() + 5);
			} else {
				g2.drawString(label, xCoordinate - (fm.stringWidth(label) / 2), bottom + fm.getHeight() + 5);
			}
			g2.drawLine(xCoordinate, bottom, xCoordinate, bottom + 5);
		}

		// draw the vertical and horizontal lines
		g2.setStroke(kmfi.axisStroke);
		g2.drawLine(left, top, left, bottom);
		g2.drawLine(left, bottom, right, bottom);

		// draw xAxis legend
		g2.drawString(kmfi.xAxisLegend, getSize().width / 2 - (fm.stringWidth(kmfi.xAxisLegend) / 2), bottom + 2 * fm.getHeight() + 10);
	}
	Double adjustedPercentIncrement = 0.0;

	/**
	 * Get the percentage increment for the time axis
	 *
	 * @return
	 */
	public Double getTimeAxisIncrementPercentage() {
		return adjustedPercentIncrement;
	}

	/**
	 * Reset the various bounds used to draw graph
	 */
	private void setFigureDimensions() {
		fm = getFontMetrics(getFont());
		titleHeight = kmfi.titleHeight;//fm.getHeight();
		xAxisLabelHeight = titleHeight;
		labelWidth = Math.max(fm.stringWidth(df.format(minPercentage)),
				fm.stringWidth(df.format(maxPercentage))) + 5;
		top = kmfi.padding + titleHeight;
		bottom = this.getHeight() - kmfi.padding - xAxisLabelHeight;
		left = kmfi.padding + labelWidth + yaxisLabel;
		right = this.getWidth() - kmfi.padding;

	}

	/**
	 * Combine the KM and Num risk into one image
	 *
	 * @param fileName
	 */
	public void savePNGKMNumRisk(String fileName) {
		if (fileName.startsWith("null") || fileName.startsWith("Null") || fileName.startsWith("NULL")) {
			return;
		}
		this.fileName = fileName;

		NumbersAtRiskPanel numbersAtRiskPanel = new NumbersAtRiskPanel();
		numbersAtRiskPanel.setKaplanMeierFigure(this);
		numbersAtRiskPanel.setSize(this.getWidth(), numbersAtRiskPanel.getHeight());
		BufferedImage imageKM = new BufferedImage(this.getWidth(), this.getHeight(), BufferedImage.TYPE_INT_RGB);
		Graphics2D graphics2D = imageKM.createGraphics();

		this.paint(graphics2D);

		BufferedImage imageNumRisk = new BufferedImage(numbersAtRiskPanel.getWidth(), numbersAtRiskPanel.getHeight(), BufferedImage.TYPE_INT_RGB);
		Graphics2D graphics2DNumRisk = imageNumRisk.createGraphics();
		numbersAtRiskPanel.paint(graphics2DNumRisk);


		BufferedImage image = new BufferedImage(numbersAtRiskPanel.getWidth(), numbersAtRiskPanel.getHeight() + this.getHeight(), BufferedImage.TYPE_INT_RGB);
		Graphics2D g = image.createGraphics();

		g.drawImage(imageKM, 0, 0, null);
		g.drawImage(imageNumRisk, 0, this.getHeight(), null);

		try {
			ImageIO.write(image, "png", new File(fileName));
		} catch (Exception ex) {
			ex.printStackTrace();
		}

	}

	/**
	 *
	 * @param fileName
	 */
	public void savePNG(String fileName) {
		if (fileName.startsWith("null") || fileName.startsWith("Null") || fileName.startsWith("NULL")) {
			return;
		}
		this.fileName = fileName;
		BufferedImage image = new BufferedImage(this.getWidth(), this.getHeight(), BufferedImage.TYPE_INT_RGB);
		Graphics2D graphics2D = image.createGraphics();

		this.paint(graphics2D);
		try {
			ImageIO.write(image, "png", new File(fileName));
		} catch (Exception ex) {
			ex.printStackTrace();
		}

	}

	/**
	 * @param args the command line arguments
	 */
	public static void main(String[] args) {
		// TODO code application logic here
		try {

			KaplanMeierFigure kaplanMeierFigure = new KaplanMeierFigure();
			LinkedHashMap<String, ArrayList<CensorStatus>> survivalDataHashMap = new LinkedHashMap<String, ArrayList<CensorStatus>>();

//            if (false) { //http://sph.bu.edu/otlt/MPH-Modules/BS/BS704_Survival/
//                ArrayList<CensorStatus> graph1 = new ArrayList<CensorStatus>();
//                graph1.add(new CensorStatus("A", 24.0, "0"));
//                graph1.add(new CensorStatus("A", 3.0, "1"));
//                graph1.add(new CensorStatus("A", 11.0, "0"));
//                graph1.add(new CensorStatus("A", 19.0, "0"));
//                graph1.add(new CensorStatus("A", 24.0, "0"));
//                graph1.add(new CensorStatus("A", 13.0, "0"));
//
//                graph1.add(new CensorStatus("A", 14.0, "1"));
//                graph1.add(new CensorStatus("A", 2.0, "0"));
//                graph1.add(new CensorStatus("A", 18.0, "0"));
//                graph1.add(new CensorStatus("A", 17.0, "0"));
//                graph1.add(new CensorStatus("A", 24.0, "0"));
//                graph1.add(new CensorStatus("A", 21.0, "0"));
//                graph1.add(new CensorStatus("A", 12.0, "0"));
//
//                graph1.add(new CensorStatus("A", 1.0, "1"));
//                graph1.add(new CensorStatus("A", 10.0, "0"));
//                graph1.add(new CensorStatus("A", 23.0, "1"));
//                graph1.add(new CensorStatus("A", 6.0, "0"));
//                graph1.add(new CensorStatus("A", 5.0, "1"));
//                graph1.add(new CensorStatus("A", 9.0, "0"));
//                graph1.add(new CensorStatus("A", 17.0, "1"));
//
//                survivalDataHashMap.put("Label 1", graph1);
//
//
//
//            }


			if (true) {



				ArrayList<CensorStatus> graph1 = new ArrayList<CensorStatus>();
				graph1.add(new CensorStatus("A", 1.0, "1"));
				graph1.add(new CensorStatus("A", 1.0, "1"));
				graph1.add(new CensorStatus("A", 1.0, "1"));
				graph1.add(new CensorStatus("A", 2.0, "1"));
				graph1.add(new CensorStatus("A", 2.0, "1"));
				graph1.add(new CensorStatus("A", 3.0, "1"));

				graph1.add(new CensorStatus("A", 4.0, "1"));
				graph1.add(new CensorStatus("A", 4.0, "1"));
				graph1.add(new CensorStatus("A", 4.0, "1"));
				graph1.add(new CensorStatus("A", 4.0, "1"));
				graph1.add(new CensorStatus("A", 4.0, "1"));
				graph1.add(new CensorStatus("A", 4.0, "1"));
				graph1.add(new CensorStatus("A", 4.0, "0"));

				graph1.add(new CensorStatus("A", 5.0, "1"));
				graph1.add(new CensorStatus("A", 5.0, "1"));

				graph1.add(new CensorStatus("A", 8.0, "0"));
				graph1.add(new CensorStatus("A", 8.0, "0"));
				graph1.add(new CensorStatus("A", 8.0, "0"));
				graph1.add(new CensorStatus("A", 8.0, "0"));
				graph1.add(new CensorStatus("A", 8.0, "0"));
				graph1.add(new CensorStatus("A", 8.0, "0"));
				graph1.add(new CensorStatus("A", 8.0, "1"));

				graph1.add(new CensorStatus("A", 9.0, "1"));
				graph1.add(new CensorStatus("A", 9.0, "1"));
				graph1.add(new CensorStatus("A", 9.0, "1"));
				graph1.add(new CensorStatus("A", 9.0, "1"));
				graph1.add(new CensorStatus("A", 9.0, "1"));


				graph1.add(new CensorStatus("A", 13.0, "0"));
				graph1.add(new CensorStatus("A", 13.0, "0"));
				graph1.add(new CensorStatus("A", 13.0, "1"));

				survivalDataHashMap.put("Label 1", graph1);

				ArrayList<CensorStatus> graph2 = new ArrayList<CensorStatus>();
				graph2.add(new CensorStatus("A", 1.0, "1"));
				graph2.add(new CensorStatus("A", 1.0, "1"));
				graph2.add(new CensorStatus("A", 1.0, "0"));
				graph2.add(new CensorStatus("A", 3.0, "0"));
				graph2.add(new CensorStatus("A", 3.0, "1"));
				graph2.add(new CensorStatus("A", 4.0, "1"));

				graph2.add(new CensorStatus("A", 4.0, "1"));
				graph2.add(new CensorStatus("A", 4.0, "1"));
				graph2.add(new CensorStatus("A", 4.0, "1"));
				graph2.add(new CensorStatus("A", 5.0, "1"));
				graph2.add(new CensorStatus("A", 5.0, "0"));
				graph2.add(new CensorStatus("A", 5.0, "0"));
				graph2.add(new CensorStatus("A", 5.0, "0"));

				graph2.add(new CensorStatus("A", 6.0, "1"));
				graph2.add(new CensorStatus("A", 6.0, "0"));

				graph2.add(new CensorStatus("A", 7.0, "0"));
				graph2.add(new CensorStatus("A", 7.0, "0"));
				graph2.add(new CensorStatus("A", 7.0, "0"));
				graph2.add(new CensorStatus("A", 7.0, "0"));
				graph2.add(new CensorStatus("A", 8.0, "1"));
				graph2.add(new CensorStatus("A", 8.0, "1"));
				graph2.add(new CensorStatus("A", 8.0, "1"));

				graph2.add(new CensorStatus("A", 8.0, "1"));
				graph2.add(new CensorStatus("A", 8.0, "1"));
				graph2.add(new CensorStatus("A", 8.0, "0"));
				graph2.add(new CensorStatus("A", 9.0, "0"));
				graph2.add(new CensorStatus("A", 9.0, "1"));


				graph2.add(new CensorStatus("A", 10.0, "0"));
				graph2.add(new CensorStatus("A", 10.0, "0"));
				graph2.add(new CensorStatus("A", 10.0, "0"));

				survivalDataHashMap.put("Label 2", graph2);
			}

			ArrayList<String> figureInfo = new ArrayList<String>();
			//DecimalFormat dfe = new DecimalFormat("0.00E0");
			//DecimalFormat df = new DecimalFormat("0.00");



			JFrame application = new JFrame();
			application.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
			application.add(kaplanMeierFigure);
			kaplanMeierFigure.setSize(500, 400);

			application.setSize(500, 400);         // window is 500 pixels wide, 400 high
			application.setVisible(true);

			ArrayList<String> titles = new ArrayList<String>();
			titles.add("Line 1");
			titles.add("Line 2");
			kaplanMeierFigure.setSurvivalData(titles, survivalDataHashMap, true);

			//   figureInfo.add("HR=2.1 95% CI(1.8-2.5)");
			//   figureInfo.add("p-value=.001");
			kaplanMeierFigure.setFigureLineInfo(figureInfo);

			kaplanMeierFigure.savePNGKMNumRisk("test.png");

		} catch (Exception e) {
			e.printStackTrace();
		}
	}
}
