// Created with MonoDevelop
//
//    QuickStep: Acquire, view and identify spectra 
//    Copyright (C) 2012 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 System.IO;

using HollyLibrary;
using Hiperscan.Unix;
using Hiperscan.I18n;


namespace Hiperscan.Extensions.ApoIdent
{
		public enum AnimationType
	{
		Working,
		Error,
		ReadyForInput,
		ReadyForEmptyDie,
		MeasuringEmptyDie,
		ReadyForMeasurement,
		ReadyForMeasurementAttention,
		ReadyForMeasurementFluid,
		ReadyForMeasurementFluidAttention,
		Measuring,
		ReadyForProtocolInput,
		ReadyForProtocolCreation,
		Saved,
		SavingFailed,
		NotMeasurable,
		InternalRecalibration,
		ExternalRecalibration,
		None,
	}
	
	public enum ErrorPriorities
	{
		High,
		Middle,
		None,
	}
	
	public enum AnimationErrorType
	{
		DeviceNotReady,
		DeviceNotInitialized,
		DeviceWarmupFailed,
		
		IdentServerNotInitialized,
		IdentServerLoadModelsFailed,
		IdentServerNoModelsAvailable,
		NonReleasedIdentModule,
		ConnectionLost,
		
		EmptyDieReferencingFailed,
		//InternalReferencingFailedFluid,
		//ExternalWhiteReferencingFailedFluid,
		//ExternalEmptyReferencingFailedFluid,
		InternalReferencingFailed,
		ExternalWhiteReferencingFailed,
		ExternalEmptyReferencingFailed,
		
		MeasurementFailed,
		EmptyDieMeasurementFailed,
		
		MisIdentificationFluid,
		MisIdentificationSolid,
		CalculationFailed,
		NotMeasurable,
		
		SavingFailed,
		PrintigFailed,
		LabelPrintigFailed,
		ShowReportFailed,
		
		NoError,
	}
	
	public class AnimationErrorData
	{
		public AnimationErrorData()
		{
			this.Header 	= String.Empty;
			this.Message	= String.Empty;
			this.Pdf		= String.Empty;
			this.Time		= String.Empty;
			this.FatalError	= false;
		}
		
		public string Header	{get; set;}
		public string Message	{get; set;}
		public string Pdf 		{get; set;}
		public bool FatalError	{get; set;}
		public string Time		{get; set;}
		
	}
	
	public class Animation
	{
		
		public Animation (DrawingArea drawing_area, Button button, Alignment alignment, int animation_surface_width)
		{
			this.AnimationImage 	= drawing_area;
			this.InfoButton			= button;
			this.AnimationSurface	= alignment;
			
			this.LastAnimation		= AnimationType.Working;
			this.CurrentAnimation	= AnimationType.Working;
			
			this.AnimationSurface.WidthRequest = animation_surface_width;
			
			int image_width = animation_surface_width - 30;
			if (image_width < 105)
				image_width = 105;
			if (image_width > 400)
				image_width = 400;
			
			int image_height = (int)Math.Round(image_width * 309.0 / 400.0);

			Console.WriteLine("Scaling status icon to: {0} x {1}", image_width, image_height);

			this.AnimationImage.WidthRequest  = image_width;
			this.AnimationImage.HeightRequest = image_height;
			this.AnimationImage.ModifyBg(StateType.Normal, FinderWindow.HIPERSCAN_RED);
			this.AnimationImage.ExposeEvent += (sender, e) => {
				using (Gdk.GC gc = new Gdk.GC(this.AnimationImage.GdkWindow))
				{
					this.AnimationImage.GdkWindow.DrawPixbuf(gc, this.AnimationImages[this.CurrentAnimation].Pixbuf, 
					                                         0, 0, 0, 0, image_width, image_height, 
					                                         Gdk.RgbDither.Normal, 0, 0);
				}
			};

			this.LoadAnimationPics(image_width, image_height);			
			this.InitErrorsDictionary();	
			
			this.InfoButton.Clicked += this.OnInfoButtonClicked;
		}
		
