//
//  Plugin.cs
//
// QuickStep: Acquire, view and identify spectra 
// Copyright (C) 2010-2017  Thomas Klose & Torsten Schache, Hiperscan GmbH Dresden, Germany
//
//  This program is free software: you can redistribute it and/or modify
//  it under the terms of the GNU General Public License as published by
//  the Free Software Foundation, either version 3 of the License, or
//  (at your option) any later version.
//
//  This program is distributed in the hope that it will be useful,
//  but WITHOUT ANY WARRANTY; without even the implied warranty of
//  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
//  GNU General Public License for more details.
//
//  You should have received a copy of the GNU General Public License
//  along with this program.  If not, see <http://www.gnu.org/licenses/>.



using System;
using System.Collections.Generic;
using System.Reflection;
using Gtk;

using Hiperscan.Common;
using Hiperscan.Spectroscopy;
using Hiperscan.Unix;


namespace Hiperscan.Extensions.Acquisition
{

	public class Plugin : IPlugin
	{
		public static readonly string name    = Catalog.GetString("Acquisition");
		public static readonly string version = "1.0.0";
		public static readonly string vendor  = "Hiperscan GmbH";
		public static readonly string description = Catalog.GetString("Perform standard measurements");
		public static readonly string guid = "a58abc50-8da4-11df-a4ee-0800200c9a66";
		
		internal static IHost host;
		internal static SpinButton acquisition_count_spin;
		internal static Label remaining_time_label;

		internal static ToolButton w_button, g_button, z_button, siz_button, save_button;

		private static MenuInfo menu_info10, menu_info11, menu_info12, transflectance_insert_menu_info;
		private static ToolbarInfo toolbar_info0, toolbar_info1, toolbar_info20, toolbar_info21, toolbar_info22, transflectance_insert_toolbar_info; 
		
		private MenuInfo[] menu_infos;
		private ToolbarInfo[] toolbar_infos;
		private bool active = true;

		public static DateTime LastExtwhiteTime                      = DateTime.MinValue;
		public static DateTime LastExtemptyTime                      = DateTime.MinValue;
		public static DateTime LastZenithTime                        = DateTime.MinValue;
		public static DateTime LastSampleInsertZenithTime            = DateTime.MinValue;
		public static DateTime LastTransflectanceInsertReferenceTime = DateTime.MinValue;

		private static TimeSpan reference_time_limit;
		
