/*--- formatted by Jindent 2.1, (www.c-lab.de/~jindent) ---*/

/*
 * The package jm.sit is part of the Renal Function Project
 * for analysis of dynamic contrast medium evalutions MRT-Images
 * of the Kidneys.
 * 
 * Copyright (C) 1999 / 2000 Jens Martin
 * 
 * This program is free software; you can redistribute it and/or
 * modify it under the terms of the GNU General Public License 2
 * as published by the Free Software Foundation.
 * 
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 * 
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
 */

package jm.sit;

import jm.kidney.*;
import jm.cepstrum.*;
import jm.sit.*;
import jm.extension.*;
import jm.util.*;

import ij.*;
import java.awt.*;
import java.awt.event.*;
import javax.swing.*;


/**
 * GUI-Element zu Anzeige der berechneten SIT-Kurven. Wird von der Swing-Klasse JLabel abgeleitet-
 * Zeigt jeweils zwei Kurven in einem Koordinatensystem an.
 * Wird von der Klasse SITResults zur Anzeige der Berechnungsergebnisse benoetigt.
 * @see jm.sit.SITResults
 * @version  2.2, 14/02/2000
 * @author   Jens Martin
 */
public class JLabelResults extends JLabel implements MouseListener, MouseMotionListener {
	private int				xMarks = 25;
	private int				yMarks = 10;

	private int				space = 4;
	private Font			verySmallFont = new Font("Dialog", 0, 8);
	private Font			smallFont = new Font("Dialog", 0, 10);
	private Font			normalFont = new Font("Dialog", 0, 12);

	private Color			colorAverageLeft = new Color(120, 0, 0);
	private Color			colorDeviationLeft = new Color(0, 120, 0);
	private Color			colorAverageRight = new Color(100, 0, 0);
	private Color			colorDeviationRight = new Color(0, 100, 0);

	private Rectangle rec = null;
	private int				x, y, w, h, a;
	private String		infoLine = null;
	private double[]	averageGrayLeft = null;
	private double[]	standardDevLeft = null;
	private double[]	averageGrayRight = null;
	private double[]	standardDevRight = null;
	private int				minX, maxX, minY, maxY;

	private boolean		showAverageGray = false;
	private boolean		showStandardDeviation = false;
	private boolean		showMarks = true;
	private boolean		showNullValues = true;

	private int				mouseX, mouseY;
	private boolean		mouseOver = false;
	private double		devScaleFactor = 1.0;


	/**
	 * Der Konstruktor.
	 */
	public JLabelResults() {
		super();
		this.addMouseListener(this);	// .addMouseListener(this);
		this.addMouseMotionListener(this);
	}


