using System;
using Gtk;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;


namespace DMX2
{
	public partial class MainWindow: Gtk.Window
	{	
		static MainWindow win;
		static object circuitKey = new object();
		ListStore lsMatrice = null;
		FileInfo conduiteFile=null;
 
		public static MainWindow Win {
			get { return win; }
		}

		public MainWindow (): base (Gtk.WindowType.Toplevel)
		{
			win=this;
			Build ();
			MajWidgets();

			ContextMenuHelper ctxHelper = new ContextMenuHelper();
			ctxHelper.ContextMenu += EventPopup;
			ctxHelper.AttachToWidget(btnGo);
			ctxHelper.AttachToWidget(btnGoBack);
			ctxHelper.AttachToWidget(masterScale);

		}

		void EventPopup (object sender, ContextMenuEventArgs e)
		{
			Menu m = Conduite.Courante.EventManager.GetMenu(e.Widget.Name,
			    delegate(object o,string eventId){
				switch(o as string){
				case "btnGo":
					Conduite.Courante.SequenceurMaitre.BindEffetSuivant(eventId);
					break;
				case "btnGoBack":
					Conduite.Courante.SequenceurMaitre.BindEffetPrecedent(eventId);
					break;
				case "masterScale":
					Conduite.Courante.BindMaster(eventId);
					break;
				}
			}
			);
			m.ShowAll();
			m.Popup();
		}




#region Sequenceur Maitre

		protected void ConstruitMatrice ()
		{

			this.MatriceUI.EnableGridLines = TreeViewGridLines.Both;

			var idCol = new Gtk.TreeViewColumn ();
			var idCell = new Gtk.CellRendererText ();
			idCol.Title = "Num";
			idCol.PackStart (idCell, true);
			idCol.SetCellDataFunc (idCell, new Gtk.TreeCellDataFunc (RenderMatriceNum));
			this.MatriceUI.AppendColumn (idCol);

			var nomCol = new Gtk.TreeViewColumn ();
			var nomCell = new Gtk.CellRendererText ();
			nomCol.Title = "Nom";
			nomCol.PackStart (nomCell, true);
			nomCol.SetCellDataFunc (nomCell, new Gtk.TreeCellDataFunc (RenderMatriceNom));
			nomCell.Editable = true;
			nomCell.Edited += OnNomCellEdited;
			this.MatriceUI.AppendColumn (nomCol);

			var dureeCol = new Gtk.TreeViewColumn ();
			var dureeCell = new Gtk.CellRendererText ();
			dureeCol.Title = "Durée";
			dureeCol.PackStart (dureeCell, true);
			dureeCol.SetCellDataFunc (dureeCell, new Gtk.TreeCellDataFunc (RenderMatriceDuree));
			dureeCell.Editable = true;
			dureeCell.Edited += OnDureeCellEdited;
			this.MatriceUI.AppendColumn (dureeCol);

			lsMatrice = new Gtk.ListStore(typeof (SequenceurMaitre.Ligne));
			this.MatriceUI.Model = lsMatrice;
			FillMatrice();
		}

		void DetruitMatrice ()
		{
			this.MatriceUI.Model = null;
			if (lsMatrice != null) lsMatrice.Dispose();
			foreach (var col in MatriceUI.Columns)
				MatriceUI.RemoveColumn (col);

		}

		static object seqkey = new object();


		void ConstruitMatriceSeqColumns ()
		{
			foreach(var c in MatriceUI.Columns)
				if(c.CellRenderers[0].Data.ContainsKey(seqkey))
					MatriceUI.RemoveColumn(c);
 
			Gtk.TreeViewColumn seqCol;
			Gtk.CellRendererText seqCell;

			foreach (Sequenceur seq in Conduite.Courante.Sequenceurs) {
				seqCol = new TreeViewColumn();
				seqCell = new CellRendererText();
				seqCol.Title = seq.Name;
				seqCol.PackStart(seqCell, true);
				seqCol.SetCellDataFunc ( seqCell, new Gtk.TreeCellDataFunc(RenderMatriceSeqVal));
				seqCell.Editable = true;
				seqCell.Edited += OnSeqValCellEdited;
				seqCell.Data[seqkey] = seq;
				this.MatriceUI.AppendColumn(seqCol);
			}

		}


