using System;
using System.Runtime.InteropServices;
using System.Collections.Generic;
using System.Linq;

namespace DMX2
{
	public partial class MidiEventProvider : IEventProvider, IDisposable 
	{

		class internalEvent {
			public string internalName;
			public string description;
			public internalEvent(string _id, string _desc)
			{
				internalName=_id;
				description=_desc;
			}
		}

		Dictionary<string,internalEvent> eventlist = new Dictionary<string, internalEvent>();
		IntPtr midi_seq_handle = IntPtr.Zero;


		public MidiEventProvider (EventManager manager)
		{
			snd_seq_open(out midi_seq_handle, "default",SND_SEQ_OPEN_DUPLEX,0);
			snd_seq_set_client_name(midi_seq_handle,"DMX2");
			snd_seq_create_simple_port(midi_seq_handle,"dmx_ctrl",
			                                      SND_SEQ_PORT_CAP_WRITE + SND_SEQ_PORT_CAP_SUBS_WRITE, 
			                                      SND_SEQ_PORT_TYPE_APPLICATION);

			manager.RegisterProvider(this);
		}


		#region IDisposable implementation
		public void Dispose ()
		{
			snd_seq_close(midi_seq_handle);
		}
		#endregion


		#region IEventProvider implementation
		/*ICollection<string> IEventProvider.GetEventList ()
		{
			return eventlist.Keys;
		}*/

		bool IEventProvider.Bind (string eventId)
		{
			return eventId.StartsWith("MIDI-");
		}

		void IEventProvider.Unbind (string eventId)
		{

			return;
		}

		Gtk.Menu IEventProvider.GetProviderSubMenu (EventManager.EventMenuData state, Gtk.ButtonPressEventHandler handler)
		{
			if(!connected) return null;
			Gtk.Menu retmenu = new Gtk.Menu ();
			if (levent != null) {
				Gtk.MenuItem lmenuitem = new Gtk.MenuItem ("Dernier");
				retmenu.Add (lmenuitem);
				Gtk.Menu lmenu = new Gtk.Menu ();
				lmenuitem.Submenu = lmenu;
				Gtk.MenuItem item = new Gtk.MenuItem(levent.description);
				item.Data[EventManager.EventIdKey] = levent.internalName;
				item.Data[EventManager.StateKey] = state;
				item.ButtonPressEvent += handler;
				lmenu.Add (item);
			}
			Gtk.MenuItem evmenuitem = new Gtk.MenuItem ("Events");
			retmenu.Add (evmenuitem);
			Gtk.Menu evmenu = new Gtk.Menu ();
			evmenuitem.Submenu = evmenu;

			List<string> sortedKeys = eventlist.Keys.ToList();
			sortedKeys.Sort();

			foreach ( string key in sortedKeys ) {
				internalEvent evt= eventlist[key];
				Gtk.MenuItem item = new Gtk.MenuItem(evt.description);
				item.Data[EventManager.EventIdKey] = evt.internalName;
				item.Data[EventManager.StateKey] = state;
				item.ButtonPressEvent += handler;
				evmenu.Add (item);
			}

			return retmenu;

		}

		EventData last; internalEvent levent=null;
		bool connected=false;

		void IEventProvider.ProcessEvents (EventManagerCallback callback)
		{
			IntPtr evPtr;
			while ((snd_seq_event_input_pending(midi_seq_handle,1))>0) {
				snd_seq_event_input(midi_seq_handle, out evPtr);
				snd_seq_event_t evS =(snd_seq_event_t) Marshal.PtrToStructure(evPtr,typeof(snd_seq_event_t));
				snd_seq_free_event(evPtr);

				string id=null, description=null; int value=0;

				switch (evS.type) {
				case snd_seq_event_type_t.SND_SEQ_EVENT_PORT_SUBSCRIBED:
					connected = true;
					continue;
				case snd_seq_event_type_t.SND_SEQ_EVENT_CONTROLLER:
					id= string.Format("MIDI-CTRL-C{0}P{1}",evS.data_ev_ctrl.channel,evS.data_ev_ctrl.param);
					description = string.Format("Controller Ch {0} Param {1}",evS.data_ev_ctrl.channel,evS.data_ev_ctrl.param);
					value = 255 * evS.data_ev_ctrl.value / 127;
					break;
				case snd_seq_event_type_t.SND_SEQ_EVENT_NOTEON:
					id= string.Format("MIDI-NOTE-C{0}N{1}",evS.data_ev_note.channel, evS.data_ev_note.note );
					description = string.Format("Note {1} Ch {0}",evS.data_ev_note.channel, evS.data_ev_note.note );
					value = 255 ;
					break;
				case snd_seq_event_type_t.SND_SEQ_EVENT_NOTEOFF:
					id= string.Format("MIDI-NOTE-C{0}N{1}",evS.data_ev_note.channel, evS.data_ev_note.note );
					description = string.Format("Note {1} Ch {0}",evS.data_ev_note.channel, evS.data_ev_note.note );
					value = 0; 
					break;
				case snd_seq_event_type_t.SND_SEQ_EVENT_CLOCK:
				case snd_seq_event_type_t.SND_SEQ_EVENT_SENSING:
					continue;

				//TODO : Regarder si d'autres controles interessants.
				default:
						id= null;
						Info.Publish(string.Format ("event {0}", evS.type) );
					continue;
				}
				connected=true;
				if(id!=null)
				{
					if(!eventlist.ContainsKey(id))
						eventlist.Add(id,new internalEvent(id,description));
					levent= eventlist[id];
					EventData evData = new EventData();
					evData.id = id;
					evData.value = (byte)value;
					if(evData.Equals(last)) continue;
					callback(evData);
					last = evData;
				}


			}
		}

		string IEventProvider.MenuName {
			get {
				return "Midi";
			}
		}
		#endregion




	}
}

