package kr.gui.kreventgraph;

import java.awt.Color;
import java.awt.Component;
import java.awt.Graphics2D;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.KeyEvent;
import java.awt.event.KeyListener;
import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;

import javax.swing.JComponent;
import javax.swing.JPopupMenu;
import javax.swing.event.MouseInputAdapter;
import javax.swing.event.PopupMenuEvent;
import javax.swing.event.PopupMenuListener;

import kr.gui.GuiModel;
import kr.gui.MidiTextGraph;
import kr.gui.TimeSyncedController;
import kr.gui.midigraph.MidiTuneGraph.MousePositionStatus;
import kr.gui.util.MouseInputUIMode;
import kr.gui.util.UIMode;
import kr.gui.util.UIModeHandler;
import kr.miditunemodel.ChangeKREventOperation;
import kr.miditunemodel.ChangeNoteOperation;
import kr.miditunemodel.EventGroup;
import kr.miditunemodel.EventGuiModel;
import kr.miditunemodel.KREvent;
import kr.miditunemodel.KREventGuiModel;
import kr.miditunemodel.KREventTransaction;
import kr.miditunemodel.MidiNoteTransaction;
import kr.miditunemodel.MidiTuneModel;
import kr.miditunemodel.NoteEvent;
import kr.miditunemodel.NoteEventGuiModel;
import kr.util.Listener;
import kr.util.ListenerManager;

public class KREventGraphController extends TimeSyncedController implements Listener
{
	private MidiTuneModel midiTuneModel;

	private KREventGraph eventGraph;
	
	private UIModeHandler uiModeHandler;

	private KREventGuiModel krEventGuiModel;


	public KREventGraphController() {
	}

	/**
	 * Removes the last mode from the stack and removes it as a listener.
	 */
	public void init(GuiModel guiModel, MidiTuneModel midiModel,
			KREventGraph eventGraph, KREventGuiModel krEventGuiModel) {
		super.init(guiModel, eventGraph);
		this.midiTuneModel = midiModel;
		this.eventGraph = eventGraph;
		this.krEventGuiModel = krEventGuiModel;

		
		uiModeHandler = new UIModeHandler(eventGraph);
		uiModeHandler.addMode(new NormalUIMode());
		
		ListenerManager.inst().registerListener(eventGraph.getEventTypeEditComponent(), this);
		ListenerManager.inst().registerListener(krEventGuiModel, this);
		ListenerManager.inst().registerListener(midiModel.getKREventGroup(), this);
	}

	public void notify(Object source, Object type, Object... values) {
		if(source == midiTuneModel.getKREventGroup())
		{
			if(type ==  EventGroup.MessageType.EVENTS_CHANGED)
			{
				//PERF Don't repaint the whole screen, just redraw the event
				eventGraph.repaintScrollingData();
			}
		}
		if(source == eventGraph.getEventTypeEditComponent())
		{
			krEventGuiModel.updateType(krEventGuiModel.getActiveEvent(),KREvent.Type.getTypeFromText((String)values[0]));
		}
		if(source == krEventGuiModel)
		{
			if(type == KREventGuiModel.MessageType.TEXT_UPDATED || 
					type == EventGuiModel.MessageType.SELECTION_CHANGED)
				//PERF Don't repaint the whole screen, just redraw the event
				eventGraph.repaintScrollingData();
		}
	}

	/**
	 * Normal mode when no buttons are held down
	 */
	private class NormalUIMode  implements UIMode, MouseListener, KeyListener
	{
		public void start(Component comp) {
			comp.addMouseListener(this);
			comp.addKeyListener(this);		
		}

		public void stop(Component comp) {
			comp.removeMouseListener(this);
			comp.removeKeyListener(this);		
		}

		public void mousePressed(MouseEvent e) {
			eventGraph.requestFocus();
			JPopupMenu editComp = eventGraph.getEventTypeEditComponent();
			//editComp.addActionListener(this);

			long micros = eventGraph.pixelsToMicros(e.getX());
			
			KREvent event = eventGraph.getEvent(e.getX(), e.getY());
			
			if(e.getButton() != MouseEvent.BUTTON1) //right mouse button
			{
				if(event != null)
				{
					System.out.println("edit event");
					krEventGuiModel.setActiveEvent(event);
					krEventGuiModel.addSelectedEvent(event);
					eventGraph.editEvent(e.getX(), e.getY());
				}
				else
				{
					//create a new note, and let the user choose the event
					krEventGuiModel.clearSelectedEvents();
					KREvent ke = new KREvent(KREvent.Type.BASS_OFF, eventGraph.pixelsToMicros(e.getX()));
					midiTuneModel.getKREventGroup().insertEvent(ke);
					
					krEventGuiModel.setActiveEvent(ke);
					krEventGuiModel.addSelectedEvent(krEventGuiModel.getActiveEvent());
					eventGraph.editEvent(e.getX(), e.getY());
					
				}
			}
			else { //left mouse button
				if(event != null)
				{
					System.out.println("select event");
					krEventGuiModel.clearSelectedEvents();
					krEventGuiModel.setActiveEvent(event);
					krEventGuiModel.addSelectedEvent(event);
	
					uiModeHandler.addMode(new MoveEventUIMode(e.getX()));
				}
			}
		}

		public void mouseClicked(MouseEvent e) {
		}

		public void mouseReleased(MouseEvent e) {
		}

		public void mouseEntered(MouseEvent e) {
		}

		public void mouseExited(MouseEvent e) {
		}

		public void keyTyped(KeyEvent e) {
		}

		public void keyPressed(KeyEvent e) {
			if(e.getKeyCode() == e.VK_DELETE)
			{
				boolean containsLastEvent = false;
				
				for(KREvent ne : krEventGuiModel.getSelectedEvents())
				{
					midiTuneModel.getKREventGroup().removeEvent(ne);
					if(ne.isTail()) containsLastEvent = true;
				}
				
				midiTuneModel.getKREventGroup().notifyChanged(containsLastEvent);

				krEventGuiModel.clearSelectedEvents();
			}
		}

		public void keyReleased(KeyEvent e) {
		}

	}

	private class MoveEventUIMode extends MouseInputUIMode implements UIMode {
		private int currX;
		
		public MoveEventUIMode(int startX) {
			this.currX = startX;
		}
		
		public void mouseDragged(MouseEvent e)
		{
			KREvent ke = krEventGuiModel.getActiveEvent();
			
			KREventTransaction changeMicros = new KREventTransaction(midiTuneModel);
			
			long microsDelta = eventGraph.pixelsToRelMicros(e.getX()- currX);
			
			if(microsDelta == 0) return;
				
			for(KREvent se : krEventGuiModel.getSelectedEvents())
			{
				ChangeKREventOperation op;
				changeMicros.add(op = new ChangeKREventOperation(se));
				op.setMicrosDelta(microsDelta);
			}
			
			if(changeMicros.doIt())
				currX = e.getX();

			//TODO: DO LATER (for undo)
			//noteEventGuiModel.getUndoLog().getCurrTransaction().merge(temp);			
		}

		public void mouseReleased(MouseEvent e) {
			//this mode finishes when the mouse is released
			uiModeHandler.removeMode(this);
		}

	}


}
