// UI.Spectrometer.cs created with MonoDevelop
// User: klose at 10:51 26.01.2009
// CVS release: $Id: UI.Spectrometer.cs,v 1.41 2011-06-24 13:48:23 klose Exp $
//
//    QuickStep: Acquire, view and identify spectra 
//    Copyright (C) 2009  Thomas Klose, 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.Text;
using System.IO;
using Gtk;
using Glade;

using Hiperscan.SGS;
using Hiperscan.FileIO;
using Hiperscan.Spectroscopy;
using Hiperscan.Extensions;
using Hiperscan.Unix;


namespace Hiperscan.GUI
{
	
	public partial class UserInterface
	{
		
        private DeviceList    device_list;
		private ActiveDevices active_devices;
		private Dictionary<string,string>  found_devices = new Dictionary<string,string>();
		
		private delegate void UnshareDelegate();
		public event EventHandler<EventArgs> SpectrometerSelectionChanged;
		
		[Widget] Gtk.CheckButton auto_connect_checkbutton = null;
		[Widget] Gtk.CheckButton auto_select_checkbutton  = null;

		
		private void OnSpectrometerTvSelectionChanged(List<Device> devices)
		{
			this.CheckForChangedDeviceProperties();
			
			this.PropertyType = Config.PropertyType.Spectrometer;

			this.active_devices.Clear();
			this.active_devices.AddRange(devices);
			
			if (this.active_devices.Count == 0)
			{
				this.UpdateDeviceControls();
				this.UpdatePropertiesSpectrometer();
				return;
			}
			
//			foreach (Device dev in devices)
//				this.OnDeviceStateChanged(dev);
			this.UpdatePropertiesSpectrometer();
			this.UpdateDeviceControls();
			
			if (this.SpectrometerSelectionChanged != null)
				this.SpectrometerSelectionChanged(devices, EventArgs.Empty);

			this.active_devices.NotifyChanged();
		}
		
		private void OnDeviceAdded(DeviceList list, List<string> serials)
		{
			UserInterface.Queues.StatusQueue.Enqueue(new Queues.StatusEvent("device_list", Catalog.GetString("A new device was found")));
			this.UpdatePropertiesSpectrometer();

			foreach (string serial in serials)
			{
				Device dev = list[serial];
				if (dev == null)
					continue;
				string name = dev.CurrentConfig.Name;
				if (!this.found_devices.ContainsKey(serial))
					this.found_devices.Add(serial, name);
				string msg = string.Format(Catalog.GetString("Found new device: {0}."), serial);
				new StatusMessage(this.parent, msg);
			}
		}
		
		private void OnDeviceRemoved(DeviceList list, Dictionary<string,string> removed)
		{
			UserInterface.Queues.StatusQueue.Enqueue(new Queues.StatusEvent("device_list", Catalog.GetString("A device has been removed")));
			this.UpdatePropertiesSpectrometer();

			foreach (string serial in removed.Keys)
			{
				if (!this.found_devices.ContainsKey(serial))
					continue;
				
				string msg = string.Format(Catalog.GetString("Device has been removed: '{0}' ({1})."),
				                           removed[serial], serial);
				new StatusMessage(this.parent, msg);
				this.found_devices.Remove(serial);
			}
		}
		
		public static void DeviceStateChanged(Device dev)
		{
			UserInterface.UI.OnDeviceStateChanged(dev);
		}
		
		private void OnDeviceStateChanged(Device dev)
		{
			UserInterface.Queues.StatusQueue.Enqueue(new Queues.StatusEvent("device_status" + dev.Info.SerialNumber,
			                                                                string.Format(Catalog.GetString("Device {0} changed state to '{1}'"),
			                                                                              dev, dev.GetState().ToString()
			                                                                              )
			                                                                )
			                                         );

			if (this.active_devices.Contains(dev))
				this.UpdatePropertiesSpectrometer();
			
			this.UpdateDeviceControls();
		}
		
		private void OnDeviceException(SGSException ex)
		{
			this.current_device = null;
			UserInterface.Queues.MessageQueue.Enqueue(ex.Message);
		}
		
		private void OnDeviceWarning(Device dev, string msg)
		{
			string s = string.Format(Catalog.GetString("Warning from device {0} ({1}):") + " " + msg, dev.CurrentConfig.Name, dev.Info.SerialNumber);
			UserInterface.Queues.MessageQueue.Enqueue(s);
		}

		internal void OnAutoConnectToggled(object o, System.EventArgs e)
		{
			CheckButton c = o as CheckButton;
			this.spectrometer_tv.AutoConnect       = c.Active;
			this.auto_select_checkbutton.Sensitive = c.Active;
			Settings.Current.AutoConnect           = c.Active;
		}