	/**
	 * Ueberschreibt die Paint-Mathode der Oberklasse, um das Koordinatensystem sowie
	 * die Kurven auf dem Canvas einzuzeichnen.
	 * @param g Der uebergebene Graphics-Canvas
	 */
	public void paint(Graphics g) {
		double	adder, pos;
		int			i;

		rec = g.getClipBounds();
		a = space;
		x = rec.x;
		y = rec.y;
		w = rec.width;
		h = rec.height;

		// Koordinatensystem einzeichnen
		g.setColor(this.getBackground());
		g.fillRect(x, y, w, h);
		g.setColor(this.getForeground());
		g.drawLine(0 + a, 0, 0 + a, h);
		g.drawLine(0, h - a - 1, w, h - a - 1);
		adder = (double) (h - a - 1) / (double) (yMarks - 1);
		pos = 0.0;
		while (pos < (double) (h - a - 1)) {
			i = (int) Math.round(pos);
			g.drawLine(0, i, 0 + a, i);
			pos = pos + adder;
		} 
		adder = (double) (w - a - 1) / (double) (xMarks - 1);
		pos = (double) (w - 1);
		while (pos > (double) (a)) {
			i = (int) Math.round(pos);
			g.drawLine(i, h - 1, i, h - a - 1);
			pos = pos - adder;
		} 

		// Y-Achsen-Bezeichnung malen
		g.setFont(smallFont);
		if (infoLine != null) {
			g.drawString(infoLine, a + 8, 8);

			// Index und aktuelle Position (Sekunden / Grauwert) einzeichnen
		} 
		drawIndex(g);
		drawCoordinates(g);

		// Kurven zeichnen
		boolean noCurveDrawn = true;
		boolean averageDataAvailable = false;
		boolean deviationDataAvailable = false;

		if (showAverageGray == true) {
			averageDataAvailable = true;
			if (averageGrayLeft != null) {
				drawGraph(g, "o", colorAverageLeft, averageGrayLeft);
				noCurveDrawn = false;
			} 
			if (averageGrayRight != null) {
				drawGraph(g, "x", colorAverageRight, averageGrayRight);
				noCurveDrawn = false;
			} 
		} 
		if (showStandardDeviation == true) {
			deviationDataAvailable = true;
			if (standardDevLeft != null && averageGrayLeft != null) {
				drawStdDev(g, "o", JMConst.LEFT, colorDeviationLeft, averageGrayLeft, standardDevLeft);
				noCurveDrawn = false;
			} 
			if (standardDevRight != null) {
				drawStdDev(g, "x", JMConst.RIGHT, colorDeviationRight, averageGrayRight, standardDevRight);

				// drawGraph(g,"x",colorDeviationLeft, standardDevRight);
				noCurveDrawn = false;
			} 
		} 
		if (noCurveDrawn) {
			g.setFont(normalFont);
			g.setColor(Color.orange);
			if ((showAverageGray == false) && (showStandardDeviation == false)) {
				g.drawString("no items selected", (w / 3), (h / 2) - a - 10);
			} else {
				g.drawString("no data available", (w / 3), (h / 2) - a - 10);
			} 
		} 

	} 


	/**
	 * Setzt die Anzahl der Unterteilungsstriche auf der x-Achse des Koordinatensystems.
	 * @param m die gewuenschte Anzahl der Unterteilungsstriche.
	 */
	public void setXMarks(int m) {
		if (m > 2) {
			xMarks = m;
			this.repaint();
		} 
	} 


	/**
	 * Liest die Anzahl der Unterteilungsstriche auf der x-Achse des Koordinatensystems.
	 * @result int die Anzahl der Unterteilungsstriche.
	 */
	public int getXMarks() {
		return xMarks;
	} 


	/**
	 * Setzt die Anzahl der Unterteilungsstriche auf der y-Achse des Koordinatensystems.
	 * @param m die gewuenschte Anzahl der Unterteilungsstriche.
	 */
	public void setYMarks(int m) {
		if (m > 2) {
			yMarks = m;
			this.repaint();
		} 
	} 


	/**
	 * Setzt die Skalierung der Y-Achse. Es muss ein Wertebereich (min-max)
	 * angegeben werden. Die dargestellten Kurven werden entsprechend mitskaliert.
	 * @param min der minimale Wert der y-Achse
	 * @param max der maximale Wert der y-Achse
	 */
	public void setYScale(int min, int max) {
		minY = min;
		maxY = max;
		this.repaint();
	} 


	/**
	 * Setzt den Skalierungsfaktor fuer die Darstellung der Stadardabweichung.
	 * @param s double-Wert fuer die gewuenschte Skalierung (0,1-1,0)
	 */
	public void setDevScaleFactor(double s) {
		devScaleFactor = s;
		this.repaint();
	} 


