package org.mote.wiimote.whiteboard.gui;

import java.awt.Color;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Point;
import java.awt.RenderingHints;
import java.awt.SystemColor;
import java.awt.event.ComponentAdapter;
import java.awt.event.ComponentEvent;
import java.awt.event.KeyAdapter;
import java.awt.event.KeyEvent;
import java.util.Map;
import java.util.Timer;
import java.util.TimerTask;

import javax.swing.BorderFactory;
import javax.swing.JDialog;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.JProgressBar;

import net.miginfocom.swing.MigLayout;

import org.jdesktop.application.Action;
import org.jdesktop.application.Application;
import org.mote.wiimote.whiteboard.WiimoteDataHandler;
import org.mote.wiimote.whiteboard.WiimoteWhiteboard;
import org.mote.wiimote.whiteboard.WiimoteDataHandler.WiimoteDataListener;
import org.mote.wiimote.whiteboard.calibration.WiimoteCalibration;
import org.mote.wiimote.whiteboard.calibration.WiimoteCalibration.CalibrationEvent;
import org.mote.wiimote.whiteboard.calibration.WiimoteCalibration.CalibrationEventListener;
import org.mote.wiimote.whiteboard.ds.IRDot;
import org.mote.wiimote.whiteboard.ds.Wiimote;
import org.mote.wiimote.whiteboard.preferences.WWPreferences;
import org.mote.wiimote.whiteboard.util.Util;

@SuppressWarnings("serial")
public class CalibrationInfoWindow extends JDialog implements CalibrationEventListener, WiimoteDataListener {
	
	private static class WiimoteWrapper {
		@SuppressWarnings("unused")
		private int idx;
		private JPanel panel;
		private WiimoteIcon icon;
		private JProgressBar trackingUtility;
		private Double d[];
		private IRDot[] lights = new IRDot[4];
		private void setVisible(boolean visible) {
			panel.setVisible(visible);
			icon.setVisible(visible);
			trackingUtility.setVisible(visible);
		}
	}
	
	private static final long REPAINT_FREQ = 1000 / 25;
	
	private static final Color CALIBRATED_COLOR = SystemColor.textHighlight;
	private static final Color TRACKING_COLOR = SystemColor.text;
	
	private static final Color COLORS[] = {Color.blue, Color.red, Color.green, Color.orange, Color.black, Color.cyan, Color.magenta, Color.pink};
	
	private WiimoteWrapper ww[] = new WiimoteWrapper[WWPreferences.WIIMOTES];
	private WiimoteCalibration calibration;
	private WiimoteDataHandler dh;
	private boolean calibrated = false;

//	private Matrix F;
	
	
	public CalibrationInfoWindow(WiimoteCalibration calibration, WiimoteDataHandler dh) {
		super(Application.getInstance(WiimoteWhiteboard.class).getMainFrame(), Util.getResourceMap(CalibrationInfoWindow.class).getString("info.Action.text"));
		getRootPane().putClientProperty("Window.style", "small");
		this.calibration = calibration;
		this.dh = dh;
		calibration.addCalibrationEventListener(this);
		dh.addWiimoteDataListener(this);
		setLayout(new MigLayout("hidemode 3"));
		// info labels
		final JLabel taLabel = Util.newComponent(JLabel.class, "trackingAreaLabel");
		final JLabel csLabel = Util.newComponent(JLabel.class, "calibratedScreenLabel");
		taLabel.setBorder(BorderFactory.createLineBorder(Color.lightGray));
		csLabel.setBorder(BorderFactory.createLineBorder(Color.lightGray));
		taLabel.setBackground(TRACKING_COLOR);
		csLabel.setBackground(CALIBRATED_COLOR);
		taLabel.setForeground(SystemColor.textText);
		csLabel.setForeground(SystemColor.textHighlightText);
		add(taLabel, "sg 1, split, span, growx, pushx");
		add(csLabel, "sg 1, gapbefore 0, growx, pushx, wrap");
		
		for (int i = 0; i < ww.length; i++) {
			ww[i] = new WiimoteWrapper();
			ww[i].idx = i;
			ww[i].icon = new WiimoteIcon(i+1);
			ww[i].icon.displayConnected(true);
			add(ww[i].panel = new InfoPanel(ww[i]), "flowy, w 400, h 300, grow, push");
			add(ww[i].icon, "cell "+i+" 1, growx, pushx");
			add(ww[i].trackingUtility = Util.newComponent(JProgressBar.class, "trackingUtility"), "cell "+i+" 1, growx, pushx");
			ww[i].setVisible(i == 0);
		}
		
		addKeyListener(new KeyAdapter() {
			@Override
			public void keyPressed(KeyEvent e) {
				if (e.getKeyCode() == KeyEvent.VK_ESCAPE) {
					setVisible(false);
				}
			}
		});
		
		addComponentListener(new ComponentAdapter() {
			@Override
			public void componentMoved(ComponentEvent e) {
				componentResized(null);
			}
			@Override
			public void componentResized(ComponentEvent e) {				
				repaintPanels();
			}
		});
		
		
		Util.getResourceMap(CalibrationInfoWindow.class).injectComponents(this);
		pack();
		Util.placeDialogWindow(this, getWidth(), getHeight());
		new Timer(true).schedule(new UpdateTask(), 0, REPAINT_FREQ);
	}
	
