package kr.gui.midigraph;

import java.awt.Component;
import java.awt.event.FocusEvent;
import java.awt.event.FocusListener;
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.event.MouseInputAdapter;

import kr.gui.GuiModel;
import kr.gui.MidiTextGraph;
import kr.gui.TimeSyncedController;
import kr.gui.util.UIMode;
import kr.gui.util.UIModeHandler;
import kr.miditunemodel.EventGroup;
import kr.miditunemodel.EventGuiModel;
import kr.miditunemodel.MidiTuneModel;
import kr.miditunemodel.NoteEvent;
import kr.miditunemodel.NoteEventGroup;
import kr.miditunemodel.NoteEventGuiModel;
import kr.util.Listener;
import kr.util.ListenerManager;

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

	private NoteEventGroup noteEventGroup;

	private MidiTextGraph midiTextGraph;
	
	private UIModeHandler uiModeHandler;

	private NoteEventGuiModel noteEventGuiModel;

	public MidiTextGraphController() {
	}

	/**
	 * Removes the last mode from the stack and removes it as a listener.
	 */
	public void init(GuiModel guiModel, MidiTuneModel midiModel,
			MidiTextGraph midiTextGraph, NoteEventGuiModel noteEventGuiModel) {
		super.init(guiModel, midiTextGraph);
		this.midiTuneModel = midiModel;
		this.midiTextGraph = midiTextGraph;
		this.noteEventGuiModel = noteEventGuiModel;
		this.noteEventGroup = midiModel.getNoteEventGroup();
		ListenerManager.inst().registerListener(noteEventGroup, this);
		ListenerManager.inst().registerListener(noteEventGuiModel, this);
		
		uiModeHandler = new UIModeHandler(midiTextGraph);
		uiModeHandler.addMode(new NormalUIMode());
	}

	public void notify(Object source, Object type, Object... values) {
		if (source == noteEventGroup) {
			if (type == EventGroup.MessageType.EVENTS_CHANGED) {
				// PERF Don't repaint the whole screen, just redraw the note
				midiTextGraph.repaintScrollingData();
			}
		}
		else if(source == noteEventGuiModel)
		{
			if(type ==  EventGuiModel.MessageType.SELECTION_CHANGED)
			{
				//PERF Don't repaint the whole screen, just redraw the affected notes
				// also need to make note event gui model return the affected notes
				midiTextGraph.repaintScrollingData();
			}
			else if(type ==  NoteEventGuiModel.MessageType.TEXT_UPDATED)
			{
				//PERF Don't repaint the whole screen, just redraw the affected notes
				// also need to make note event gui model return the affected notes
				midiTextGraph.repaintScrollingData();
			}
			else if(type ==  EventGuiModel.MessageType.EVENT_EDITTED_CHANGED)
			{
				if(noteEventGuiModel.getEdittedEvent() != null)
				{
					midiTextGraph.editEvent(noteEventGuiModel.getEdittedEvent());
					uiModeHandler.addMode(new TextEntryUIMode());
				}	
				else
				{
					NoteEvent e = (NoteEvent)values[0];
					
					noteEventGuiModel.updateText(e, midiTextGraph.getEditTextField().getText());
					midiTextGraph.stopEdittingEvent();
					
					uiModeHandler.removeMode();
				}
			}
		}
	}

	/**
	 * Normal mode when no buttons are held down
	 */
	private class NormalUIMode extends MouseInputAdapter implements UIMode
	{
		public void start(Component c)
		{
			c.addMouseListener(this);
		}

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

	    public void mouseClicked(MouseEvent e) { 
			long micros = midiTextGraph.pixelsToMicros(e.getX());
			
			//PERF: specify a starting point
			NoteEvent closestNote = midiTuneModel.getNoteEventGroup().getClosestEvent(null, micros);
			
			if(closestNote != null && closestNote.getMicros() <= micros && closestNote.getEndMicros() > micros)
			{
				System.out.println("select note");
				noteEventGuiModel.clearSelectedEvents();
				noteEventGuiModel.setActiveEvent(closestNote);
				noteEventGuiModel.addSelectedEvent(closestNote);
				noteEventGuiModel.editEvent(closestNote);
			}
		}

	}
	
	private class TextEntryUIMode extends MouseInputAdapter implements UIMode, KeyListener, FocusListener, MouseListener
	{
		
		public void start(Component c)
		{
			JComponent editComp = midiTextGraph.getEditTextField();
			editComp.addKeyListener(this);
			editComp.addFocusListener(this);
			c.addMouseListener(this);
		}

		public void stop(Component c)
		{
			JComponent editComp = midiTextGraph.getEditTextField();
			midiTextGraph.getEditTextField().removeKeyListener(this);
			editComp.removeFocusListener(this);
			c.removeMouseListener(this);
		}

		public void keyTyped(KeyEvent e) {
		}

		public void keyPressed(KeyEvent e) {
		}

		public void keyReleased(KeyEvent e) {
			if(e.getKeyCode() == KeyEvent.VK_ENTER)
			{
				noteEventGuiModel.editEvent(null);
			}
			
		}
		
		public void mouseClicked(MouseEvent e) { 
			//called when clicked inside the main window, not the edit box
			noteEventGuiModel.editEvent(null);
		}

		public void focusGained(FocusEvent e) {
		}

		public void focusLost(FocusEvent e) {
			noteEventGuiModel.editEvent(null);
		}
	}
}