		void RenderMatriceNum (TreeViewColumn tree_column, CellRenderer cell, TreeModel tree_model, TreeIter iter)
		{
			if (Conduite.Courante==null) return;
			string num=string.Empty;
			SequenceurMaitre.Ligne l =  tree_model.GetValue (iter, 0) as SequenceurMaitre.Ligne;
			if( Conduite.Courante.SequenceurMaitre.IndexLigneEnCours == Conduite.Courante.SequenceurMaitre.Lignes.IndexOf(l) )
				num+= "->";
			if( Conduite.Courante.SequenceurMaitre.IndexLigneaSuivre == Conduite.Courante.SequenceurMaitre.Lignes.IndexOf(l) )
				num+= "* ";
			(cell as Gtk.CellRendererText).Text = num + (Conduite.Courante.SequenceurMaitre.Lignes.IndexOf(l)+1).ToString();
		}

		void RenderMatriceNom (TreeViewColumn tree_column, CellRenderer cell, TreeModel tree_model, TreeIter iter)
		{
			if (Conduite.Courante==null) return;
			SequenceurMaitre.Ligne l =  tree_model.GetValue (iter, 0) as SequenceurMaitre.Ligne;
			(cell as Gtk.CellRendererText).Text = l.Nom;
		}

		void RenderMatriceDuree (TreeViewColumn tree_column, CellRenderer cell, TreeModel tree_model, TreeIter iter)
		{
			if (Conduite.Courante==null) return;
			SequenceurMaitre.Ligne l =  tree_model.GetValue (iter, 0) as SequenceurMaitre.Ligne;
			if (l.Duree== TimeSpan.Zero) (cell as Gtk.CellRendererText).Text = string.Empty;
			else (cell as Gtk.CellRendererText).Text = (l.Duree.TotalMilliseconds /100).ToString();
		}

		void RenderMatriceSeqVal (TreeViewColumn tree_column, CellRenderer cell, TreeModel tree_model, TreeIter iter)
		{
			if (Conduite.Courante==null) return;
			SequenceurMaitre.Ligne l =  tree_model.GetValue (iter, 0) as SequenceurMaitre.Ligne;
			Sequenceur seq = cell.Data[seqkey] as Sequenceur;
			(cell as Gtk.CellRendererText).Text = l[seq];
		}
		void FillMatrice ()
		{
			lsMatrice.Clear ();
			foreach (SequenceurMaitre.Ligne l in Conduite.Courante.SequenceurMaitre.Lignes) {
				lsMatrice.AppendValues(l);
			}
		}

		void OnNomCellEdited (object o, EditedArgs args)
		{
			Gtk.TreeIter iter;
			lsMatrice.GetIter (out iter, new Gtk.TreePath (args.Path));
			SequenceurMaitre.Ligne l = lsMatrice.GetValue(iter,0) as SequenceurMaitre.Ligne;
			l.Nom = args.NewText;

		}

		void OnDureeCellEdited (object o, EditedArgs args)
		{
			Gtk.TreeIter iter;
			lsMatrice.GetIter (out iter, new Gtk.TreePath (args.Path));
			SequenceurMaitre.Ligne l = lsMatrice.GetValue (iter, 0) as SequenceurMaitre.Ligne;
			if (args.NewText.Length == 0)
				l.Duree = TimeSpan.Zero;
			else {
				int val;
				if(int.TryParse(args.NewText, out val))
					l.Duree = TimeSpan.FromMilliseconds(val *100);
			}
		}

		void OnSeqValCellEdited (object o, EditedArgs args)
		{
			Gtk.CellRendererText cell = o as CellRendererText;
			Sequenceur seq = cell.Data[seqkey] as Sequenceur;
			Gtk.TreeIter iter;
			lsMatrice.GetIter (out iter, new Gtk.TreePath (args.Path));
			SequenceurMaitre.Ligne l = lsMatrice.GetValue(iter,0) as SequenceurMaitre.Ligne;
			l[seq] = args.NewText;
		}

				int IndexEffetSelectionne()
		{
			var sel = MatriceUI.Selection.GetSelectedRows();
			if(sel.Length ==0) return -1;
			return MatriceUI.Selection.GetSelectedRows()[0].Indices[0];
		}

		void SelectionneEffet (int index)
		{
			if(index <0 ) return;
			MatriceUI.SetCursor( new TreePath( new int[1] {index })   ,null,false);
		}

		protected void OnBtnAjoutLigneClicked (object sender, EventArgs e)
		{
			int pos=IndexEffetSelectionne() + 1;
			Conduite.Courante.SequenceurMaitre.AjoutLigne(pos);
			FillMatrice();
			MatriceUI.SetCursor( new TreePath( new int[1] {pos })   , MatriceUI.Columns[1] ,true);
		}	