	private class UpdateTask extends TimerTask {
		@Override
		public void run() {
			if (isVisible()) {
				repaintPanels();
			}
		}
	}
	
	private void repaintPanels() {
		for (WiimoteWrapper wrapper : ww) {
			if (wrapper.panel.isVisible())
				wrapper.panel.repaint();
		}
	}
	
	@Action(enabledProperty="calibrated")
	public void info() {
		setVisible(true);
	}
	
	public boolean isCalibrated() {
		return calibrated;
	}
	
	private void updateCalibrated() {
		final boolean old = calibrated;
		calibrated = dh.isConnected() && calibration.isDone() && calibration.isAnyCalibrated(dh.getConnectedWiimotes());
		firePropertyChange("calibrated", old, calibrated);
	}
	
	public void calibrationEvent(CalibrationEvent e) {
		switch (e) {
			case LOADED:
				update();
			case FINISHED:
				update();
				break;
			case SCREEN_CHANGED:
			case STARTED:
			case ABORTED:
			case SAVED:
		}
		updateCalibrated();
		if (!isCalibrated())
			setVisible(false);
	}
	
	public void batteryLevel(Wiimote wiimote, double level) {}
	public void irLights(Wiimote wiimote, IRDot[] lights) {
		ww[wiimote.getId()-1].lights = lights;
	}
	public void irWarped(Map<Wiimote, IRDot[]> data, Point[] warped) {}
	public void wiimoteConnected(Wiimote wiimote) {
		update();
		updateCalibrated();
	}
	public void wiimoteDisconnected(Wiimote wiimote) {}	
	
	private int r(double d) {
		return (int)Math.round(d);
	}
	
	private void update() {
		Map<String, Double[]> finals = calibration.getFinals();
		

		
		for (Wiimote wiimote : dh.getConnectedWiimotes()) {
			WiimoteWrapper w = ww[wiimote.getId()-1];
			if (!calibration.isCalibrated(wiimote)) continue;
			
			w.d = finals.get(wiimote.getAddress());
			
			double x0 = w.d[0]*1024, y0 = w.d[1]*768;
			double x1 = w.d[2]*1024, y1 = w.d[3]*768;
			double x3 = w.d[4]*1024, y3 = w.d[5]*768;
			double x2 = w.d[6]*1024, y2 = w.d[7]*768;
			double idealArea = 0.8 * 1024 * 0.8 * 768;
			double calibratedArea = 0.5 * (Math.abs(
					(x1 - x2) * (y0 - y3) - (x0 - x3) * (y1 - y2)
			));
			
			w.trackingUtility.setValue(r(calibratedArea));
			w.trackingUtility.setString(Util.getResourceMap(CalibrationInfoWindow.class).getString("trackingUtilString", 100 * (calibratedArea / idealArea)));
			
			if (!w.panel.isVisible()) {
				w.setVisible(true);
				pack();
				Util.placeDialogWindow(this, getWidth(), getHeight());
			}
		}
		repaintPanels();
	}
	
	private class InfoPanel extends JPanel {
		private static final int DIAMETER = 6;
		private WiimoteWrapper wrapper;
		public InfoPanel(WiimoteWrapper wrapper) {
			this.wrapper = wrapper;
			setLayout(null);
			setOpaque(true);
			setBackground(TRACKING_COLOR);
			setBorder(BorderFactory.createLineBorder(SystemColor.lightGray));
		}
		@Override
		protected void paintComponent(Graphics g) {
			super.paintComponent(g);
			if (wrapper.d != null) {
				Graphics2D g2d = (Graphics2D)g;
				g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
				final int w = getWidth();
				final int h = getHeight();
				int x[] = new int[5], y[] = new int[5];
				for (int i = 0; i < 4; i++) {
					x[i] = r(wrapper.d[2*i]*w);
					y[i] = r(h-wrapper.d[2*i+1]*h);
				}
				x[4] = x[0]; y[4] = y[0];

				g2d.setColor(CALIBRATED_COLOR);
				g2d.fillPolygon(x, y, 5);
				
				
				for (int i = 0; i < 4; i++) {
					IRDot dot = wrapper.lights[i];
					if (dot != null) {
							g2d.setColor(COLORS[i]);

						g2d.fillOval(r(w*dot.x)-DIAMETER/2, r(h-h*dot.y)-DIAMETER/2, DIAMETER, DIAMETER);

					}
				}
			}
		}		
	}


}