		public static readonly Gdk.Pixbuf[] FinderIcons = new Gdk.Pixbuf[5];
		public static bool SampleInsertMode = false;

		
		public Plugin()
		{
			int ref_time_limit_minutes;
			if (!int.TryParse(Environment.GetEnvironmentVariable(Env.Plugin.ACQUISITION_TIME_LIMIT), out ref_time_limit_minutes))
				ref_time_limit_minutes = 20;
			Plugin.reference_time_limit = new TimeSpan(0, ref_time_limit_minutes, 0);

			Assembly assembly = Assembly.GetExecutingAssembly();
			FinderIcons[0] = new Gdk.Pixbuf(assembly, "Acquisition.finder16x16.png");
			FinderIcons[1] = new Gdk.Pixbuf(assembly, "Acquisition.finder32x32.png");
			FinderIcons[2] = new Gdk.Pixbuf(assembly, "Acquisition.finder48x48.png");
			FinderIcons[3] = new Gdk.Pixbuf(assembly, "Acquisition.finder64x64.png");
			FinderIcons[4] = new Gdk.Pixbuf(assembly, "Acquisition.finder128x128.png");

			IconSet icon_set = new IconSet();
			foreach (Gdk.Pixbuf pixbuf in Plugin.FinderIcons)
			{
				IconSource icon_source = new IconSource();
				icon_source.Pixbuf = pixbuf;
				icon_set.AddSource(icon_source);
			}
			IconFactory icon_factory = new IconFactory();
			icon_factory.Add("hiperscan.acquisition.finder", icon_set);
			icon_factory.AddDefault();
			
			menu_info10 = new MenuInfo(Catalog.GetString("Solid sample (auto reference)"));
			menu_info10.WatchCursor = true;
			menu_info10.NeedIdleDevice = true;
			menu_info10.Invoked += delegate(object sender, EventArgs e) {
				Handlers.OnAutoSolidClicked(Plugin.host, false);
			};
			
			menu_info11 = new MenuInfo(Catalog.GetString("Fluid sample (auto reference)"));
			menu_info11.WatchCursor = true;
			menu_info11.NeedIdleDevice = true;
			menu_info11.Invoked += delegate(object sender, EventArgs e) {
				Handlers.OnAutoFluidClicked(Plugin.host);
			};
			
			menu_info12 = new MenuInfo(Catalog.GetString("Solid sample (Sample Insert)"));
			menu_info12.WatchCursor = true;
			menu_info12.NeedIdleDevice = true;
			menu_info12.Invoked += delegate(object sender, EventArgs e) {
				Handlers.OnAutoSolidClicked(Plugin.host, true);
			};
			
			MenuInfo menu_info2 = new MenuInfo(Catalog.GetString("White Reference"));
			menu_info2.WatchCursor = true;
			menu_info2.NeedIdleDevice = true;
			menu_info2.Invoked += delegate(object sender, EventArgs e) {
				Handlers.OnTitanClicked(Plugin.host);
			};
			
			MenuInfo menu_info3 = new MenuInfo(Catalog.GetString("Empty Reference"));
			menu_info3.WatchCursor = true;
			menu_info3.NeedIdleDevice = true;
			menu_info3.Invoked += delegate(object sender, EventArgs e) {
				Handlers.OnEmptyClicked(Plugin.host);
			};
			
			MenuInfo menu_info4 = new MenuInfo(Catalog.GetString("Labsphere"));
			menu_info4.WatchCursor = true;
			menu_info4.NeedIdleDevice = true;
			menu_info4.Invoked += delegate(object sender, EventArgs e) {
				Handlers.OnLabsphereClicked(Plugin.host);
			};
			
			transflectance_insert_menu_info = new MenuInfo(Catalog.GetString("Transflectance Insert"));
			transflectance_insert_menu_info.WatchCursor = true;
			transflectance_insert_menu_info.NeedIdleDevice = true;
			transflectance_insert_menu_info.Invoked += delegate(object sender, EventArgs e) {
				Handlers.OnTransflectanceInsertClicked(Plugin.host);
			};

			MenuInfo menu_info5a = new MenuInfo(Catalog.GetString("Zenith"));
			menu_info5a.WatchCursor = true;
			menu_info5a.NeedIdleDevice = true;
			menu_info5a.Invoked += delegate(object sender, EventArgs e) {
				Handlers.OnZenithClicked(Plugin.host, false);
			};

			MenuInfo menu_info5b = new MenuInfo(Catalog.GetString("Zenith (Sample Insert)"));
			menu_info5b.WatchCursor = true;
			menu_info5b.NeedIdleDevice = true;
			menu_info5b.Invoked += delegate(object sender, EventArgs e) {
				Handlers.OnZenithClicked(Plugin.host, true);
			};

			MenuInfo menu_info6a = new MenuInfo(Catalog.GetString("Reset output path"));
			menu_info6a.WatchCursor = false;
			menu_info6a.NeedIdleDevice = false;
			menu_info6a.Invoked += delegate(object sender, EventArgs e) {
				Handlers.OnResetReferencePathClicked(Plugin.host);
			};

			MenuInfo menu_info6b = new MenuInfo(Catalog.GetString("Reset external references"));
			menu_info6b.WatchCursor = false;
			menu_info6b.NeedIdleDevice = false;
			menu_info6b.Invoked += delegate(object sender, EventArgs e) {
				Plugin.ResetExternalReferences();
			};

			MenuInfo menu_info7 = new MenuInfo(Catalog.GetString("Acquire samples"));
			menu_info7.WatchCursor = true;
			menu_info7.NeedIdleDevice = true;
			menu_info7.NeedSingleDevice = true;
			menu_info7.Invoked += delegate(object sender, EventArgs e) {
				new SampleAcquisitionDialog();
			};

			MenuInfo menu_info8 = new MenuInfo(Catalog.GetString("Settings ..."));
			menu_info8.WatchCursor = false;
			menu_info8.NeedIdleDevice = false;
			menu_info8.Invoked += delegate(object sender, EventArgs e) {
				new AcquisitionSettingsDialog();
			};

			this.menu_infos = new MenuInfo[] {
				menu_info10, menu_info11, menu_info12, menu_info2, menu_info3, 
				menu_info4, transflectance_insert_menu_info, menu_info5a, menu_info6a, menu_info6b, 
				menu_info7, menu_info8};

			Plugin.remaining_time_label = new Label(" 0 min");
			Plugin.remaining_time_label.WidthChars = 7;
			ToolItem item = new ToolItem();
			item.Add(Plugin.remaining_time_label);
			toolbar_info0 = new ToolbarInfo(item,
			                                Catalog.GetString("Remaining time"),
			                                Catalog.GetString("Remaining time before external references expire"));

			Plugin.acquisition_count_spin = new SpinButton(1f, 20f, 1f);
			Plugin.acquisition_count_spin.WidthChars = 2;
			Alignment align = new Alignment(0.5f, 0.5f, 0f, 0f);
			align.Add(Plugin.acquisition_count_spin);
			item = new ToolItem();
			item.Add(align);
			toolbar_info1 = new ToolbarInfo(item,
			                                Catalog.GetString("Acquisition count"),
			                                Catalog.GetString("Specify number of spectra to acquire"));
			toolbar_info1.NeedIdleDevice = true;


			item = new ToolItem();
			EventBox eb = new EventBox();
			ToolButton s_button = new ToolButton(new Label("5:S"), string.Empty);
			eb.Add(s_button);
			item.Add(eb);
			transflectance_insert_toolbar_info = new ToolbarInfo(item, 
			                                                    Catalog.GetString("Transflectance Reference"),
			                                                    Catalog.GetString("Acquire Transflectance Reference spectrum"));
			transflectance_insert_toolbar_info.WatchCursor = true;
			transflectance_insert_toolbar_info.NeedIdleDevice = true;
			s_button.Clicked += delegate(object sender, EventArgs e) {
				Handlers.OnTransflectanceInsertClicked(Plugin.host);
			};


			item = new ToolItem();
			eb = new EventBox();
			siz_button = new ToolButton(new Label("3:E"), string.Empty);
			siz_button.Sensitive = false;
			eb.Add(siz_button);
			item.Add(eb);
			ToolbarInfo toolbar_info8 = new ToolbarInfo(item, 
			                                            Catalog.GetString("Sample Insert White Reference"), 
			                                            Catalog.GetString("Acquire Sample Insert White Reference spectrum"));
			toolbar_info8.WatchCursor = true;
			toolbar_info8.NeedIdleDevice = true;
			siz_button.Clicked += delegate(object sender, EventArgs e) {
				Handlers.OnZenithClicked(Plugin.host, true);
			};

			item = new ToolItem();
			eb = new EventBox();
			eb.ModifyBg(StateType.Normal, new Gdk.Color(100, 255, 100));
			ToolButton b10 = new ToolButton(Stock.Execute);
			eb.Add(b10);
			item.Add(eb);
			toolbar_info20 = new ToolbarInfo(item, 
			                                 Catalog.GetString("Auto acquisition (solid)"), 
			                                 Catalog.GetString("Acquire solid sample spectrum (auto reference)"));
			toolbar_info20.WatchCursor = true;
			toolbar_info20.NeedIdleDevice = true;
			b10.Clicked += delegate(object sender, EventArgs e) {
				Handlers.OnAutoSolidClicked(Plugin.host, false);
			};
			
			item = new ToolItem();
			eb = new EventBox();
			eb.ModifyBg(StateType.Normal, new Gdk.Color(100, 100, 255));
			ToolButton fluid_button = new ToolButton(Stock.Execute);
			eb.Add(fluid_button);
			item.Add(eb);
			toolbar_info21 = new ToolbarInfo(item, 
			                                 Catalog.GetString("Auto acquisition (fluid)"), 
			                                 Catalog.GetString("Acquire fluid sample spectrum (auto reference)"));
			toolbar_info21.WatchCursor = true;
			toolbar_info21.NeedIdleDevice = true;
			fluid_button.Clicked += delegate(object sender, EventArgs e) {
				Handlers.OnAutoFluidClicked(Plugin.host);
			};
			
			item = new ToolItem();
			eb = new EventBox();
			eb.ModifyBg(StateType.Normal, new Gdk.Color(0, 0, 0));
			ToolButton si_button = new ToolButton(Stock.Execute);
			si_button.Visible = false;
			si_button.NoShowAll = true;
			eb.Add(si_button);
			item.Add(eb);
			toolbar_info22 = new ToolbarInfo(item, 
			                                 Catalog.GetString("Auto acquisition (Sample Insert)"), 
			                                 Catalog.GetString("Acquire solid sample spectrum (Sample Insert)"));
			toolbar_info22.WatchCursor = true;
			toolbar_info22.NeedIdleDevice = true;
			si_button.Clicked += delegate(object sender, EventArgs e) {
				Handlers.OnAutoSolidClicked(Plugin.host, true);
			};


			w_button = new ToolButton(new Label("1:W"), string.Empty);
			ToolbarInfo toolbar_info3 = new ToolbarInfo(w_button, 
			                                            Catalog.GetString("White Reference"),
			                                            Catalog.GetString("Acquire White Standard spectrum"));
			toolbar_info3.WatchCursor = true;
			toolbar_info3.NeedIdleDevice = true;
			toolbar_info3.Invoked += delegate(object sender, EventArgs e) {
				Handlers.OnTitanClicked(Plugin.host);
			};
			
			g_button = new ToolButton(new Label("4:B"), string.Empty);
			ToolbarInfo toolbar_info4 = new ToolbarInfo(g_button, 
			                                            Catalog.GetString("Black Standard"),
			                                            Catalog.GetString("Acquire Black Standard spectrum"));
			toolbar_info4.WatchCursor = true;
			toolbar_info4.NeedIdleDevice = true;
			toolbar_info4.Invoked += delegate(object sender, EventArgs e) {
				Handlers.OnEmptyClicked(Plugin.host);
			};
			
//			ToolButton b4 = new ToolButton(new Label("3:L"), string.Empty);
//			ToolbarInfo toolbar_info5 = new ToolbarInfo(b4, 
//			                                            Catalog.GetString("Labsphere"),
//			                                            Catalog.GetString("Acquire Labsphere spectrum"));
//			toolbar_info5.WatchCursor = true;
//			toolbar_info5.NeedIdleDevice = true;
//			toolbar_info5.Invoked += delegate(object sender, EventArgs e) {
//				Handlers.OnLabsphereClicked(Plugin.host);
//			};


			z_button = new ToolButton(new Label("2:Z"), string.Empty);
			ToolbarInfo toolbar_info6z = new ToolbarInfo(z_button, 
			                                             Catalog.GetString("Zenith"),
			                                             Catalog.GetString("Acquire Zenith spectrum"));
			toolbar_info6z.WatchCursor = true;
			toolbar_info6z.NeedIdleDevice = true;
			toolbar_info6z.Invoked += delegate(object sender, EventArgs e) {
				Handlers.OnZenithClicked(Plugin.host, false);
			};

			ToolButton b7 = new ToolButton("hiperscan.acquisition.finder");
			ToolbarInfo toolbar_info7 = new ToolbarInfo(b7, 
			                                            Catalog.GetString("Sample Acquisition"), 
			                                            Catalog.GetString("Start Sample Acquisition"));
			ToggleToolButton b12 = new ToggleToolButton();
			b12.LabelWidget = new Label("SI");
			ToolbarInfo toolbar_info12 = new ToolbarInfo(b12, 
			                                            Catalog.GetString("Sample Insert Mode"), 
			                                            Catalog.GetString("Switch Sample Insert Mode"));
			toolbar_info12.NeedIdleDevice = false;
			toolbar_info12.NeedSingleDevice = false;
			toolbar_info12.Invoked += delegate(object sender, EventArgs e) {
				ToggleToolButton ttb = sender as ToggleToolButton;
				Plugin.SampleInsertMode = ttb.Active;
				Plugin.ResetExternalReferences();
				Plugin.UpdateControls();

				if (ttb.Active)
				{
					s_button.Sensitive = false;
					siz_button.Sensitive = true;

					fluid_button.HideAll();
					fluid_button.NoShowAll = true;
					si_button.NoShowAll = false;
					si_button.ShowAll();
				}
				else
				{
					siz_button.Sensitive = false;
					s_button.Sensitive = true;

					si_button.HideAll();
					si_button.NoShowAll = true;
					fluid_button.NoShowAll = false;
					fluid_button.ShowAll();
				}
			};

			save_button = new ToolButton(Stock.SaveAs);
			ToolbarInfo toolbar_info61 = new ToolbarInfo(save_button, Catalog.GetString("Save all"), Catalog.GetString("Save all Sample Spectra"));
			save_button.Sensitive = false;
			toolbar_info61.Invoked += delegate(object sender, EventArgs e) {
				Handlers.OnSaveAllClicked(Plugin.host);
			};

			toolbar_info7.NeedIdleDevice = true;
			toolbar_info7.NeedSingleDevice = true;
			toolbar_info7.Invoked += delegate(object sender, EventArgs e) {
				new SampleAcquisitionDialog();
			};

			this.toolbar_infos = new ToolbarInfo[] {toolbar_info0, toolbar_info1, toolbar_info12, toolbar_info20, toolbar_info21, 
				toolbar_info22, toolbar_info3, toolbar_info6z, toolbar_info8, toolbar_info4, transflectance_insert_toolbar_info, toolbar_info61, toolbar_info7};

			Plugin.UpdateControls();
		}

