using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Collections.ObjectModel;
using System.Threading;
using System.Xml;


namespace DMX2
{
	public class Conduite : IComparer<Circuit>, IDisposable
	{

		static Conduite courante = null;

		public static Conduite Courante { get { return courante; } }

		Timer timer = null;
		DateTime dernierTick;
		DateTime derniereMaj;
		string _name;
		int master=100;

		List<Circuit> circuits = new List<Circuit>();
		List<UniversDMX> univers = new List<UniversDMX>();

		List<DriverDMX> drivers = new List<DriverDMX>();

		SequenceurMaitre seqmaitre = new SequenceurMaitre();

		MidiEventProvider midip=null;

		actionEventTarget masterEventTarget;

		public Conduite()
		{

			if(courante!= null)
				courante.Dispose();
			courante = this;

			timer = new Timer(new TimerCallback(TimerTick),this, 1000,10);
			derniereMaj = dernierTick=DateTime.Now;

			// Crée l'univers par défaut
			var u = new UniversDMX();
			Patches.Add(u);
			u.Nom = "Univers par Défaut";

			Pause = false;


			midip = new MidiEventProvider(eventManager);

			masterEventTarget = new actionEventTarget(
				delegate(EventData data){
				Master = 100 * data.value /255;
				return true;
			});
		}

		EventManager eventManager = new EventManager();

		public EventManager EventManager {
			get {
				return eventManager;
			}
			set {
				eventManager = value;
			}
		}


		public ReadOnlyCollection<Circuit> Circuits {
			get {
				return circuits.AsReadOnly();
			}
		}

		public Circuit NouveauCircuit ()
		{
			lock (this) {
				Circuit c = new	Circuit ();
				circuits.Add (c);
				return c;
			}
		}

/*		public void SupprimeCircuits (IEnumerable<Circuit> lc)
		{
			lock (this) {
				foreach(var c in lc)
					circuits.Remove (c);
				foreach(var seq in Sequenceurs)
					seq.MajCircuitsSupprimes();
			}
		}*/

		public Circuit GetCircuitByID (int i)
		{
			foreach(Circuit c in circuits)
				if(c.ID == i) return c;
			return null;
		}

		public string Name {
			get {
				return _name;
			}
			set {
				_name = value;
			}
		}

		public int Master {
			get {
				return master;
			}
			set {
				master= value;
			}
		}

		public bool Pause { get; set; }

		public bool BlackOut { get; set ; }

		public SequenceurMaitre SequenceurMaitre {
			get {
				return seqmaitre;
			}
		}

		public List<DriverDMX> Drivers {
			get {
				return drivers;
			}
		}

		int IComparer<Circuit>.Compare (Circuit x, Circuit y)
		{
				return Conduite.Courante.circuits.IndexOf(x) -
					Conduite.Courante.circuits.IndexOf(y);
		}

		List<Sequenceur> sequenceurs= new List<Sequenceur>();

		public ReadOnlyCollection<Sequenceur> Sequenceurs {
			get {
				return sequenceurs.AsReadOnly();
			}
		}

		public void AjoutSequenceur (Sequenceur seq)
		{
			lock (this) {
				sequenceurs.Add(seq);
				seq.Renamed += SequenceurRenomme;
			}
		}

		void SequenceurRenomme (object sender, Sequenceur.SeqRenamedEventArgs e)
		{
			MainWindow.Win.NextUpdateFull();
		}

		public Sequenceur GetSeqByID (int i)
		{
			foreach(Sequenceur seq in sequenceurs)
				if(seq.ID == i)
					return seq;
			return null;

		}

		public List<UniversDMX> Patches {
			get {
				return univers;
			}
		}

		static public void TimerTick (object state)
		{
			Conduite.Courante.Tick();
		}

		void Tick ()
		{
			DateTime tickTime = DateTime.Now;
			TimeSpan deltaT = tickTime - dernierTick;
			dernierTick = tickTime;

			if (!Pause) {

				/*	if (deltaT > TimeSpan.FromMilliseconds (15))
					Info.Publish(string.Format ("{0}", deltaT));*/

				lock (this) {

					seqmaitre.Tick (deltaT);

					// 'Actionne' les sequenceurs
					foreach (var seq in sequenceurs) {
						seq.Tick (deltaT);				
					}

					// Mets a jour les valeurs circuits.
					if(BlackOut)
					{
						foreach (var c in circuits) 
							c.ValeurCourante = 0;
					}
					else
					{
					foreach (var c in circuits) {
						int val = 0;
						foreach (var seq in Sequenceurs) {
							val = Math.Max (val, seq.ValeurCircuit (c));
						}
						c.ValeurCourante = val * master / 100;
					}

					}

					EventManager.ProcessEvents();
				}
				// Cette fonction retourne quasi immédiatement, même si il y'a beaucoup a faire sur l'affichage
			}


			if(tickTime - derniereMaj > TimeSpan.FromMilliseconds(50)){
				MainWindow.Win.ScheduleUpdate();
				derniereMaj = DateTime.Now;
			}

		}