		private void LoadAnimationPics(int width, int height)
		{
			this.AnimationImages = new Dictionary<AnimationType,Image>();
			
			string path = Path.Combine("Resources", "Images");
            path = Locale.ResourceFileRelativeToAssembly(Assembly.GetExecutingAssembly(), path, String.Empty, false);

			//Calibration images
			string file = Path.Combine(path, "internal_calibration.png");
			Gtk.Image internal_calibration = new Gtk.Image(); 
			internal_calibration.Pixbuf = new Gdk.Pixbuf(file).ScaleSimple(width, height, Gdk.InterpType.Bilinear);
			this.AnimationImages.Add(AnimationType.InternalRecalibration, internal_calibration);

			file = Path.Combine(path, "external_calibration.png");
			Gtk.Image external_calibration = new Gtk.Image(); 
			external_calibration.Pixbuf = new Gdk.Pixbuf(file).ScaleSimple(width, height, Gdk.InterpType.Bilinear);
			this.AnimationImages.Add(AnimationType.ExternalRecalibration, external_calibration);
			
			//Working images
			file = Path.Combine(path, "working.png");
			Gtk.Image working = new Gtk.Image(); 
			working.Pixbuf = new Gdk.Pixbuf(file).ScaleSimple(width, height, Gdk.InterpType.Bilinear);
			this.AnimationImages.Add(AnimationType.Working, working);

			file = Path.Combine(path, "measuring.png");
			Gtk.Image measuring = new Gtk.Image(); 
			measuring.Pixbuf = new Gdk.Pixbuf(file).ScaleSimple(width, height, Gdk.InterpType.Bilinear);
			this.AnimationImages.Add(AnimationType.Measuring, measuring);
				
			file = Path.Combine(path, "measuring_empty_die.png");
			Gtk.Image measuring_empty_die = new Gtk.Image(); 
			measuring_empty_die.Pixbuf = new Gdk.Pixbuf(file).ScaleSimple(width, height, Gdk.InterpType.Bilinear);
			this.AnimationImages.Add(AnimationType.MeasuringEmptyDie, measuring_empty_die);
			
			//Waiting images
			file = Path.Combine(path, "ready_for_empty_die.png");
			Gtk.Image ready_for_empty_die = new Gtk.Image(); 
			ready_for_empty_die.Pixbuf = new Gdk.Pixbuf(file).ScaleSimple(width, height, Gdk.InterpType.Bilinear);
			this.AnimationImages.Add(AnimationType.ReadyForEmptyDie, ready_for_empty_die);

			file = Path.Combine(path, "ready_for_input.png");
			Gtk.Image ready_for_input = new Gtk.Image(); 
			ready_for_input.Pixbuf = new Gdk.Pixbuf(file).ScaleSimple(width, height, Gdk.InterpType.Bilinear);
			this.AnimationImages.Add(AnimationType.ReadyForInput, ready_for_input);

			file = Path.Combine(path, "ready_for_measurement.png");
			Gtk.Image ready_for_measurement = new Gtk.Image(); 
			ready_for_measurement.Pixbuf = new Gdk.Pixbuf(file).ScaleSimple(width, height, Gdk.InterpType.Bilinear);
			this.AnimationImages.Add(AnimationType.ReadyForMeasurement, ready_for_measurement);

			file = Path.Combine(path, "ready_for_measurement_attention.png");
			Gtk.Image ready_for_measurement_attention = new Gtk.Image(); 
			ready_for_measurement_attention.Pixbuf = new Gdk.Pixbuf(file).ScaleSimple(width, height, Gdk.InterpType.Bilinear);
			this.AnimationImages.Add(AnimationType.ReadyForMeasurementAttention, ready_for_measurement_attention);

			file = Path.Combine(path, "ready_for_measurement_fluid.png");
			Gtk.Image ready_for_measurement_fluid = new Gtk.Image(); 
			ready_for_measurement_fluid.Pixbuf = new Gdk.Pixbuf(file).ScaleSimple(width, height, Gdk.InterpType.Bilinear);
			this.AnimationImages.Add(AnimationType.ReadyForMeasurementFluid, ready_for_measurement_fluid);

			file = Path.Combine(path, "ready_for_measurement_fluid_attention.png");
			Gtk.Image ready_for_measurement_fluid_attention = new Gtk.Image(); 
			ready_for_measurement_fluid_attention.Pixbuf = new Gdk.Pixbuf(file).ScaleSimple(width, height, Gdk.InterpType.Bilinear);
			this.AnimationImages.Add(AnimationType.ReadyForMeasurementFluidAttention, ready_for_measurement_fluid_attention);

			file = Path.Combine(path, "ready_for_protocol_input.png");
			Gtk.Image ready_for_protocol_input = new Gtk.Image(); 
			ready_for_protocol_input.Pixbuf = new Gdk.Pixbuf(file).ScaleSimple(width, height, Gdk.InterpType.Bilinear);
			this.AnimationImages.Add(AnimationType.ReadyForProtocolInput, ready_for_protocol_input);

			file = Path.Combine(path, "ready_for_protocol_creation.png");
			Gtk.Image ready_for_protocol_creation = new Gtk.Image(); 
			ready_for_protocol_creation.Pixbuf = new Gdk.Pixbuf(file).ScaleSimple(width, height, Gdk.InterpType.Bilinear);
			this.AnimationImages.Add(AnimationType.ReadyForProtocolCreation, ready_for_protocol_creation);

			//save + error image
			file = Path.Combine(path, "saved.png");
			Gtk.Image saved = new Gtk.Image(); 
			saved.Pixbuf = new Gdk.Pixbuf(file).ScaleSimple(width, height, Gdk.InterpType.Bilinear);
			this.AnimationImages.Add(AnimationType.Saved, saved);

			file = Path.Combine(path, "saving_failed.png");
			Gtk.Image saving_failed = new Gtk.Image(); 
			saving_failed.Pixbuf = new Gdk.Pixbuf(file).ScaleSimple(width, height, Gdk.InterpType.Bilinear);
			this.AnimationImages.Add(AnimationType.SavingFailed, saving_failed);
				
			file = Path.Combine(path, "error.png");
			Gtk.Image error = new Gtk.Image(); 
			error.Pixbuf = new Gdk.Pixbuf(file).ScaleSimple(width, height, Gdk.InterpType.Bilinear);
			this.AnimationImages.Add(AnimationType.Error, error);

			file = Path.Combine(path, "not_measurable.png");
			Gtk.Image not_measurable = new Gtk.Image(); 
			not_measurable.Pixbuf = new Gdk.Pixbuf(file).ScaleSimple(width, height, Gdk.InterpType.Bilinear);
			this.AnimationImages.Add(AnimationType.NotMeasurable, not_measurable);
			
			this.CurrentAnimation = AnimationType.Working;
		}