		private static bool timer_is_running = false;

		public static bool UpdateControls()
		{
			TimeSpan max_span = TimeSpan.MinValue;
			DateTime now = DateTime.Now;

			Gdk.Color red = Gdk.Color.Zero;
			Gdk.Color green = Gdk.Color.Zero;
			Gdk.Color.Parse("red", ref red);
			Gdk.Color.Parse("darkgreen", ref green);

			bool need_another_update = false;
			bool valid = true;

			TimeSpan span = now - Plugin.LastExtwhiteTime;
			Label l = (Label)((Button)(Plugin.w_button.Child)).Child;
			if (span > reference_time_limit)
			{
				l.ModifyFg(StateType.Normal, red);
				valid = false;
			}
			else
			{
				l.ModifyFg(StateType.Normal, green);
				need_another_update = true;
			}
			if (span > max_span)
				max_span = span;

			span = now - Plugin.LastExtemptyTime;
			l = (Label)((Button)(Plugin.g_button.Child)).Child;
			if (span > reference_time_limit)
			{
				l.ModifyFg(StateType.Normal, red);
				valid = false;
			}
			else
			{
				l.ModifyFg(StateType.Normal, green);
				need_another_update = true;
			}
			if (span > max_span)
				max_span = span;

			span = now - Plugin.LastZenithTime;
			l = (Label)((Button)(Plugin.z_button.Child)).Child;
			if (span > reference_time_limit)
			{
				l.ModifyFg(StateType.Normal, red);
				valid = false;
			}
			else
			{
				l.ModifyFg(StateType.Normal, green);
				need_another_update = true;
			}
			if (span > max_span)
				max_span = span;

			if (Plugin.SampleInsertMode)
			{
				span = now - Plugin.LastSampleInsertZenithTime;
				l = (Label)((Button)(Plugin.siz_button.Child)).Child;
				if (span > reference_time_limit)
				{
					l.ModifyFg(StateType.Normal, red);
					valid = false;
				}
				else
				{
					l.ModifyFg(StateType.Normal, green);
					need_another_update = true;
				}
				if (span > max_span)
					max_span = span;
			}

			Plugin.transflectance_insert_menu_info.IsSensitive = valid;
			Plugin.transflectance_insert_toolbar_info.IsSensitive = valid;

			bool insert_valid = valid && (now - Plugin.LastTransflectanceInsertReferenceTime < reference_time_limit);

			Plugin.menu_info10.IsSensitive = valid && insert_valid == false;
			Plugin.toolbar_info20.IsSensitive = valid && insert_valid == false;
			Plugin.menu_info12.IsSensitive = valid && insert_valid == false;
			Plugin.toolbar_info22.IsSensitive = valid && insert_valid == false;

			Plugin.menu_info11.IsSensitive = insert_valid;
			Plugin.toolbar_info21.IsSensitive = insert_valid;

			Plugin.remaining_time_label.Sensitive = valid;

			if (Plugin.host != null && valid == false && need_another_update == true)
				Plugin.host.UpdateDeviceControls();

			if (valid)
			{
				double total_minutes = (Plugin.reference_time_limit - max_span).TotalMinutes;
				if (total_minutes < 1.0)
					Plugin.remaining_time_label.Text = "<1 min";
				else
					Plugin.remaining_time_label.Text = string.Format("{0,2} min", Math.Round(total_minutes));
			}
			else
				Plugin.remaining_time_label.Text = " 0 min";

			if (Plugin.timer_is_running == false && need_another_update == true)
				GLib.Timeout.Add(10000, Plugin.UpdateControls);

			Plugin.timer_is_running = need_another_update;

			if (Plugin.host != null)
			{
				bool save_active = false;

				foreach (Spectrum spectrum in Plugin.host.CurrentSpectra.Values)
				{
					if (spectrum.SpectrumType == SpectrumType.FluidSpecimen ||
					    spectrum.SpectrumType == SpectrumType.SolidSpecimen)
					{
						save_active = true;
						break;
					}
				}

				Plugin.save_button.Sensitive = save_active;
			}

			return need_another_update;
		}