	/**
	 * Hilfsfunktion, um einen kleinen Index in das Koordinatensystem zu zeichnen.
	 * @param g uebergebener Graphics-Canvas.
	 */
	private void drawIndex(Graphics g) {
		g.setFont(verySmallFont);
		g.setColor(colorAverageRight);
		int textPosX = g.getClipBounds().width - getFontMetrics(verySmallFont).stringWidth(" - right");
		int x = textPosX - 3;
		int y = g.getClipBounds().height - space - 8;

		g.drawLine(x - 1, y - 1, x + 1, y + 1);
		g.drawLine(x - 1, y + 1, x + 1, y - 1);
		g.drawString(" - left", textPosX, y + 2);

		g.setColor(colorAverageLeft);
		y = y - 10;
		g.drawLine(x - 1, y - 1, x + 1, y - 1);
		g.drawLine(x - 1, y + 1, x + 1, y + 1);
		g.drawLine(x - 1, y - 1, x - 1, y + 1);
		g.drawLine(x + 1, y - 1, x + 1, y + 1);
		g.drawString(" - right", textPosX, y + 2);
	} 


	/**
	 * Hilfsfunktion, um den aktuellen Kurvenwert an der Mausposition im Koordinatensystem
	 * einzuzeichnen. Die Darstellung erfolgt nur, wenn sich der Mauszeiger ueber dem
	 * Koordinatensystem befindet.
	 * @param g uebergebener Graphics-Canvas.
	 */
	private void drawCoordinates(Graphics g) {
		if (mouseOver) {
			int			px = mouseX - space;
			int			py = g.getClipBounds().height - space - mouseY;

			double	valX = (double) (maxX - minX) / (double) (g.getClipBounds().width - space - 1) * (double) px;
			double	valY = (double) (maxY - minY) / (double) (g.getClipBounds().height - space - 1) * (double) py;

			valX = (double) minX + valX;
			valY = (double) minY + valY;

			String	s = "" + (int) valX + " sec / " + (int) valY + " gray";

			g.setFont(smallFont);
			g.setColor(Color.lightGray);

			g.drawString("Position", space + 8, 30);
			g.drawString(s, space + 8, 40);
		} else {

			// Schrift in schwarz zeichnen -> alte loeschen !
			g.setColor(Color.black);
			g.setFont(smallFont);
			g.drawString("Position", space + 8, 30);
			g.drawString("8 sec / 8 gray", space + 8, 40);
			g.drawString("88 sec / 88 gray", space + 8, 40);
			g.drawString("888 sec / 888 gray", space + 8, 40);

		} 
	} 


	/**
	 * Zeichnet eine Kurve entsprechend des uebergebenen Datenarrays in das Koordinatensystem
	 * @param g uebergebener Graphics-Canvas.
	 * @param sign Markierung fuer die Punkte der Kurve. Gueltig sind "x" und "o".
	 * @param c Farbe der zu zeichnenden Kurve.
	 * @param data double-Array mit den Werten der zu zeichnenden Kurve.
	 */
	private void drawGraph(Graphics g, String sign, Color c, double[] data) {
		int i = 0;
		int lx = 0;
		int ly = 0;
		int x, y;

		rec = g.getClipBounds();
		a = space;
		w = rec.width;
		h = rec.height;
		int			arrayLength = data.length;
		double	baseX = (double) (w - a - 1) / (double) (maxX - minX);

		// baseX = baseX + 1.0;
		double	baseY = (double) (h - a - 1) / (double) (maxY - minY);
		double	timePerImage = (double) (maxX - minX) / (double) (arrayLength - 1);

		g.setColor(c);
		for (i = 0; i < arrayLength; i++) {
			y = h - a - 1 - (int) Math.round((data[i] - (double) minY) * baseY);
			x = a + (int) Math.round((double) (i - minX) * baseX * timePerImage);
			if (i == 0) {
				lx = x;
				ly = y;
			} 
			if (showMarks) {
				if (sign.charAt(0) == 'x') {
					g.drawLine(x - 1, y - 1, x + 1, y + 1);
					g.drawLine(x - 1, y + 1, x + 1, y - 1);
					g.drawLine(lx - 1, ly - 1, lx + 1, ly + 1);
					g.drawLine(lx - 1, ly + 1, lx + 1, ly - 1);
				} else {
					g.drawLine(x - 1, y - 1, x + 1, y - 1);
					g.drawLine(x - 1, y + 1, x + 1, y + 1);
					g.drawLine(x - 1, y - 1, x - 1, y + 1);
					g.drawLine(x + 1, y - 1, x + 1, y + 1);
					g.drawLine(lx - 1, ly - 1, lx + 1, ly - 1);
					g.drawLine(lx - 1, ly + 1, lx + 1, ly + 1);
					g.drawLine(lx - 1, ly - 1, lx - 1, ly + 1);
					g.drawLine(lx + 1, ly - 1, lx + 1, ly + 1);
				} 
			} 
			if (showNullValues) {
				g.drawLine(x, y, lx, ly);
			} else {
				if (y < (h - a) && ly < (h - a)) {
					g.drawLine(x, y, lx, ly);
				} 
			} 
			lx = x;
			ly = y;
		} 
	} 