		private void InitErrorsDictionary()
		{
			this.AnimationErrors = new Dictionary<AnimationErrorType, AnimationErrorData>();
			
			AnimationErrorData tmp = new AnimationErrorData()
			{
				Header 		= Catalog.GetString("Device not ready"),
				FatalError	= true,
			};
			this.AnimationErrors.Add( AnimationErrorType.DeviceNotReady, tmp);	
			
			tmp = new AnimationErrorData()
			{
				Header 		= Catalog.GetString("Device Not Initialized"),
				FatalError	= true,
			};
			this.AnimationErrors.Add( AnimationErrorType.DeviceNotInitialized, tmp);

			tmp = new AnimationErrorData()
			{
				Header 		= Catalog.GetString("Device Warmup Failed"),
				FatalError	= true,
			};
			this.AnimationErrors.Add( AnimationErrorType.DeviceWarmupFailed, tmp);
			
			tmp = new AnimationErrorData()
			{
				Header 		= Catalog.GetString("Ident Server Not Initialized"),
				FatalError	= true,
			};
			this.AnimationErrors.Add( AnimationErrorType.IdentServerNotInitialized, tmp);			
			
			tmp = new AnimationErrorData()
			{
				Header 		= Catalog.GetString("Ident Server Cannot Load Models"),
				FatalError	= true,
			};
			this.AnimationErrors.Add( AnimationErrorType.IdentServerLoadModelsFailed, tmp);	
			
			tmp = new AnimationErrorData()
			{
				Header 		= Catalog.GetString("No Models Available"),
				FatalError	= true,
			};
			this.AnimationErrors.Add( AnimationErrorType.IdentServerNoModelsAvailable, tmp);

			tmp = new AnimationErrorData()
			{
				Header 		= Catalog.GetString("Not Released Ident Module"),
				FatalError	= true,
			};
			this.AnimationErrors.Add( AnimationErrorType.NonReleasedIdentModule, tmp);

			tmp = new AnimationErrorData()
			{
				Header 		= Catalog.GetString("No Connection To Ident Server"),
				FatalError	= true,
			};
			this.AnimationErrors.Add( AnimationErrorType.ConnectionLost, tmp);
			
			tmp = new AnimationErrorData()
			{
				Header 		= Catalog.GetString("Empty Die Calibration Failed"),
				FatalError	= false,
				Pdf			= this.Url + "error_transflectance_reference.pdf",
			};
			this.AnimationErrors.Add( AnimationErrorType.EmptyDieReferencingFailed, tmp);	

			tmp = new AnimationErrorData()
			{
				Header 		= Catalog.GetString("Empty Die Measurement Failed"),
				FatalError	= false,
			};
			this.AnimationErrors.Add( AnimationErrorType.EmptyDieMeasurementFailed, tmp);
			
			tmp = new AnimationErrorData()
			{
				Header 		= Catalog.GetString("Internal Calibration Failed"),
				FatalError	= false,
			};			
			this.AnimationErrors.Add( AnimationErrorType.InternalReferencingFailed, tmp);		
			
			tmp = new AnimationErrorData()
			{
				Header 		= Catalog.GetString("External Empty Calibration Failed"),
				FatalError	= false,
				Pdf			= this.Url + "error_black_reference.pdf",
			};	
			this.AnimationErrors.Add( AnimationErrorType.ExternalEmptyReferencingFailed, tmp);	
			
			tmp = new AnimationErrorData()
			{
				Header 		= Catalog.GetString("External White Calibration Failed"),
				FatalError	= false,
				Pdf			= this.Url + "error_white_reference.pdf",
			};	
			this.AnimationErrors.Add( AnimationErrorType.ExternalWhiteReferencingFailed, tmp);	
			
			tmp = new AnimationErrorData()
			{
				Header 		= Catalog.GetString("Measurement Failed"),
				FatalError	= false,
			};
			this.AnimationErrors.Add( AnimationErrorType.MeasurementFailed, tmp);
			
			tmp = new AnimationErrorData()
			{
				Header 		= Catalog.GetString("Saving Failed"),
				FatalError	= false,
			};
			this.AnimationErrors.Add( AnimationErrorType.SavingFailed, tmp);	
			
			tmp = new AnimationErrorData()
			{
				Header 		= Catalog.GetString("Printing Failed"),
				FatalError	= false,
			};
			this.AnimationErrors.Add( AnimationErrorType.PrintigFailed, tmp);	
			
			tmp = new AnimationErrorData()
			{
				Header 		= Catalog.GetString("Printing Label Failed"),
				FatalError	= false,
			};
			this.AnimationErrors.Add( AnimationErrorType.LabelPrintigFailed, tmp);	
			
			tmp = new AnimationErrorData()
			{
				Header 		= Catalog.GetString("Showing Report Failed"),
				FatalError	= false,
			};
			this.AnimationErrors.Add( AnimationErrorType.ShowReportFailed, tmp);
			
			tmp = new AnimationErrorData()
			{
				Header 		= Catalog.GetString("Identification Failed"),
				FatalError	= false,
				Pdf			= this.Url + "misidentification_liquids.pdf",
			};
			this.AnimationErrors.Add( AnimationErrorType.MisIdentificationFluid, tmp);
			
			tmp = new AnimationErrorData()
			{
				Header 		= Catalog.GetString("Identification Failed"),
				FatalError	= false,
				Pdf			= this.Url + "misidentification_solids.pdf",
			};
			this.AnimationErrors.Add( AnimationErrorType.MisIdentificationSolid, tmp);
		}
		