		protected void OnBtnRetireLigneClicked (object sender, EventArgs e)
		{
			if(IndexEffetSelectionne()==-1) return;
			Conduite.Courante.SequenceurMaitre.RetireLigne(IndexEffetSelectionne());
			FillMatrice();
		}		

		protected void OnBtnGoBackClicked (object sender, EventArgs e)
		{
			Conduite.Courante.SequenceurMaitre.EffetPrecedent();
			//FillMatrice();
		}		

		protected void OnBtnGoClicked (object sender, EventArgs e)
		{
			Conduite.Courante.SequenceurMaitre.EffetSuivant();
			//FillMatrice();
		}		

		protected void OnMatriceUICursorChanged (object sender, EventArgs e)
		{
			if (Conduite.Courante==null) return;
			TreeViewColumn col;
			TreePath path;
			MatriceUI.GetCursor (out path, out col);

			if (MatriceUI.Columns [0] == col) {
				Conduite.Courante.SequenceurMaitre.IndexLigneaSuivre = IndexEffetSelectionne();
			}

		}

#endregion

#region Application

		protected void OnDeleteEvent (object sender, DeleteEventArgs a)
		{
			Application.Quit ();
			a.RetVal = true;
		}
		protected void OnQuitActionActivated (object sender, EventArgs e)
		{
			Application.Quit ();
		}

#endregion

#region Gestion de la Conduite

		protected void OnNewActionActivated (object sender, EventArgs e)
		{
			var dlg = new Dialog ("Nom ?", this, DialogFlags.Modal); var entry = new Entry ("Conduite sans Nom");
			dlg.AddButton (Stock.Ok, ResponseType.Ok).GrabDefault(); dlg.AddButton (Stock.Cancel, ResponseType.Cancel);
			dlg.VBox.Add (new Label("Nom de la nouvelle Conduite :")); dlg.VBox.Add (entry); dlg.VBox.ShowAll (); 
			entry.ActivatesDefault=true;
			if ((ResponseType)dlg.Run () == ResponseType.Ok) {
				new Conduite ();
				Conduite.Courante.Name = entry.Text;
			}
			MajWidgets();
			dlg.Destroy();

		}

		protected void OnCloseActionActivated (object sender, EventArgs e)
		{
			Conduite.Courante.Dispose();
			MajWidgets();
		}		

		protected void OnSaveActionActivated (object sender, EventArgs e)
		{
			if(conduiteFile==null)
				SaveFileAs();
			else
				SaveFile();
		}

		protected void OnSaveAsActionActivated (object sender, EventArgs e)
		{
			SaveFileAs();
		}

		void SaveFileAs ()
		{
			FileChooserDialog fcd = new FileChooserDialog ("Sauver sous ...", this, FileChooserAction.Save,
			                                              "Annuler", ResponseType.Cancel,
			                                              "Enregistrer", ResponseType.Accept);

			fcd.Filter = new FileFilter ();

			fcd.Filter.AddPattern ("*.dmx2");

			bool ok = false;

			while (!ok) {

				if ((ResponseType)fcd.Run () == ResponseType.Cancel) {
					fcd.Destroy ();
					return;
				}
				string file = fcd.Filename;

				if (!file.EndsWith (".dmx2"))
					file += ".dmx2";

				conduiteFile = new FileInfo(file);

				if(conduiteFile.Exists)
				{
					MessageDialog msg = new MessageDialog(fcd,DialogFlags.Modal,
					                                      MessageType.Warning,
					                                      ButtonsType.YesNo,
					                                      "Le fichier existe déja. \nVoulez vous écraser le fichier existant ?");
					if ((ResponseType)msg.Run () == ResponseType.Yes)ok=true;
					msg.Destroy();

				}
				else ok=true;
			}
			fcd.Destroy();
			SaveFile();
		}

		void SaveFile ()
		{
			if (conduiteFile == null)
				return;

			using (FileStream stream = conduiteFile.Open(FileMode.Create,FileAccess.Write)) {
				System.Xml.XmlDocument doc = Conduite.Courante.Save ();
				doc.Save (stream);
				stream.Close ();
			}
			
		}	