	/**
	 * Zeichnet die Standardabweichung einer Kurve ein.
	 * @param g uebergebener Graphics-Canvas.
	 * @param sign Markierung fuer die Punkte der Kurve. Gueltig sind "x" und "o".
	 * @param c Farbe der zu zeichnenden Kurve.
	 * @param half Bildhaelfte
	 * @param data double-Array mit den Werten der zu zeichnenden Kurve.
	 * @param dev double-Array mit den Standardabweichungen.
	 */
	private void drawStdDev(Graphics g, String sign, int half, Color c, double[] data, double[] dev) {
		int i = 0;
		int lx = 0;
		int ly = 0;
		int x, y, devMin, devMax;

		rec = g.getClipBounds();
		a = space;
		w = rec.width;
		h = rec.height;
		int			arrayLength = data.length;
		double	baseX = (double) (w - a - 1) / (double) (maxX - minX);

		// baseX = baseX + 1.0;
		double	baseY = (double) (h - a - 1) / (double) (maxY - minY);
		double	timePerImage = (double) (maxX - minX) / (double) (arrayLength - 1);

		g.setColor(c);
		for (i = 0; i < arrayLength; i++) {
			y = h - a - 1 - (int) Math.round((data[i] - (double) minY) * baseY);
			x = a + (int) Math.round((double) (i - minX) * baseX * timePerImage);
			devMin = h - a - 1 - (int) Math.round((data[i] - (dev[i] * devScaleFactor) - (double) minY) * baseY);
			devMax = h - a - 1 - (int) Math.round((data[i] + (dev[i] * devScaleFactor) - (double) minY) * baseY);
			if (i == 0) {
				lx = x;
				ly = y;
			} 

			if (showMarks) {
				if (sign.charAt(0) == 'x') {
					g.drawLine(x - 1, y - 1, x + 1, y + 1);
					g.drawLine(x - 1, y + 1, x + 1, y - 1);
					g.drawLine(lx - 1, ly - 1, lx + 1, ly + 1);
					g.drawLine(lx - 1, ly + 1, lx + 1, ly - 1);
				} else {
					g.drawLine(x - 1, y - 1, x + 1, y - 1);
					g.drawLine(x - 1, y + 1, x + 1, y + 1);
					g.drawLine(x - 1, y - 1, x - 1, y + 1);
					g.drawLine(x + 1, y - 1, x + 1, y + 1);
					g.drawLine(lx - 1, ly - 1, lx + 1, ly - 1);
					g.drawLine(lx - 1, ly + 1, lx + 1, ly + 1);
					g.drawLine(lx - 1, ly - 1, lx - 1, ly + 1);
					g.drawLine(lx + 1, ly - 1, lx + 1, ly + 1);
				} 
			} 
			if (i > 0) {
				if (showNullValues) {
					if (half == JMConst.LEFT) {
						g.drawLine(x + 1, devMin, x + 1, devMax);

						// g.drawLine(x+1,y,x+1,y+(int)(dev[i]));
					} else {
						g.drawLine(x - 1, devMin, x - 1, devMax);

						// g.drawLine(x-1,y,x-1,y-(int)(dev[i]));
						// g.drawLine(x-1,y,x-1,y+(int)(dev[i]));
					} 

				} else {
					if (y < (h - a) && ly < (h - a)) {
						if (half == JMConst.LEFT) {
							g.drawLine(x + 1, devMin, x + 1, devMax);

							// g.drawLine(x+1,y,x+1,y-(int)(dev[i]));
							// g.drawLine(x+1,y,x+1,y+(int)(dev[i]));
						} else {
							g.drawLine(x - 1, devMin, x - 1, devMax);

							// g.drawLine(x-1,y,x-1,y-(int)(dev[i]));
							// g.drawLine(x-1,y,x-1,y+(int)(dev[i]));
						} 
					} 
				} 
			} 
			lx = x;
			ly = y;
		} 
	} 