		public void SetAnimation(AnimationType animation_pic)
		{
			this.SetAnimation(animation_pic, false);
		}
		
		public void SetAnimation(AnimationType animation_pic, bool push_old_animation)
		{
            Console.WriteLine("SetAnimation: {0}", animation_pic);
			if (animation_pic == AnimationType.None)
			{
				this.CurrentAnimation = this.LastAnimation;
			}
			else
			{
				this.CurrentAnimation = animation_pic;
				if (push_old_animation == true)
					this.LastAnimation = this.CurrentAnimation ;				
			}	
		}
			
		
		
		
		
		public void SetError(AnimationErrorType type, string message)
		{
			if (type != AnimationErrorType.NoError)
			{
				bool set_error = false;
				if (this.LastError == AnimationErrorType.NoError)
					set_error = true;
				
				if (set_error == false)
				{
					if (this.AnimationErrors[this.LastError].FatalError == false)
						set_error = true;
				}
				
				if (set_error == false)
				{
					if (this.AnimationErrors[this.LastError].FatalError == true && this.AnimationErrors[this.LastError].FatalError == true)
						set_error = true;
				}
					
				if (set_error == true)
				{
					this.LastError = type;
					if (String.IsNullOrEmpty(message) == false)
					{
						this.AnimationErrors[type].Time		= this.ErrorTime;
						this.AnimationErrors[type].Message 	= this.AnimationErrors[type].Time + "\n\n" + message;
					}
				}  
				
				
				this.InfoButton.Visible = true;
				this.InfoButton.TooltipText  = String.Format(Catalog.GetString("Last message at {0} "), this.AnimationErrors[type].Time);
			}			
		}

		
		private void SetError(AnimationErrorType type)
		{
			this.SetError(type, String.Empty);
		}
		
