//
//  SampleIdWindow.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.IO;
using System.Text.RegularExpressions;
using System.Collections.Generic;
using Glade;
using Gtk;
using Hiperscan.Common;
using Hiperscan.Spectroscopy;
using Hiperscan.Spectroscopy.IO;
using Hiperscan.Unix;
using Catalog = Mono.Unix.Catalog;

namespace Hiperscan.Extensions.Acquisition
{

	public class SampleIdWindow
	{
		[Widget] Gtk.Window sample_id_window = null;
		[Widget] Gtk.Entry sample_id_entry = null;
		[Widget] Gtk.Entry series_id_entry = null;

		[Widget] Gtk.Label file_name_label = null;

		[Widget] Gtk.Button ok_button1 = null;

        private static string series_id_cache = string.Empty;
        private static string serial_cache = string.Empty;

		private static Dictionary<string, string> saved_spectra = new Dictionary<string, string>();
		private static readonly string id_postfix = Environment.GetEnvironmentVariable(Env.Plugin.ACQUISITION_ID_POSTFIX);

		public SampleIdWindow()
		{
			Glade.XML gui = new Glade.XML(null, "Hiperscan.Extensions.Acquisition.Acquisition.glade", "sample_id_window", "Hiperscan");
			gui.Autoconnect(this);

			this.sample_id_entry.Changed += this.UpdateOkButton;
			this.series_id_entry.Changed += this.UpdateOkButton;

            this.series_id_entry.Text = SampleIdWindow.series_id_cache;

            this.sample_id_window.TransientFor = Plugin.host.MainWindow;
			this.sample_id_window.ShowAll();
		}

		internal void OnOkButtonClicked(object sender, EventArgs args)
		{
            bool saving_success = false;
            bool skipped = false;
            bool saved = false;
            string raw_data_path = String.Empty;

            try
			{
				int ix = 0;
				int ix_si = 0;
				SpectrumType last_spectrum_type = SpectrumType.Unknown;

				bool auto_increase_number = false;
				bool si_spectra = false;
    
                string sample_regex = String.Empty;

				foreach (Spectrum spectrum in Plugin.host.CurrentSpectra.Values)
				{
					if (spectrum.IsFromFile)
						continue;

					if (spectrum.SpectrumType != SpectrumType.SolidSpecimen &&
					    spectrum.SpectrumType != SpectrumType.FluidSpecimen)
					{
						skipped = true;
						continue;
					}

					if (last_spectrum_type != SpectrumType.Unknown &&
					    spectrum.SpectrumType != last_spectrum_type)
						throw new Exception(Catalog.GetString("Cannot save solids and liquids spectra at the same time."));

					string path = Path.Combine(Handlers.GetSavePath(), "Rohdaten");
                    raw_data_path = Handlers.GetSavePath();

                    string si_path, fname;
					if (spectrum.ProbeType == ProbeType.FinderInsert || spectrum.ProbeType == ProbeType.FinderSDInsert)
					{
						si_spectra = true;

                        if (Handlers.GetSavePath() == ".QuickStep")
                        {
                            si_path = Path.Combine(Handlers.GetSavePath(), "Rohdaten_SI");
                        }
                        else
                        {
                            si_path = Path.Combine(Handlers.GetSavePath() + "_SI", "Rohdaten_SI");
                            raw_data_path = Handlers.GetSavePath() + "_SI";
                        }

						fname = $"{this.sample_id_entry.Text}SI_{this.series_id_entry.Text}_{(++ix_si).ToString()}.csv";
                    }
					else if (string.IsNullOrEmpty(id_postfix) == false)
					{
						si_path = null;
						fname = $"{this.sample_id_entry.Text}_{this.series_id_entry.Text}{id_postfix}_{(++ix_si).ToString()}.csv";
					}
					else
					{
						si_path = null;
						fname = $"{this.sample_id_entry.Text}_{this.series_id_entry.Text}_{(++ix).ToString()}.csv";
					}

					string save_path;
					if (string.IsNullOrEmpty(si_path))
					{
						FileIO.CreateDirectory(path);
						save_path = Path.Combine(path, fname);
					}
					else
					{
						FileIO.CreateDirectory(si_path);
						save_path = Path.Combine(si_path, fname);
					}

					if (SampleIdWindow.saved_spectra.ContainsKey(spectrum.Id))
					{
						string msg = string.Format(Catalog.GetString("Spectrum '{0}' has already been saved under the name '{1}'."),
									   spectrum.Label, SampleIdWindow.saved_spectra[spectrum.Id]);
						throw new Exception(msg);
					}

					if (FileIO.ExistsFile(save_path))
					{
						if (auto_increase_number == false)
						{
							MessageDialog dialog = new MessageDialog(this.sample_id_window,
												 DialogFlags.Modal,
												 MessageType.Question,
												 ButtonsType.YesNo,
												 Catalog.GetString("A spectrum with name '{0}' already exists. Do you want to save with increased spectrum number anyway?"),
												 fname);

							ResponseType response = (ResponseType)dialog.Run();
							dialog.Destroy();

							auto_increase_number = response == ResponseType.Yes;
						}

						if (auto_increase_number)
						{
							while (FileIO.ExistsFile(save_path))
							{
								int spectrum_ix = int.Parse(Regex.Match(save_path, @"[0-9]*\.csv$").ToString().Replace(".csv", string.Empty));
								save_path = save_path.Replace(spectrum_ix + ".csv", ++spectrum_ix + ".csv");
							}
						}
						else
						{
							throw new Exception(Catalog.GetString("The operation has been aborted by user."));
						}
					}

                    CSV.Write(spectrum, save_path);
					SampleIdWindow.saved_spectra.Add(spectrum.Id, fname);

					saved = true;
                    SampleIdWindow.series_id_cache = this.series_id_entry.Text;
                }

                if (saved)
                {
                    Plugin.TestProcess.RawDateDir = raw_data_path;
                    Plugin.TestProcess.SequenceNumber = $"{this.sample_id_entry.Text}SI_{this.series_id_entry.Text}";
                }


                if (skipped)
					Plugin.host.ShowInfoMessage(this.sample_id_window,
								    Catalog.GetString("Not all shown spectra have been saved."));

				if (saved == false)
					Plugin.host.ShowInfoMessage(this.sample_id_window,
								    Catalog.GetString("Not all spectra have been saved."));

				if (skipped == false && saved)
					Plugin.host.ClearPlot();

				if (si_spectra && saved)
				{
					string src_dir = Path.Combine(Handlers.GetSavePath(), SpectrumDirNames.REFERENCING);
					string dest_dir = Path.Combine($"{Handlers.GetSavePath()}_SI", SpectrumDirNames.REFERENCING);
					SampleIdWindow.CopyDirectory(src_dir, dest_dir);
				}
                saving_success = true;
			}
			catch (Exception ex)
			{
				Plugin.host.ShowInfoMessage(this.sample_id_window,
							    Catalog.GetString("Cannot save sample spectra: {0}"),
							    ex.Message);
				Console.WriteLine($"Cannot save sample spectra: {ex}");
			}
			finally
			{
				this.sample_id_window.Destroy();
                if (saving_success && saved && Settings.Current.TestProcess)
                    System.Threading.Tasks.Task.Run(() => Plugin.TestProcess.ProcessSpectra());
			}
		}

