/**
 * CertainTrust SDK
 * 
 * Implements the computational trust model "CertainTrust"
 * in Java.
 * See <http://www.tk.informatik.tu-darmstadt.de/de/research/smart-security-and-trust/> for further details.
 * 
 * 
 * Telecooperation Department, Technische Universit�t Darmstadt
 * <http://www.tk.informatik.tu-darmstadt.de/>
 * 
 * Prof. Dr. Max Mühlhäuser <max@informatik.tu-darmstadt.de>
 * Florian Volk <florian.volk@cased.de>
 * 
 * 
 * @author	David Kalnischkies
 * @version	1.0
 */

/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */


package CertainTrust;
import java.awt.Canvas;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.Image;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.awt.event.MouseMotionAdapter;
import java.util.HashMap;
import java.util.Map;
import java.util.Observable;
import java.util.Observer;


/**
 * The canvas is a lovely image trying to visualize
 * certainty and trust in an easy understandable way.
 *
 * @author David Kalnischkies <kalnischkies@gmail.com>
 */
public class CertainTrustSimpleCanvas extends Canvas implements Observer {
	private static final long serialVersionUID = -8020598465155085223L;

	private static final int CROSSHAIRLENGTH = 5;
	private CertainTrustSimple cl;
	private Map<String,String> config;

	private Image buffer;
	private double old_f = -1;

	/**
	 * Constructs a new Canvas to display the certainlogic element
	 * @param certainlogic element to display
	 */
	public CertainTrustSimpleCanvas(CertainTrustSimple certainlogic) {
		setup(certainlogic, null);
	}
	/**
	 * Constructs a new Canvas to display the certainlogic element
	 * 
	 * Supported config options are:
	 * - readonly.mouse: disables all mouse actions if set to "true"
	 * - canvas.height and canvas.width to define the dimensions
	 * 
	 * @param certainlogic element to display
	 * @param config to use for this HTI
	 */
	public CertainTrustSimpleCanvas(CertainTrustSimple certainlogic, Map<String,String> config) {
		setup(certainlogic, config);
	}
	
	private void setup(CertainTrustSimple certainlogic, Map<String,String> config) {
		if (config != null)
			this.config = config;
		else
			this.config = new HashMap<String,String>();
		
		// set the requested dimensions
		if (this.config.containsKey("canvas.width") == false)
			this.config.put("canvas.width", "120");
		if (this.config.containsKey("canvas.height") == false)
			this.config.put("canvas.height", "100");
		final int canvasWidth = Integer.parseInt(this.config.get("canvas.width")); 
		final int canvasHeight = Integer.parseInt(this.config.get("canvas.height"));
		setPreferredSize(new Dimension(canvasWidth, canvasHeight));
		
		cl = certainlogic;
		cl.addObserver(this);

		// register for mouse events if required
		if (this.config.containsKey("readonly") == false) {
			if (this.config.containsKey("readonly.mouse") == false)
				this.config.put("readonly.mouse", "false");
		} else {
			String readOnly = this.config.get("readonly");
			if (this.config.containsKey("readonly.mouse") == false)
				this.config.put("readonly.mouse", readOnly);
		}
		if (this.config.get("readonly.mouse").compareToIgnoreCase("false") == 0) {
			addMouseListener(new MouseAdapter () {
			      @Override
				public void mousePressed(MouseEvent e) {
				  setTCByMouseEvent(e, canvasWidth, canvasHeight);
			    }});
	
			 addMouseMotionListener(new MouseMotionAdapter () {
			      @Override
				public void mouseDragged(MouseEvent e) {
				  setTCByMouseEvent(e, canvasWidth, canvasHeight);
			     }});
		} else if (this.config.get("readonly.mouse").compareToIgnoreCase("true") != 0)
			throw new IllegalArgumentException("Mouse has no valid readonly setting");

		// call paint() indirectly the first time
		update(null, null);
	}
	
	private void setTCByMouseEvent(MouseEvent e, int canvasWidth, int canvasHeight) {
		// mouse position to x and y coordinate 
		double pointer_x = Math.max(Math.min(canvasWidth, e.getX()), 0);
		double pointer_y = Math.max(Math.min(canvasHeight, canvasHeight - e.getY()), 0);
		// ensure that the value is in the interval [0;1]
		double t = Math.max(Math.min(pointer_x / canvasWidth, 1.0), 0.0);
		double c = Math.max(Math.min(pointer_y / canvasHeight, 1.0), 0.0);
		cl.setTC(t, c);
	}

	@Override
	public void update(Observable ignored1, Object ignored2) {
		repaint();
	}

	@Override
	public void paint(Graphics g) {
		final int canvasWidth = Integer.parseInt(this.config.get("canvas.width")); 
		final int canvasHeight = Integer.parseInt(this.config.get("canvas.height"));
		if (null == buffer) {
			buffer = createImage(canvasWidth, canvasHeight);
		}

		// do we need to update the colorgradient?
		double initf = cl.getF();
		if (old_f != initf) {
			Graphics bg = buffer.getGraphics();
			for (int y = 0; y < canvasHeight; ++y){
				double certainty = 1 - ((double)y / canvasHeight);
				double resultp2 = ((1 - certainty) * initf);
				for (int x = 0;x < canvasWidth; ++x){
					double trust = (double)x / canvasWidth;
					double result = (trust * certainty) + resultp2;

					if (result < 0.5) {
						bg.setColor(new Color(255, (int) Math.min(255, (255 * 2 * result)), 0));
					} else {
						bg.setColor(new Color((int) Math.min(255, (2 - (2 * result)) * 255), 255, 0));
					}

					bg.drawLine(x, y, x, y);
				}
			}
			old_f = initf;
		}
		g.drawImage(buffer, 0, 0, this);

		// draw the crosshair
		g.setColor(Color.BLACK);
		int pointer_x = (int) Math.round(cl.getT() * canvasWidth);
		int pointer_y = (int) Math.round((1 - cl.getC()) * canvasHeight);
		g.drawLine(pointer_x - CROSSHAIRLENGTH, pointer_y, pointer_x + CROSSHAIRLENGTH, pointer_y);
		g.drawLine(pointer_x, pointer_y - CROSSHAIRLENGTH, pointer_x, pointer_y + CROSSHAIRLENGTH);
	}
}