		public void ResetAllErrors()
		{
			this.IsMisIdentficationSolid 	= false;
			this.IsMisIdentficationFLuid	= false;
			this.LastError					= AnimationErrorType.NoError;
			this.InfoButton.Visible 		= false;
		}
		
		public void ResetErrorsWithoutMisIdentification()
		{
			if (this.IsMisIdentficationSolid == false && this.IsMisIdentficationFLuid == false)
			{
				this.LastError			= AnimationErrorType.NoError;
				this.InfoButton.Visible = false;
			}

			if (this.IsMisIdentficationSolid == true)
				this.LastError = AnimationErrorType.MisIdentificationSolid;
			
			if (this.IsMisIdentficationFLuid == true)
				this.LastError = AnimationErrorType.MisIdentificationFluid;
			
			if (this.LastError != AnimationErrorType.NoError)
				this.SetError(this.LastError);
		}
		
		private void OnInfoButtonClicked(object sender, EventArgs e)
		{
			if (this.LastError == AnimationErrorType.NoError)
				return;
			
			AnimationErrorData tmp = this.AnimationErrors[this.LastError];
			if (String.IsNullOrEmpty(tmp.Pdf) == false)
			{
				HToolTip.ShowToolTipAsDialogWithLink(this.InfoButton,
				                        	tmp.Header,
				                    		tmp.Message,			           
				                             Stock.DialogInfo,
											 Catalog.GetString("More information ..."),
											 this.ShowHelp);
			}
			else
			{
				HToolTip.ShowToolTipAsDialog(this.InfoButton,
				                        	tmp.Header,
				                    		tmp.Message,			           
				                             Stock.DialogInfo);
			}
		}
		
		private void ShowHelp(object sender, EventArgs e)
		{
			try 
			{
				Console.WriteLine(this.AnimationErrors[this.LastError].Pdf);
				System.Diagnostics.Process.Start(this.AnimationErrors[this.LastError].Pdf);
			} 
			catch (Exception) {}
			
		}
		
		private string ErrorTime
		{
			get
			{
				return DateTime.Now.ToString("HH:mm:ss");
			}
		}

		private string Url
		{
			get
			{
				string path = Path.Combine("Resources", "Pdfs");
                path = Locale.ResourceFileRelativeToAssembly(Assembly.GetExecutingAssembly(), path, String.Empty, false);

				string url = path.Replace("\\", "/");
				if (url.EndsWith("/") == false)
					url += "/";
	
				url = "file://" + url;

				return url;
			}				
		}
		
		private Dictionary<AnimationType, Gtk.Image> AnimationImages				{get; set;}
		private Dictionary<AnimationErrorType, AnimationErrorData> AnimationErrors	{get; set;}
		
		private Gtk.DrawingArea AnimationImage	{get; set;}
		private Gtk.Button InfoButton			{get; set;}
		private Gtk.Alignment AnimationSurface	{get; set;}
		
		private AnimationType LastAnimation		{get; set;} 
		private AnimationType CurrentAnimation	{get; set;}
		
		public bool IsMisIdentficationSolid		{private get; set;}
		public bool IsMisIdentficationFLuid		{private get; set;}
		private AnimationErrorType LastError	{get; set;}	
	}
}