		internal void OnCancelButtonClicked(object sender, EventArgs args)
		{
			this.sample_id_window.Destroy();
		}

		private void UpdateOkButton(object sender, EventArgs args)
		{
			if (string.IsNullOrEmpty(Handlers.GetSavePath()) == false &&
			    Regex.IsMatch(this.sample_id_entry.Text, @"^[0-9]{5}$") &&
			    Regex.IsMatch(this.series_id_entry.Text, @"^[A-Z]{2}$"))
			{
				this.file_name_label.Text = $"{this.sample_id_entry.Text}(SI)_{this.series_id_entry.Text}_1.csv";
				this.ok_button1.Sensitive = true;
			}
			else
			{
				this.file_name_label.Text = Catalog.GetString("invalid");
				this.ok_button1.Sensitive = false;
			}
		}

		private static void CopyDirectory(string src_dir, string dest_dir)
		{
			DirectoryInfo dir = new DirectoryInfo(src_dir);

			if (dir.Exists == false)
				throw new DirectoryNotFoundException(string.Format(Catalog.GetString("Source directory does not exist or could not be found: {0}"),
										   src_dir));

			DirectoryInfo[] dirs = dir.GetDirectories();

			FileIO.CreateDirectory(dest_dir);

			FileInfo[] files = dir.GetFiles();
			foreach (FileInfo file in files)
			{
				string temp_path = Path.Combine(dest_dir, file.Name);
				if (false == FileIO.ExistsFile(temp_path))
					file.CopyTo(temp_path, false);
			}

			foreach (DirectoryInfo subdir in dirs)
			{
				string temp_path = Path.Combine(dest_dir, subdir.Name);
				CopyDirectory(subdir.FullName, temp_path);
			}
		}

		public static string SerialNumber
		{
			set
			{
				if (value == SampleIdWindow.serial_cache)
					return;

                SampleIdWindow.series_id_cache = string.Empty;
                SampleIdWindow.serial_cache = value;
			}
		}
	}
}