	/**
	 * Setzt die Skalierung der X-Achse. Es muss ein Wertebereich (min-max)
	 * angegeben werden. Die dargestellten Kurven werden entsprechend mitskaliert.
	 * @param min der minimale Wert der x-Achse
	 * @param max der maximale Wert der x-Achse
	 */
	public void setXScale(int min, int max) {
		minX = min;
		maxX = max;
		this.repaint();
	} 


	/**
	 * Liest die Anzahl der Unterteilugnen der Y-Achse.
	 * @result int die Anzahl der dargestellten Unterteilungsstriche
	 */
	public int getYMarks() {
		return yMarks;
	} 


	/**
	 * Setzt die Achsenbeschriftung der y-Achse.
	 * @param text die darzustellende Beschriftung als String
	 */
	public void setText(String text) {
		if (text != null) {
			infoLine = text;
			this.repaint();
		} 
	} 


	/**
	 * Setzt die Farbe der Kurve fuer die Darstellung der mittleren Grauwerte.
	 * @param c die gewuenschte Farbe der Darstellung
	 */
	public void setAverageCurveColor(Color c) {
		if (c != null) {
			colorAverageLeft = c;
			int r = c.getRed();
			int g = c.getGreen();
			int b = c.getBlue();

			if (r > 60) {
				r = r - 60;
			} 
			if (g > 60) {
				g = g - 60;
			} 
			if (b > 60) {
				b = b - 60;
			} 
			colorAverageRight = new Color(r, g, b);
			this.repaint();
		} 
	} 


	/**
	 * Setzt die Farbe der Kurve fuer die Darstellung der Standardabweichung
	 * @param c die gewuenschte Farbe der Darstellung
	 */
	public void setDeviationCurveColor(Color c) {
		if (c != null) {
			colorDeviationLeft = c;
			int r = c.getRed();
			int g = c.getGreen();
			int b = c.getBlue();

			if (r > 60) {
				r = r - 60;
			} 
			if (g > 60) {
				g = g - 60;
			} 
			if (b > 60) {
				b = b - 60;
			} 
			colorDeviationRight = new Color(r, g, b);
			this.repaint();
		} 
	} 


	/**
	 * Uebergibt die Daten der Kruve fuer den mittleren Grauwert.
	 * @param half die Bildhaelfte
	 * @param data double-Array mit den Kurvenwerten
	 */
	public void setAverageGray(int half, double[] data) {
		if (half == JMConst.LEFT) {
			averageGrayLeft = data;
		} 
		if (half == JMConst.RIGHT) {
			averageGrayRight = data;
		} 
		this.repaint();
	} 


	/**
	 * Uebergibt die Daten der Kruve fuer die Standardabweichung
	 * @param half die Bildhaelfte
	 * @param data double-Array mit den Kurvenwerten
	 */
	public void setStandardDeviation(int half, double[] data) {
		if (half == JMConst.LEFT) {
			standardDevLeft = data;
		} 
		if (half == JMConst.RIGHT) {
			standardDevRight = data;
		} 
		this.repaint();
	} 


