package org.mote.wiimote.whiteboard.gui;

import java.awt.Color;
import java.awt.Font;
import java.awt.Graphics;
import java.awt.Point;
import java.awt.SystemColor;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.WindowEvent;
import java.awt.event.WindowFocusListener;
import java.util.Map;
import java.util.logging.Level;

import javax.swing.BorderFactory;
import javax.swing.ButtonGroup;
import javax.swing.ImageIcon;
import javax.swing.JButton;
import javax.swing.JCheckBox;
import javax.swing.JLabel;
import javax.swing.JOptionPane;
import javax.swing.JPanel;
import javax.swing.JProgressBar;
import javax.swing.JRadioButton;
import javax.swing.JSeparator;
import javax.swing.SwingConstants;

import net.miginfocom.swing.MigLayout;

import org.jdesktop.application.Action;
import org.jdesktop.application.Application;
import org.jdesktop.application.ResourceMap;
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 MainPanel extends JPanel implements WiimoteDataListener, CalibrationEventListener {
	
	private static final ImageIcon CALIBRATED = new ImageIcon(MainPanel.class.getResource("resources/icons/ok.png"));
	private static final ImageIcon NOT_CALIBRATED = new ImageIcon(MainPanel.class.getResource("resources/icons/warning.png"));
	
	private static final WWPreferences prefs = WWPreferences.getPreferences();

	private DotLabel[] dotLabel = new DotLabel[4];
	private JProgressBar[] batteryLevel = new JProgressBar[WWPreferences.WIIMOTES];
	private JButton[] resetButton = new JButton[WWPreferences.WIIMOTES];
	private WiimoteIcon[] wiimoteIcon = new WiimoteIcon[WWPreferences.WIIMOTES];
	private JLabel[] statusLabel = new JLabel[WWPreferences.WIIMOTES];
	private JCheckBox cursorControl;
	private JRadioButton moveMouse, leftClick;
	private JButton calibrationButton, cameraButton 
	private ScreenSelector screenSelector;
	
	private boolean notifiedLowBattery = false;
	
	private Wiimote[] wiimotes = new Wiimote[WWPreferences.WIIMOTES];
	
	private WiimoteDataHandler dh;
	private WiimoteCalibration calibration;
	private ResourceMap r = Util.getResourceMap(MainPanel.class);
	
	public MainPanel(WiimoteDataHandler dh, WiimoteCalibration calibration) {
		this.dh = dh;
		this.calibration = calibration;
		
		dh.addWiimoteDataListener(this);
		calibration.addCalibrationEventListener(this);
		
		createComponents();

		getValues();
		update();
	}
	
	public void getValues() {
		cursorControl.setSelected(dh.isCursorControl());
		leftClick.setSelected(prefs.isLeftClick());
		moveMouse.setSelected(!prefs.isLeftClick());
	}
	
	public void calibrationEvent(CalibrationEvent e) {
		switch (e) {
			case FINISHED:
			case LOADED:
				cursorControl.setSelected(WWPreferences.MOUSE_CONTROL_AFTER_CALIBRATION);
			case STARTED:
			case SCREEN_CHANGED:
			case ABORTED:
				update();
				break;
		}	
	}
	
	public void batteryLevel(Wiimote wiimote, double level) {
		batteryLevel[wiimote.getId()-1].setValue((int) Math.round(level * 100));
		batteryLevel[wiimote.getId()-1].setString(r.getString("batteryLevel", level * 100));
		
		if (!notifiedLowBattery && prefs.isLowBatteryWarning() && dh.isConnected(wiimote) && level <= .05) {
			notifiedLowBattery = true;
			WiimoteWhiteboard.getLogger().log(Level.WARNING, r.getString("lowBattery"));
			new Thread(new Runnable() {
				public void run() {
					JOptionPane.showMessageDialog(null, r.getString("lowBattery"), WiimoteWhiteboard.getProperty("id"), JOptionPane.WARNING_MESSAGE);
				}
			}).start();
		}
	}
	
	public void irLights(Wiimote wiimote, IRDot[] lights) {
		for (int i = 0; i < lights.length; i++) {
			dotLabel[i].update(wiimote, lights[i] != null);
		}
	}
	
	public void irWarped(Map<Wiimote, IRDot[]> data, Point[] warped) {
	}	
	
	public void wiimoteConnected(Wiimote wiimote) {
		wiimotes[wiimote.getId()-1] = wiimote;
		
		if (wiimote.getId() > 1) {
			for (int i = 0; i < wiimote.getId(); i++) {
				wiimoteIcon[i].setVisible(true);
			}
		}
		statusLabel[wiimote.getId()-1].setVisible(true);

		batteryLevel[wiimote.getId()-1].setVisible(true);
		resetButton[wiimote.getId()-1].setVisible(true);
		cursorControl.setSelected(WWPreferences.MOUSE_CONTROL_AFTER_CALIBRATION);
		update();
	}
	
	public void wiimoteDisconnected(Wiimote wiimote) {
		if (wiimote.getId() != 1) batteryLevel[wiimote.getId()-1].setVisible(false);
		if (wiimote.getId() != 1) wiimoteIcon[wiimote.getId()-1].setVisible(false);
		if (wiimote.getId() != 1) resetButton[wiimote.getId()-1].setVisible(false);
		if (wiimote.getId() != 1) statusLabel[wiimote.getId()-1].setVisible(false);
		update();
		JOptionPane.showMessageDialog(null, r.getString("disconnected", wiimote.getId()), WiimoteWhiteboard.getProperty("id"), JOptionPane.ERROR_MESSAGE);
		Application.getInstance(WiimoteWhiteboard.class).exit();
	}
	
	
	/*
	 * UPDATE UI WIDGETS
	 */
	
	@Action
	public void update() {
		calibrationButton.setEnabled(dh.isConnected());
		for (int i = 0; i < WWPreferences.WIIMOTES; i++) {
			resetButton[i].setEnabled(dh.isConnected(wiimotes[i]));
			wiimoteIcon[i].displayConnected(dh.isConnected(wiimotes[i]));
			if (dh.isConnected(wiimotes[i])) {				
				boolean calibrated = calibration.isCalibrated(wiimotes[i]);
				statusLabel[i].setText(r.getString(calibrated ? "calibrated" : "notCalibrated"));
				statusLabel[i].setIcon(calibrated ? CALIBRATED : NOT_CALIBRATED);
			}
		}
		cameraButton.setEnabled(dh.isConnected());
		screenSelector.setEnabled(dh.isConnected() && !calibration.inProgress());		
		
		if (!dh.isConnected()) {
			batteryLevel[0].setString(r.getString("searching"));
			batteryLevel[0].setValue(0);
			batteryLevel[0].setToolTipText(Util.getResourceMap(MainPanel.class).getString("batteryLevelBar.toolTipText"));
		} else {
			batteryLevel[0].setToolTipText(null);
		}
		
		cursorControl.setEnabled(dh.isConnected() && calibration.isDone() && calibration.isAnyCalibrated(dh.getConnectedWiimotes()));
		if (!cursorControl.isEnabled())
			cursorControl.setSelected(false);
		
		moveMouse.setEnabled(cursorControl.isSelected());
		leftClick.setEnabled(cursorControl.isSelected());

		dh.setCursorControl(cursorControl.isSelected());
		prefs.setLeftClick(leftClick.isSelected());
	}
	
	/*
	 * MAIN PANEL
	 */
	
	private void createComponents() {
		setLayout(new MigLayout("nocache, hidemode 3, gap 0", "[fill]"));
		
		addHeadline(r.getString("wiimoteHeadline"), false);
		
		for (int i = 0; i < WWPreferences.WIIMOTES; i++) {
			add(wiimoteIcon[i] = new WiimoteIcon(i+1), "split");
			if (i > 0) wiimoteIcon[i].setVisible(false);
		}
		
		for (int i = 0; i < WWPreferences.WIIMOTES; i++) {
			add(batteryLevel[i] = Util.newComponent(JProgressBar.class, "batteryLevelBar"), (i == 0 ? "newline related/2, " : "") + "split");
			if (i > 0) batteryLevel[i].setVisible(false);
		}

		for (int i = 0; i < WWPreferences.WIIMOTES; i++) {
			add(statusLabel[i] = Util.newComponent(JLabel.class, "statusLabel"), (i == 0 ? "newline related/2, split, " : "") + "sg, h 16!, center");
			statusLabel[i].setFont(statusLabel[i].getFont().deriveFont(10f));
			if (i > 0) statusLabel[i].setVisible(false);
		}

		String gap = Util.MAC_OS_X_LEOPARD_OR_HIGHER ? "0" : "related/2";
		for (int i = 0; i < WWPreferences.WIIMOTES; i++) {
			final int j = i;
			add(resetButton[i] = Util.newComponent(JButton.class, "resetCameraButton"), (i == 0 ? "newline "+gap+", split, " : "") + "");
			resetButton[i].putClientProperty("JButton.buttonType", "textured");
			resetButton[i].putClientProperty("JComponent.sizeVariant", "small");
			resetButton[i].addActionListener(new ActionListener() {
				public void actionPerformed(ActionEvent e) {
					try {
						dh.enableIR(wiimotes[j]);
					} catch (Exception e1) {
						e1.printStackTrace();
					}
				}
			});
			if (i > 0) resetButton[i].setVisible(false);
		}
		
		add(new JSeparator(), String.format("newline %s, span, growx, wrap related", Util.MAC_OS_X_LEOPARD_OR_HIGHER ? "related/3" : "related"));
		
		add(Util.newComponent(JLabel.class, "visibleDotsLabel"), "split" + (Util.MAC_OS_X ? ", gapleft 4" : ""));		
		add(dotLabel[0] = new DotLabel("1"), "w 18, h 18");
		add(dotLabel[1] = new DotLabel("2"), "w 18, h 18");
		add(dotLabel[2] = new DotLabel("3"), "w 18, h 18");
		add(dotLabel[3] = new DotLabel("4"), "w 18, h 18, wrap related" + (Util.MAC_OS_X ? ", gapright 4" : ""));

		add(cameraButton = new JButton(Util.getAction(new CameraMonitor(dh), "monitor")), "");
		cameraButton.putClientProperty("JButton.buttonType", "textured");
		
		// CALIBRATION
		
		addHeadline(r.getString("calibrationHeadline"));
		
		
		final JButton infoButton = new JButton(Util.getAction(new CalibrationInfoWindow(calibration, dh), "info"));
		infoButton.putClientProperty("JButton.buttonType", "textured");
		infoButton.putClientProperty("JComponent.sizeVariant", "small");
		add(infoButton, "wrap related");

		
		add(screenSelector = new ScreenSelector(calibration, dh), "align center, grow 0, wrap related");

		add(calibrationButton = Util.newComponent(JButton.class, "calibrationButton"), "");
		calibrationButton.putClientProperty("JButton.buttonType", "textured");
		calibrationButton.addActionListener(new ActionListener() {
			public void actionPerformed(ActionEvent e) {
				calibration.start(dh.getConnectedWiimotes());
			}
		});

		// MOUSE CONTROL
	
		addHeadline(r.getString("mouseControlHeadline"));
		
		add(cursorControl = Util.newComponent(JCheckBox.class, "cursorControl"), "wrap related");
		add(moveMouse = Util.newComponent(JRadioButton.class, "moveMouse"), "pad 0 20 0 0, wmin pref+20, wrap related");
		add(leftClick = Util.newComponent(JRadioButton.class, "leftClick"), "pad 0 20 0 0, wmin pref+20, wrap");

		ButtonGroup group = new ButtonGroup();
		group.add(moveMouse);
		group.add(leftClick);

		cursorControl.addActionListener(Util.getAction(this, "update"));
		moveMouse.addActionListener(Util.getAction(this, "update"));
		leftClick.addActionListener(Util.getAction(this, "update"));

		Application.getInstance(WiimoteWhiteboard.class).getMainFrame().addWindowFocusListener(new WindowFocusListener() {
			public void windowGainedFocus(WindowEvent e) {
				updateUI();
			}
			public void windowLostFocus(WindowEvent e) {}
		});
		
		Util.getResourceMap(MainPanel.class).injectComponents(this);
	}
	
	private JLabel addHeadline(String name) {
		return addHeadline(name, true);
	}
		
	private JLabel addHeadline(String name, boolean newline) {
		final JLabel label = new JLabel(name);
		label.setFont(label.getFont().deriveFont(Font.BOLD, 18f));
		add(label, (newline ? "newline unrelated, " : "") + "split, span");
		add(new JSeparator(), "growx, wrap related");
		return label;
	}
	
	private class DotLabel extends JLabel {
		
		private boolean[] state = new boolean[WWPreferences.WIIMOTES];
		
		public DotLabel(String name) {
			setText(name);
			setHorizontalAlignment(SwingConstants.CENTER);
			setBorder(BorderFactory.createLineBorder(Color.lightGray));
		}
		
		public void update(Wiimote wiimote, boolean state) {
			if (this.state[wiimote.getId() - 1] != state) {
				this.state[wiimote.getId() - 1] = state;
				repaint();
			}
		}
		
		@Override
		protected void paintComponent(Graphics g) {
			final int n = Math.max(1, dh.getNumberOfConnectedWiimotes());
			for (int i = 1; i <= n; i++) {
				g.setColor(state[i-1] ? WiimoteIcon.COLORS[i-1] : SystemColor.textInactiveText);
				g.fillRect(0, (i-1)*this.getHeight()/n, this.getWidth(), this.getHeight()/n);
			}
			super.paintComponent(g);
		}
		
	}
}
