package kr.miditunemodel;

import java.util.ArrayList;
import java.util.Collection;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import java.util.SortedSet;
import java.util.TreeSet;

import kr.gui.GuiModel.MessageType;
import kr.undo.UndoLog;
import kr.util.ListenerManager;
import kr.util.audio.AudioClip;
import kr.util.audio.MidiAudioClip;

/**
 * Handles gui aspects of manipulating a midi track. Includes keeping track of 
 * selected events, the "active" event, etc
 */
public class EventGuiModel<T extends Event> {
	private Set<T> selectedEvents = new HashSet<T>();
	private T activeEvent = null;
	private UndoLog undoLog = new UndoLog();
	private T edittedEvent;
	private EventGroup<T> eventGroup;
	
	private SortedSet<T> copiedEvents = new TreeSet<T>();
	private long pastePointMicros;
	private MidiTuneModel midiTuneModel;
	
	public enum MessageType { SELECTION_CHANGED, EVENT_EDITTED_CHANGED };
	

	public EventGuiModel(EventGroup<T> eventGroup)
	{	
		this.eventGroup = eventGroup;
	}

	public void setActiveEvent(T e) {
		this.activeEvent = e;
	}


	public T getActiveEvent() {
		return activeEvent;
	}

	public void addSelectedEvents(List<T> events)
	{
		for(T e : events)
			selectedEvents.add(e);
		
		ListenerManager.inst().notify(this, MessageType.SELECTION_CHANGED);
	}
	
	public void addSelectedEvent(T event) {
		selectedEvents.add(event);
		ListenerManager.inst().notify(this, MessageType.SELECTION_CHANGED);
	}

	public boolean isSelectedEvent(T event)
	{
		return selectedEvents.contains(event);
	}


	public void clearSelectedEvents() {
		selectedEvents.clear();
		activeEvent = null;
		ListenerManager.inst().notify(this, MessageType.SELECTION_CHANGED);
	}


	public int getSelectedEventsSize() {
		return selectedEvents.size();
	}


	public Collection<T> getSelectedEvents() {
		return selectedEvents;
	}

	public UndoLog getUndoLog() {
		return undoLog;
	}

	public void editEvent(T event) 
	{
		if(edittedEvent == event) return;
		
		T lastEdittedEventVal = this.edittedEvent;
		this.edittedEvent = event;
		ListenerManager.inst().notify(this, MessageType.EVENT_EDITTED_CHANGED, lastEdittedEventVal);
	} 
	
	public boolean editNextEvent(boolean backwards) {
		if(this.edittedEvent == null)
			return false;
		
		T nextEvent = (T) (backwards ? edittedEvent.getPrevious() : edittedEvent.getNext());
		
		if(nextEvent == null)
			return false;
		
		editEvent(nextEvent);
		
		return true;
	}


	public T getEdittedEvent() {
		return edittedEvent;
	}

	public void deleteSelectedEvents() {
		for(T ne : getSelectedEvents())
		{
			eventGroup.removeEvent(ne);
		}

		clearSelectedEvents();
	}

	public void copySelectedEvents() {
		copiedEvents.clear();
		copyEvents(getSelectedEvents(), copiedEvents);
	}
	
	private void copyEvents(Collection<T> from, Collection<T> to)
	{
		for(T ne : from)
		{
			to.add((T) ne.clone());
		}
	}

	public void pasteSelectedEvents() {
		long startMicros = -1;
		
		MidiTransaction<T> midiTransaction = eventGroup.createTransaction();
		
		for(T e : copiedEvents)
		{
			if(startMicros == -1)
				startMicros = e.getMicros();
			
			T e2 = (T) e.clone();
			
			e2.moveTo(pastePointMicros + e.getMicros() - startMicros);
			
			CreateEventOperation<T> ceo = new CreateEventOperation<T>(eventGroup);
			ceo.setNewEvent(e2);
			
			midiTransaction.add(ceo);
		}
		
		midiTransaction.doIt();
		
	}

	public void cutSelectedEvents() {
		copySelectedEvents();
		deleteSelectedEvents();
	}

	public void setPastePointMicros(long micros) {
		this.pastePointMicros = micros;
	}

	public long getPastePointMicros() {
		return pastePointMicros;
	}


}