		protected void OnOpenActionActivated (object sender, EventArgs e)
		{
			FileChooserDialog fcd = new FileChooserDialog ("Sauver sous ...", this, FileChooserAction.Open,
                                              "Annuler", ResponseType.Cancel,
                                              "Ouvrir", ResponseType.Accept);

			fcd.Filter = new FileFilter ();
			fcd.Filter.AddPattern ("*.dmx2");
			if ((ResponseType)fcd.Run () == ResponseType.Cancel) {
				fcd.Destroy ();
				return;
			}	

			FileInfo openFile = new FileInfo (fcd.Filename);
			fcd.Destroy ();
			if (!openFile.Exists)
				return;

			System.Xml.XmlDocument doc;

			try {
				using(FileStream stream = openFile.OpenRead())
				{
					doc = new System.Xml.XmlDocument();
					doc.Load(stream);
					stream.Close();
				}

				Conduite cond = Conduite.Load(doc);
				if (cond==null)
				{
					// TODO Message erreur au chargement
					return;
				}

				conduiteFile = openFile;

			} catch (IOException) {
			}

			foreach (Sequenceur s in Conduite.Courante.Sequenceurs) 
				AddSeqUI (s);
			
			MajWidgets();
			NextUpdateFull();
		}		

#endregion

#region Gestion des Circuits

		protected void OnCircuitsActionActivated (object sender, EventArgs e)
		{
			GestionCircuits gc= new GestionCircuits(this);
			gc.Run();
			gc.Destroy();
			NextUpdateFull();
		}

#endregion

#region Gestion des Sequenceurs

		// bonton ajout de sequenceur linéraire
		protected void OnSeqLinActionActivated (object sender, EventArgs e)
		{
			Sequenceur s = new SequenceurLineaire();
			Conduite.Courante.AjoutSequenceur(s);
			AddSeqUI(s);
			NextUpdateFull();
		}

		void AddSeqUI (Sequenceur s)
		{
			onglets.AppendPage(s.GetUI(),new Gtk.Label(s.Name));
			onglets.ShowAll();
		}

		protected void OnShowAllActionActivated (object sender, EventArgs e)
		{
			foreach (var notebookchild in onglets.Children) 
					notebookchild.Destroy();

			foreach (Sequenceur s in Conduite.Courante.Sequenceurs) 
				AddSeqUI(s);
		}	

		protected void OnSeqMacroActionActivated (object sender, EventArgs e)
		{
			Sequenceur s = new SequenceurMacro();
			Conduite.Courante.AjoutSequenceur(s);
			AddSeqUI(s);
			NextUpdateFull();
		}

#endregion

#region Gestion de l'affichage

		bool updScheduled=false;
		bool fullUpdateFlag=false;

		public void NextUpdateFull ()
		{
			fullUpdateFlag=true;
		}


		public void ScheduleUpdate ()
		{
			if (updScheduled) {
				//Console.WriteLine("{0} Skip !",DateTime.Now);
				return;
			}
			updScheduled=true;
			Gtk.Application.Invoke(new EventHandler(Update));

		}

		void Update (object sender, EventArgs e)
		{
			lblInfo.Text = Info.GetInfo();
			if(Conduite.Courante==null)return;
			foreach (var notebookchild in onglets.Children) {
				SequenceurUI sequi = notebookchild as SequenceurUI;
				if(sequi != null) sequi.Update(fullUpdateFlag);
			}
			MajCircuits(fullUpdateFlag);
			timeLabel.LabelProp = string.Format (@"{0:0\.0}", Conduite.Courante.SequenceurMaitre.TimeStamp.TotalMilliseconds / 100);
			if(fullUpdateFlag) ConstruitMatriceSeqColumns();
			if( Conduite.Courante.SequenceurMaitre.EffetChange() )MatriceUI.QueueDraw();
			fullUpdateFlag=false;
			masterScale.Value = Conduite.Courante.Master;
			updScheduled=false;
		}

		protected void MajWidgets ()
		{
			if (Conduite.Courante != null) {
				//Activation / Désactivation des boutons en fonction de l'état de la counduite courante
				btnAjoutLigne.Sensitive = btnRetireLigne.Sensitive = btnGo.Sensitive = btnGoBack.Sensitive =
					showAllAction.Sensitive = universAction.Sensitive = masterScale.Sensitive = 
					seqLinAction.Sensitive = seqMacroAction.Sensitive = circAction.Sensitive = saveAction.Sensitive = 
					btnPause.Sensitive = btnBlackOut.Sensitive =
					saveAsAction.Sensitive = closeAction.Sensitive = true;
				openAction.Sensitive = newAction.Sensitive = false;
				this.Title = "DMX 2.0 - " + Conduite.Courante.Name;
				ConstruitMatrice();
			} else {
				btnAjoutLigne.Sensitive = btnRetireLigne.Sensitive = btnGo.Sensitive = btnGoBack.Sensitive =
					showAllAction.Sensitive = universAction.Sensitive = masterScale.Sensitive = 
					seqLinAction.Sensitive = seqMacroAction.Sensitive = circAction.Sensitive = saveAction.Sensitive = 
					btnPause.Sensitive = btnBlackOut.Sensitive =
					saveAsAction.Sensitive = closeAction.Sensitive = false;
				openAction.Sensitive = newAction.Sensitive = true;
				this.Title = "DMX 2.0";
				DetruitMatrice();
				foreach (var widget in vboxCircuits.Children)
					vboxCircuits.Remove (widget);
				foreach (var notebookvbox in onglets.Children) 
						notebookvbox.Destroy();
			}
		}
 

