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

namespace DMX2
{
	public class SequenceurMaitre
	{
		public class Ligne {
			public Ligne(){}
			string nom;
			TimeSpan duree = TimeSpan.Zero;
			Dictionary<Sequenceur,string> data = new Dictionary<Sequenceur, string>();

			public string Nom {
				get {
					return nom;
				}
				set {
					nom = value;
				}
			}

			public TimeSpan Duree {
				get {
					return duree;
				}
				set {
					duree = value;
				}
			}

			public string this [Sequenceur index] {
				get {
					string value;
					if(!data.TryGetValue(index, out value)) return string.Empty;
					return value;
				}
				set {
					if(value.Length==0) {
						if(data.ContainsKey(index)) 
							data.Remove(index);
					}
					else
						data[index] = value;
				}
			}
			public void Save (XmlElement parent)
			{
				XmlElement el = parent.OwnerDocument.CreateElement ("Ligne");
				parent.AppendChild (el);

				el.SetAttribute ("nom", nom);
				el.SetAttribute ("duree", duree.ToString ());

				XmlElement xmlSeq;
				foreach (var val in data) {
					el.AppendChild(xmlSeq=parent.OwnerDocument.CreateElement ("data"));
					xmlSeq.SetAttribute("seq",val.Key.ID.ToString());
					xmlSeq.SetAttribute("val",val.Value);
				}
			}			

			public static Ligne Load (Conduite c, XmlElement el)
			{
				Ligne l = new Ligne();
				l.nom = el.GetAttribute ("nom");
				l.duree = TimeSpan.Parse(el.GetAttribute("duree"));

				foreach (var xv in el.GetElementsByTagName("data")) {
					XmlElement xmlSeq = xv as XmlElement;
					Sequenceur seq = c.GetSeqByID(int.Parse(xmlSeq.GetAttribute("seq")));
					l[seq]= xmlSeq.GetAttribute ("val");
				}
				return l;
			}
		}

		List<Ligne> lignes = new List<Ligne>();

		Ligne enCours = null;

		TimeSpan timeStamp = TimeSpan.Zero;
		actionEventTarget goNextEventTarget=null;
		actionEventTarget goBackEventTarget=null;

		bool change = false;

		public TimeSpan TimeStamp {
			get {
				return timeStamp;
			}
		}

		public int IndexLigneEnCours
		{
			get {
				if (enCours == null) return -1;
				return lignes.IndexOf(enCours);
			}
		}

		Ligne aSuivre = null;

		public int IndexLigneaSuivre 
		{
			get {
				if (aSuivre == null)
					return -1;
				return lignes.IndexOf (aSuivre);
			}
			set {
				aSuivre = lignes[value];
			}
		}

		public void EffetSuivant ()
		{
			lock (this) {
				if(lignes.Count==0) return;
				if (aSuivre == null) {
					if (IndexLigneEnCours + 1 < lignes.Count)
						enCours = lignes [IndexLigneEnCours + 1];
					else
						enCours = lignes [0];
				} else {
					enCours = aSuivre;
				}
				aSuivre = null;
				timeStamp = TimeSpan.Zero;
				change = true;
				LanceSequenceurs();
			}
		}

		public void EffetPrecedent ()
		{
			lock (this) {
				if(lignes.Count==0) return;
				if (IndexLigneEnCours > 0)
					enCours = lignes [IndexLigneEnCours - 1];
				else
					enCours = lignes [0];
				aSuivre = null;
				timeStamp = TimeSpan.Zero;
				change = true;
				LanceSequenceurs();
			}
		}

		void LanceSequenceurs ()
		{
			foreach (Sequenceur s in Conduite.Courante.Sequenceurs) {
				if(enCours[s].Length > 0)
					s.Command(enCours[s]);
			}
		}

		public string[] GetCommands(Sequenceur s)
		{
			string[] res = new string[lignes.Count];
			for (int i = 0; i < lignes.Count; i++) {
				res[i] = lignes[i][s];
			}
			return res;
		}

		public void SetCommands (Sequenceur s, string[] commands)
		{
			lock (this) {
				for (int i = 0; i < Math.Max(lignes.Count,commands.Length); i++) {
					lignes [i] [s] = commands [i];
				}
				change= true;

			}
		}


		public ReadOnlyCollection<Ligne> Lignes {
			get {
				return lignes.AsReadOnly();
			}
		}

		public SequenceurMaitre ()
		{
			//lignes.Add(new Ligne());
			goNextEventTarget = new actionEventTarget(
				delegate(EventData data){
				if(data.value!=0) EffetSuivant();
				return true;
			});

			goBackEventTarget = new actionEventTarget(
				delegate(EventData data){
				if(data.value!=0) EffetPrecedent();
				return true;
			});

		}

		public int AjoutLigne (int pos)
		{
			lock (this) {
				lignes.Insert (pos, new Ligne ());
				return pos;
			}
		}

		public void RetireLigne (int pos)
		{
			lock (this) {
				if(pos<0 || pos >= lignes.Count) return;
				if(lignes[pos]== enCours)
				{
					enCours=null;
					if(pos+1 < lignes.Count)
						aSuivre = lignes[pos+1];
				} 
				if(lignes[pos] == aSuivre)
					aSuivre=null;
				lignes.RemoveAt(pos);
			}
		}
		public void Tick (TimeSpan deltaT)
		{
			timeStamp += deltaT;
			if(enCours != null)
			if (enCours.Duree != TimeSpan.Zero && enCours.Duree <= timeStamp ) {
				EffetSuivant();
			}
		}

		public bool EffetChange ()
		{
			if (change) {
				change = false;
				return true;
			}
			return false;
		}

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

		public void BindEffetPrecedent (string eventId)
		{
			if (eventId.Length == 0) {
				Conduite.Courante.EventManager.Unbind (goBackEventTarget);
				return;
			}
			Conduite.Courante.EventManager.Bind(eventId,goBackEventTarget);
		}
		public void Save (XmlElement parent)
		{
			XmlElement el = parent.OwnerDocument.CreateElement("SequenceurMaitre");
			parent.AppendChild(el);

			foreach(Ligne l in lignes)
				l.Save(el);

			XmlElement xmlEl = parent.OwnerDocument.CreateElement ("EffetSuivant");
			if(EventManager.SaveBindings(xmlEl,goNextEventTarget ))	el.AppendChild(xmlEl);

			xmlEl = parent.OwnerDocument.CreateElement ("EffetPrecedent");
			if(EventManager.SaveBindings(xmlEl,goBackEventTarget ))	el.AppendChild(xmlEl);

		}


		public static SequenceurMaitre Load (Conduite c, XmlElement el)
		{
			SequenceurMaitre seq = new SequenceurMaitre ();

			foreach (var xl in el.GetElementsByTagName("Ligne")) {
				seq.lignes.Add(Ligne.Load(c,xl as XmlElement));
			}


			XmlElement xmlE;
			if ((xmlE = el["EffetSuivant"])!= null)
				foreach(string id in EventManager.LoadBindings(xmlE))
					seq.BindEffetSuivant(id);

			if ((xmlE = el["EffetPrecedent"])!= null)
				foreach(string id in EventManager.LoadBindings(xmlE))
					seq.BindEffetPrecedent(id);


			return seq;
		}
	}
}