		internal void OnAutoSelectToggled(object o, System.EventArgs e)
		{
			CheckButton c = o as CheckButton;
			this.spectrometer_tv.AutoSelect = c.Active;
			Settings.Current.AutoSelect     = c.Active;
		}
				
		private void StartSingle(Device dev)
        {
			Device.State state = dev.GetState();
			
			if (state == Device.State.Single)
				return;
			
			if (state != Device.State.Idle &&
			    state != Device.State.Continuous &&
			    state != Device.State.Stream &&
			    state != Device.State.QuickStream)
				return;
			
			dev.StartSingle(true);
        }

		private void StartStream(Device dev)
        {
			Device.State state = dev.GetState();
			
			if ((state == Device.State.Continuous) ||
			    (state == Device.State.Stream))
				return;
			
			if (state != Device.State.Idle &&
			    state != Device.State.Continuous &&
			    state != Device.State.Stream &&
			    state != Device.State.QuickStream)
				return;
			
			dev.StartStream();			
        }
		
		private void StartQuickStream(Device dev)
		{
			Device.State state = dev.GetState();
			
			if (state == Device.State.QuickStream)
				return;
			
			if (state != Device.State.Idle &&
			    state != Device.State.Continuous &&
			    state != Device.State.Stream &&
			    state != Device.State.QuickStream)
				return;

			dev.StartQuickStream();			
		}
		
		internal bool ShareDevice(Device dev)
		{
			return this.ShareDevice(MainClass.MainWindow, dev);
		}
		
		internal bool ShareDevice(Window parent, Device dev)
		{
			string title = string.Format(Catalog.GetString("Port number ({0})"), dev.Info.SerialNumber);
			Dialog dialog = new Dialog(title, parent, DialogFlags.Modal, 
			                           Stock.Ok,     ResponseType.Ok,
			                           Stock.Cancel, ResponseType.Cancel);
			
			dialog.Default = dialog.ActionArea.Children[1];
			
			HBox hbox = new HBox();
			hbox.Add(new Label(Catalog.GetString("Specify TCP port number:")));
			SpinButton spin = new SpinButton(49152f, 65535f, 1f);
			spin.Value = Settings.Current.RemoteUSBSharePort;
			spin.ActivatesDefault = true;
			hbox.Add(spin);
			hbox.Spacing = 10;
			hbox.BorderWidth = 5;
			dialog.VBox.Add(hbox);
			dialog.ShowAll();
			ResponseType choice = (ResponseType)dialog.Run();
			uint port = (uint)spin.Value;
			dialog.Destroy();
			
			if (choice != ResponseType.Ok)
				return false;

			return this.ShareDevice(parent, dev, port);
		}
		
		internal bool ShareDevice(Window parent, Device dev, uint port)
		{
			try
			{
				dev.Share(port);
				Settings.Current.RemoteUSBSharePort = port;
			}
			catch (System.Net.Sockets.SocketException ex)
			{
				new InfoMessage(parent, Catalog.GetString("Cannot share device: {0}\n\nTCP port {1} could be in use by another application."),
				                ex.Message, port.ToString());
			}
			catch (Exception ex)
			{
				new InfoMessage(parent, Catalog.GetString("Cannot share device: {0}"), ex.Message);
				return false;
			}
			finally
			{
				TrayIcon.SetTooltip(SharedUSB.SharedCount, SharedUSB.ConnectedCount);
			}
			
			return true;
		}
		
		internal bool UnshareDevice(Device dev)
		{
			return this.UnshareDevice(MainClass.MainWindow, dev);
		}
		
		internal bool UnshareDevice(Window parent, Device dev)
		{
			if (dev.GetState() != Device.State.Shared)
				return false;
			
			try
			{
				UnshareDelegate dlg = dev.Unshare;
				dlg.BeginInvoke(null, null);
			}
			catch (Exception ex)
			{
				new InfoMessage(parent, Catalog.GetString("Cannot unshare device: {0}"), ex.Message);
				return false;
			}
			finally
			{
				TrayIcon.SetTooltip(SharedUSB.SharedCount, SharedUSB.ConnectedCount);
			}
			
			return true;
		}
		
