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.AbstractAction;
import javax.swing.JComponent;
import javax.swing.KeyStroke;
import javax.swing.event.MouseInputAdapter;

import kr.gui.GuiModel;
import kr.gui.MidiTextGraph;
import kr.gui.TimeSyncedController;
import kr.gui.metrodome.MetrodomeGraphController;
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;

	private GuiModel guiModel;

	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.guiModel = guiModel;
		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)
			{
				NoteEvent currEdittedNoteEvent = (NoteEvent)values[0];
				
				if(noteEventGuiModel.getEdittedEvent() != null)
				{
					NoteEvent edittedEvent = noteEventGuiModel.getEdittedEvent();
					
					int pixels = midiTextGraph.microsToPixels(edittedEvent.getMicros());
					int endPixels = midiTextGraph.microsToPixels(edittedEvent.getEndMicros());
					
					if(pixels < 0)
					{
						guiModel.updateNowMicros(midiTextGraph.pixelsToMicros(pixels));
					}
					else if(endPixels > midiTextGraph.getWidth())
					{
						//reposition so it's near the center of the screen
						guiModel.updateNowMicros(midiTextGraph.pixelsToMicros(pixels - midiTextGraph.getWidth()/2));
					}
					
					//if we were editting an event
					if(currEdittedNoteEvent != null)
						noteEventGuiModel.updateText(currEdittedNoteEvent, midiTextGraph.getEditTextField().getText());
					else
					{
						//start event editting mode
						uiModeHandler.addMode(new TextEntryUIMode());
					}

					midiTextGraph.editEvent(noteEventGuiModel.getEdittedEvent());
				}	
				else
				{
					if(currEdittedNoteEvent != null)
						noteEventGuiModel.updateText(currEdittedNoteEvent, 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);
			editComp.setFocusTraversalKeysEnabled(false);
			c.addMouseListener(this);

//			editComp.getInputMap(JComponent.WHEN_IN_FOCUSED_WINDOW).put(KeyStroke.getKeyStroke(
//	                KeyEvent.VK_TAB, 0),"tab");
//			editComp.getInputMap(JComponent.WHEN_IN_FOCUSED_WINDOW).put(KeyStroke.getKeyStroke(
//	                KeyEvent.VK_TAB, KeyEvent.SHIFT_DOWN_MASK),"shifttab");
//			
//			for(Object k : editComp.getActionMap().allKeys())
//			{
//				System.out.println(k+": "+editComp.getActionMap().get(k));
//			}
//			editComp.getActionMap().put("tab", new AbstractAction()
//					{
//				public void actionPerformed(java.awt.event.ActionEvent e) 
//				{
//					noteEventGuiModel.editNextEvent();
//				}
//					});
//			
//			editComp.getActionMap().put("shifttab", new AbstractAction()
//					{
//				public void actionPerformed(java.awt.event.ActionEvent e) 
//				{
//					noteEventGuiModel.editNextEvent();
//				}
//					});
//			
		
		}

		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);// save the current event text
			}
			if(e.getKeyCode() == KeyEvent.VK_TAB)
			{
				//go to the next event
				if(e.isShiftDown())
					noteEventGuiModel.editNextEvent(true);
				else
					noteEventGuiModel.editNextEvent(false);
			}
			
		}
		
		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);
		}
	}
}