		protected void MajCircuits (bool full)
		{
			// Ajoute une ProgressBar par circuit, met a jour le texte si necessaire
			ProgressBar pb;int i = 0;
			if (full) {
				foreach (var widget in vboxCircuits.Children)
					vboxCircuits.Remove (widget);
				foreach (var circuit in Conduite.Courante.Circuits) {
					vboxCircuits.PackStart (pb=new ProgressBar (),false,false,0);
					pb.Text = (++i).ToString() + " - " + circuit.Name;
					pb.HeightRequest = 12;
					pb.Data[circuitKey] = circuit;
					pb.Name ="pb";
				}
				vboxCircuits.ShowAll ();
			}

			foreach (var widget in vboxCircuits.Children) {
				pb = (ProgressBar)widget;
				pb.Fraction = (double) (pb.Data[circuitKey] as Circuit).ValeurCourante / 255;
			}
		}


#endregion

		protected void OnMasterScaleValueChanged (object sender, EventArgs e)
		{
			if(!updScheduled) Conduite.Courante.Master = (int)(masterScale.Value);
			ChangeCouleurInfo();
		}		


		bool isfullscreen=false;
		protected void OnFullscreenAction1Activated (object sender, EventArgs e)
		{
			if (!isfullscreen) {
				Fullscreen ();
				isfullscreen = true;
			} else {
				Unfullscreen();
				isfullscreen = false;
			}
		}

#region Boite dialogue Univers

		EditionUnivers uDlg = null;
		protected void OnUniversActionActivated (object sender, EventArgs e)
		{

			if (uDlg != null) {
				uDlg.Show();
				return;
			}

			uDlg = new EditionUnivers(this);
			uDlg.ShowAll();
			uDlg.Destroyed += udlgDestroyed;

		}

		void udlgDestroyed (object sender, EventArgs e)
		{
			uDlg=null;
		}

#endregion

		protected void OnConnectActionActivated (object sender, EventArgs e)
		{

			Conduite.Courante.Drivers.Add( new DriverBoitierV1());

		}		

		protected void OnBtnPauseToggled (object sender, EventArgs e)
		{
			Conduite.Courante.Pause = btnPause.Active;
			ChangeCouleurInfo();
		}
		protected void OnBtnBlackOutToggled (object sender, EventArgs e)
		{
			Conduite.Courante.BlackOut = btnBlackOut.Active;
			ChangeCouleurInfo();
		}		

		void ChangeCouleurInfo ()
		{
			if (Conduite.Courante.BlackOut) {


				evInfo.ModifyBg (Gtk.StateType.Normal,new Gdk.Color (255, 100, 100));
				return;
			}

			if (Conduite.Courante.Pause) {
				evInfo.ModifyBg (Gtk.StateType.Normal,new Gdk.Color (255, 255, 100));
				return;
			}

			if (Conduite.Courante.Master != 100) {
				evInfo.ModifyBg (Gtk.StateType.Normal,new Gdk.Color (205, 205, 255));
				return;
			}

			evInfo.ModifyBg (Gtk.StateType.Normal, this.Style.Background(StateType.Normal) );

		}	

		protected void OnSelectColorActionActivated (object sender, EventArgs e)
		{
			if(System.IO.File.Exists("style.gtkrc")) Gtk.Rc.Parse("style.gtkrc");
			else 
				using (System.IO.Stream stream = System.Reflection.Assembly.GetExecutingAssembly().GetManifestResourceStream("style.gtkrc"))
					using (System.IO.TextReader reader = new System.IO.StreamReader(stream))
						Gtk.Rc.ParseString (reader.ReadToEnd()); 

			this.ResetRcStyles();

		}
	}
}