//
//  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 Mono.Unix;

using Hiperscan.Common;
using Hiperscan.Spectroscopy;
using Hiperscan.Spectroscopy.IO;


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, "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)
        {
            try
            {
                bool skipped = false;
                bool saved   = false;

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

                bool auto_increase_number = false;
                bool si_spectra = false;

                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 mix solid and fluid spectra."));

                    string path = Path.Combine(Handlers.GetSavePath(), "Rohdaten");
                    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");

                        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))
                    {
                        if (Directory.Exists(path) == false)
                            Directory.CreateDirectory(path);
                        save_path = Path.Combine(path, fname);
                    }
                    else
                    {
                        if (Directory.Exists(si_path) == false)
                            Directory.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 filename '{1}'."),
                                                   spectrum.Label, SampleIdWindow.saved_spectra[spectrum.Id]);
                        throw new Exception(msg);
                    }

                    if (File.Exists(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 (File.Exists(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 (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("No spectra have been saved."));

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

                if (si_spectra && saved)
                {
                    string src_dir  = Path.Combine(Handlers.GetSavePath(), Catalog.GetString("Referencing"));
                    string dest_dir = Path.Combine($"{Handlers.GetSavePath()}_SI", Catalog.GetString("Referencing"));
                    SampleIdWindow.CopyDirectory(src_dir, dest_dir);
                }
                    
            }
            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();
            }
        }

        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();

            if (Directory.Exists(dest_dir) == false)
                Directory.CreateDirectory(dest_dir);

            FileInfo[] files = dir.GetFiles();
            foreach (FileInfo file in files)
            {
                string temp_path = Path.Combine(dest_dir, file.Name);
                if (File.Exists(temp_path) == false)
                    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;
            }
        }
    }
}