		public static void ResetExternalReferences(TimeSpan span)
		{
			TimeSpan limit = Plugin.reference_time_limit - span;

			if (DateTime.Now - Plugin.LastExtwhiteTime > limit)
				Plugin.LastExtwhiteTime = DateTime.MinValue;

			if (DateTime.Now - Plugin.LastExtemptyTime > limit)
				Plugin.LastExtemptyTime = DateTime.MinValue;

			if (DateTime.Now - Plugin.LastZenithTime > limit)
				Plugin.LastZenithTime = DateTime.MinValue;

			if (DateTime.Now - Plugin.LastSampleInsertZenithTime > limit)
				Plugin.LastSampleInsertZenithTime = DateTime.MinValue;

			if (DateTime.Now - Plugin.LastTransflectanceInsertReferenceTime > limit)
				Plugin.LastTransflectanceInsertReferenceTime = DateTime.MinValue;
		}

		public static void ResetExternalReferences()
		{
			Plugin.LastExtwhiteTime          = DateTime.MinValue;
			Plugin.LastExtemptyTime          = DateTime.MinValue;
			Plugin.LastZenithTime            = DateTime.MinValue;
			Plugin.LastSampleInsertZenithTime = DateTime.MinValue;
			Plugin.LastTransflectanceInsertReferenceTime = DateTime.MinValue;
		}
		