		internal void ConnectRemoteDevice()
		{
			string title = Catalog.GetString("Connect Remote Device");
			Dialog dialog = new Dialog(title, this.parent, DialogFlags.Modal,
			                           Stock.Ok,     ResponseType.Ok,
			                           Stock.Cancel, ResponseType.Cancel);
			
			dialog.Default = dialog.ActionArea.Children[1];			
			
			HBox hbox = new HBox();
			hbox.Add(new Label(Catalog.GetString("Specify server address:")));
			
			HBox hbox2 = new HBox();
			hbox2.Add(new Label("tcp://"));
			Entry entry = new Entry(20);
			hbox2.Add(entry);
			hbox2.Add(new Label(":"));
			SpinButton spin = new SpinButton(49152f, 65535f, 1f);
			hbox2.Add(spin);
			
			hbox.Add(hbox2);
			hbox.Spacing = 10;
			hbox.BorderWidth = 5;
			
			entry.Text = Settings.Current.RemoteUSBServer;
			entry.ActivatesDefault = true;
			spin.Value = Settings.Current.RemoteUSBServerPort;
			spin.ActivatesDefault = true;
			
			dialog.VBox.Add(hbox);
			dialog.ShowAll();
			ResponseType choice = (ResponseType)dialog.Run();
			string server = entry.Text;
			uint port = (uint)spin.Value;
			dialog.Destroy();
			
			if (choice != ResponseType.Ok)
				return;
			
			try
			{
				if (string.IsNullOrEmpty(server))
					throw new ArgumentException(Catalog.GetString("A server address must be specified."));
				this.DeviceList.AddRemoteDevice(server, port);
				Settings.Current.RemoteUSBServer     = server;
				Settings.Current.RemoteUSBServerPort = port;
			}
			catch (Exception ex)
			{
				new InfoMessage(this.parent, Catalog.GetString("Cannot connect remote device: {0}"), ex.Message);
			}
		}
		
		internal void DisconnectRemoteDevice(Device dev)
		{
			if (!dev.Info.IsRemote)
				return;
			
			Device.State state = dev.GetState();
			if (state != Device.State.Unprogrammed &&
			    state != Device.State.Disconnected &&
			    state != Device.State.Unknown)
			{
				this.StopStream(dev);
				dev.WaitForIdle(10000);
			}
			
			dev.Close();
			
			this.DeviceList.RemoveRemoteDevice(dev);
		}
		
		public void ShowQuickStream(Device dev)
		{
			this.parent.ShowAll();
			this.parent.Present();
			
			this.StartQuickStream(dev);
			
			Dialog dialog = new Dialog(Catalog.GetString("QuickStream"), this.parent,
			                           DialogFlags.Modal, Stock.GoBack, ResponseType.Close);
			dialog.Run();
			dialog.Destroy();
			
			this.StopStream(dev);
		}
		
		internal void StopStream(Device dev)
		{
			Device.State state = dev.GetState();

			if ((state != Device.State.Continuous) &&
			    (state != Device.State.Stream)      &&
			    (state != Device.State.QuickStream))
				return;
			
			dev.StopStream();
		}
		
		private Spectrum Single(Device dev, bool auto_finder_light)
		{
			Spectrum spectrum = null;
			try
			{
				spectrum = dev.Single(auto_finder_light, UserInterface.ProcessEvents_);
			}
			catch (System.Exception ex)
			{
				new InfoMessage(this.parent,
				                Catalog.GetString("An error occured while accessing the device with name {0} (serial number {1}): {2}"),
				                dev.CurrentConfig.Name,
				                dev.Info.SerialNumber,
				                ex.Message);
			}
			return spectrum;
		}
		
		public void StopAllStreams()
		{
			foreach (Device dev in this.device_list.Values)
			{
				this.StopStream(dev);
			}
			foreach (Device dev in this.device_list.Values)
			{
				if (dev.GetState() == Device.State.Disconnected)
					continue;
				dev.WaitForIdle(10000);
			}
		}
		
		public void CloseAllDevices()
		{
			foreach (Device dev in this.device_list.Values)
			{
				if (dev.IsIdle)
					dev.Close();
			}
			Device.Exit();
		}
		
		public void SetCreator(Spectrum spectrum)
		{
			UserInterface._SetCreator(spectrum);
		}
		
		internal static void _SetCreator(Spectrum spectrum)
		{
			spectrum.Creator = MainClass.ApplicationName + " " + 
				MainClass.Assembly.GetName().Version.ToString();
			if (!string.IsNullOrEmpty(MainClass.BuildDate))
				spectrum.Creator += " (" + MainClass.BuildDate + ")";
		}
		
		public DeviceList DeviceList
		{
			get { return this.device_list; }
		}
		
		public IActiveDevices ActiveDevices
		{
			get { return this.active_devices; }
		}
	}
}