	/**
	 * Legt fest, ob die Kurve fuer die mittleren Grauwerte angezeigt wird.
	 * @param show true, wenn die Kurve angezeigt werden soll.
	 */
	public void showAverageGrayValues(boolean show) {
		showAverageGray = show;
		this.repaint();
	} 


	/**
	 * Legt fest, ob die Kurve fuer die Standdardabweichungen angezeigt wird.
	 * @param show true, wenn die Kurve angezeigt werden soll.
	 */
	public void showStandardDeviationValues(boolean show) {
		showStandardDeviation = show;
		this.repaint();
	} 


	/**
	 * Legt fest, ob die Markierungen an den Kurvenpunkten angezeigt werden.
	 * @param show true, wenn die Markierungen angezeigt werden sollen.
	 */
	public void showMarks(boolean show) {
		showMarks = show;
		this.repaint();
	} 


	/**
	 * Legt fest, ob die Nullwerte innerhalb der Kurve angezeigt werden.
	 * @param show true, wenn Nullwerte angezeigt werden sollen.
	 */
	public void showNullValues(boolean show) {
		showNullValues = show;
		this.repaint();
	} 


	/**
	 * Faengt Events ab, die durch Mausbewegungen ausgeloest werden. Wird benoetigt, um festzustellen,
	 * ob der Benutzer den Mauszeiger ueber das Koordinatensystem bewegt hat.
	 * @param e MouseEvent
	 */
	public void mouseEntered(java.awt.event.MouseEvent e) {
		mouseOver = true;
		repaint();
	} 


	/**
	 * Faengt Events ab, die durch Mausbewegungen ausgeloest werden. Wird benoetigt, um festzustellen,
	 * ob der Benutzer den Mauszeiger ueber das Koordinatensystem bewegt hat.
	 * @param e MouseEvent
	 */
	public void mouseExited(java.awt.event.MouseEvent e) {
		mouseOver = false;
		repaint();
	} 


	/**
	 * Faengt Events ab, die durch Klicken der Maustasten oder Bewegung der Maus
	 * ausgeloest werden. Wird benoetigt, um festzustellen,
	 * ob der Benutzer den Mauszeiger ueber das Koordinatensystem bewegt hat.
	 * @param e MouseEvent
	 */
	public void mouseReleased(java.awt.event.MouseEvent e) {}


	/**
	 * Faengt Events ab, die durch Klicken der Maustasten oder Bewegung der Maus
	 * ausgeloest werden. Wird benoetigt, um festzustellen,
	 * ob der Benutzer den Mauszeiger ueber das Koordinatensystem bewegt hat.
	 * @param e MouseEvent
	 */
	public void mousePressed(java.awt.event.MouseEvent e) {}


	/**
	 * Faengt Events ab, die durch Klicken der Maustasten oder Bewegung der Maus
	 * ausgeloest werden. Wird benoetigt, um festzustellen,
	 * ob der Benutzer den Mauszeiger ueber das Koordinatensystem bewegt hat.
	 * @param e MouseEvent
	 */
	public void mouseClicked(java.awt.event.MouseEvent e) {}


	/**
	 * Faengt Events ab, die durch Klicken der Maustasten oder Bewegung der Maus
	 * ausgeloest werden. Wird benoetigt, um festzustellen,
	 * ob der Benutzer den Mauszeiger ueber das Koordinatensystem bewegt hat.
	 * @param e MouseEvent
	 */
	public void mouseMoved(java.awt.event.MouseEvent e) {
		mouseX = e.getX();
		mouseY = e.getY();
		repaint();
	} 


	/**
	 * Faengt Events ab, die durch Klicken der Maustasten oder Bewegung der Maus
	 * ausgeloest werden. Wird benoetigt, um festzustellen,
	 * ob der Benutzer den Mauszeiger ueber das Koordinatensystem bewegt hat.
	 * @param e MouseEvent
	 */
	public void mouseDragged(java.awt.event.MouseEvent e) {}

}


/*--- formatting done in "My Own Convention" style on 08-04-2000 ---*/