		public void ReplaceGui()
		{
		}
		
		public MenuInfo[] MenuInfos
		{
			get { return this.menu_infos; }
		}
		
		public ToolbarInfo[] ToolbarInfos
		{
			get { return this.toolbar_infos; }
		}

		public PlotTypeInfo[] PlotTypeInfos
		{
			get { return null; }
		}

		public FileTypeInfo[] FileTypeInfos
		{
			get { return null; }
		}

		public void Initialize(IHost host)
		{
			Plugin.host = host;
			
			host.SpectrometerSelectionChanged += delegate(object sender, EventArgs e) {
				Handlers.OnResetReferencePathClicked(host);
			};

			host.ActiveDevices.Changed += delegate(IActiveDevices active_devices) {
				Plugin.ResetExternalReferences();
				Plugin.UpdateControls();
			};

			Settings.Init();
			Plugin.acquisition_count_spin.Value = Settings.Current.AcquisitionCount;
			Plugin.acquisition_count_spin.Changed += delegate(object sender, EventArgs e) {
				Settings.Current.AcquisitionCount = (int)Plugin.acquisition_count_spin.Value;
			};
		}
		
		public string Name
		{
			get { return Plugin.name; }
		}
		
		public string Version
		{
			get { return Plugin.version; }
		}
		
		public string Vendor
		{
			get { return Plugin.vendor; }
		}

		public Assembly Assembly
		{
			get { return Assembly.GetExecutingAssembly(); }
		}

		public string Description
		{
			get { return Plugin.description; }
		}
		
		public Guid Guid
		{
			get { return new Guid(Plugin.guid); }
		}
		
		public bool Active
		{
			get { return this.active;  }
			set { this.active = value; }
		}

		public bool CanReplaceGui
		{
			get { return false; }
		}
	}
}