		public void BindMaster (string eventId)
		{
			if (eventId.Length == 0) {
				Conduite.Courante.EventManager.Unbind (masterEventTarget);
				return;
			}
			Conduite.Courante.EventManager.Bind(eventId,masterEventTarget);
		}


		#region IDisposable implementation
		bool disposed=false;

		public void Dispose()
		{
			if(courante==this) 
				courante = null;
			disposed=true;
			if(timer!=null)
				timer.Dispose();
			if(midip!=null)
				midip.Dispose();
			foreach(var driver in Drivers)
				driver.Dispose();
			timer=null;
		}

		void IDisposable.Dispose ()
		{
			if(!disposed)
				Dispose();
		}
		#endregion

		#region Sauvegarde

		public XmlDocument Save ()
		{
			XmlDocument xmlDoc = new XmlDocument ();
			XmlElement xmlRoot = xmlDoc.CreateElement ("Conduite");

			xmlDoc.AppendChild (xmlRoot);

			xmlRoot.SetAttribute ("nom", this.Name);

			XmlElement xmlCircuits = xmlDoc.CreateElement ("Circuits");
			xmlRoot.AppendChild (xmlCircuits);

			foreach (Circuit c in circuits) {
				c.Save(xmlCircuits);
			}

			XmlElement xmlSequenceurs = xmlDoc.CreateElement("Sequenceurs");
			xmlRoot.AppendChild(xmlSequenceurs);

			foreach(Sequenceur seq in sequenceurs)
			{
				seq.Save(xmlSequenceurs);
			}

			XmlElement xmlUniversList = xmlDoc.CreateElement("ListeUnivers");
			xmlRoot.AppendChild(xmlUniversList);

			foreach(UniversDMX univ in univers)
			{
				univ.Save(xmlUniversList);
			}

			XmlElement xmlMaster = xmlDoc.CreateElement("Master");
			xmlMaster.SetAttribute("value",master.ToString());
			EventManager.SaveBindings(xmlMaster,masterEventTarget);
			xmlRoot.AppendChild(xmlMaster);

			seqmaitre.Save(xmlRoot);

			return xmlDoc;
		}

		public static Conduite Load (XmlDocument doc)
		{
			Conduite cond = new Conduite ();
			cond.LoadDoc (doc);
			return cond;
//			cond.Dispose();
//			return null;
		}

		private void LoadDoc (XmlDocument doc)
		{
			XmlElement root = doc.DocumentElement;

			_name = root.Attributes ["nom"].Value;

			foreach (var xc in root["Circuits"].ChildNodes) {
				Circuit c = Circuit.Load (xc as XmlElement);
				if (c != null)
					circuits.Add (c);
			}

			foreach (var xs in root["Sequenceurs"].ChildNodes) {
				Sequenceur s = Sequenceur.Load (this, xs as XmlElement);
				if (s != null){
					sequenceurs.Add (s);
					s.Renamed += SequenceurRenomme;
				}
			}

			univers.Clear();

			foreach (var xu in root["ListeUnivers"].ChildNodes) {
				UniversDMX u = UniversDMX.Load(this,xu as XmlElement);
				if(u!=null)
					univers.Add(u);
			}

			XmlElement xmlMaster;
			if((xmlMaster = root["Master"])!=null)
			{
				master = int.Parse( xmlMaster.GetAttribute("value"));
				foreach(string id in EventManager.LoadBindings(xmlMaster))
					BindMaster(id);
					
			}

			seqmaitre = SequenceurMaitre.Load(this,root["SequenceurMaitre"]);

		}

		#endregion

	}

	public class Circuit
	{
		static int maxid=1;
		string name;

		public const int SNLen= 8;

		public Circuit()
		{
			id=maxid++;
			Name = "Circuit n°" + id.ToString();
			ShortName = "Cir" + id.ToString();
		}

		public string Name {
			get {
				return name;
			}
			set {
				name = value;
			}
		}

		string shortName;

		public string ShortName {
			get {
				return shortName;
			}
			set {
				if (value.Length > SNLen)
					shortName = value.Substring(0,SNLen);
				else
					shortName = value;
			}
		}

		int id;

		public int ID {
			get {
				return id;
			}
		}

		int _curval;

		public int ValeurCourante {
			get {
				return _curval;
			}
			set {
				_curval = value;
			}
		}		

		public void Save (XmlElement parent)
		{
			XmlElement el= parent.OwnerDocument.CreateElement ("Circuit");
			parent.AppendChild(el);
			el.SetAttribute("ID",id.ToString());
			el.SetAttribute("name",name);
			el.SetAttribute("shortName",shortName);
		}	

		public static Circuit Load (XmlElement xc)
		{
			if(xc.Name!="Circuit") throw new ErreurLectureFichier("<Circuit> attendu.");
			return new Circuit(xc);
		}

		private Circuit(XmlElement xc)
		{
			id = int.Parse(xc.GetAttribute("ID"));
			maxid = Math.Max (maxid,id+1);
			name = xc.GetAttribute("name");
			shortName = xc.GetAttribute("shortName"); 
		}
	}


}

