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

using Glade;

using Gtk;

using Hiperscan.Common.IO;
using Hiperscan.Common.LogWriter;
using Hiperscan.Unix;
using Hiperscan.ApoIdentCore;
using Hiperscan.ApoIdentCommon.ApoConfig;
using Hiperscan.AnalysisModule;

namespace Hiperscan.Extensions.ApoIdent
{
    public partial class DataBackupDialog
    {
        public enum ExportMode
        {
            Backup,
            OnlyLogFiles,
            CustomerSupport,
        }

        [Widget] Gtk.FileChooserDialog backup_filechooserdialog = null;
        [Widget] Gtk.RadioButton backup_radiobutton             = null;
        [Widget] Gtk.RadioButton support_radiobutton            = null;
        [Widget] Gtk.Label day_label                            = null;
        [Widget] Gtk.SpinButton day_spinbutton                  = null;

        public static DataBackupDialog Start(AppControl control, Gtk.Window window)
        {
            return new DataBackupDialog(control, window);
        }

        private DataBackupDialog(AppControl control, Gtk.Window window)
        {
            this.Control = control;
            //BACKUP
            this.Control.ProgramSync.EnterCriticialZone();

            ResponseType response = ResponseType.None;
            string file_name = String.Empty;

            this.Init(window);

            this.backup_filechooserdialog.IconList = ApoIdentStock.FinderIcons;

            GuiDesigner.SetSchema(this.backup_filechooserdialog);
            response = (ResponseType)this.backup_filechooserdialog.Run();
            file_name = this.backup_filechooserdialog.Filename;
            this.backup_filechooserdialog.Destroy();

            control.ProgramSync.LeaveCriticalZone();


            if (response == ResponseType.Accept)
            {
                System.Threading.Tasks.Task.Run(() =>
                {
                    this.Control.ProgramSync.EnterCriticialZone();
                    this.SaveData(file_name);
                    this.Control.ProgramSync.LeaveCriticalZone();
                });
            }
        }

        private void Init(Gtk.Window parent)
        {
            Glade.XML gui = new Glade.XML(null, "Backup.glade", "backup_filechooserdialog", "Hiperscan");
            gui.Autoconnect(this);

            ApoIdent.ApoIdentApplication.SetWmcClassName(this.backup_filechooserdialog);

            this.backup_filechooserdialog.TransientFor = parent;
           
            this.support_radiobutton.Group  = this.backup_radiobutton.Group;
            this.backup_radiobutton.Active = true;
            this.day_spinbutton.Value = -1;
            this.OnDataExportOptionToggled(this.backup_radiobutton, EventArgs.Empty);

            this.day_spinbutton.Changed += this.OnDaySpinbuttonValueChanged;
            this.day_spinbutton.FocusOutEvent += this.OnDaySpinbuttonFocusedOut;

            FileFilter filter = new FileFilter();
            filter.Name = Catalog.GetString("Archive");
            filter.AddPattern("*.zip");

            this.backup_filechooserdialog.AddFilter(filter);

            List<Gtk.ComboBox> boxes = new List<ComboBox>();
            this.FindComboBox(this.backup_filechooserdialog, boxes);
            boxes[0].Visible = false;

            this.backup_filechooserdialog.SetCurrentFolder(this.Control.FileSystem.DefaultDataExportDir);
            this.backup_filechooserdialog.DoOverwriteConfirmation = true;
        }

        private void FindComboBox(Gtk.Widget widget, List<ComboBox> boxes)
        {
            if (widget is Gtk.ComboBox)
            {
                boxes.Add(widget as Gtk.ComboBox);
                return;
            }

            Container container = widget as Gtk.Container;
            if (container == null)
                return;

            foreach (Widget child in container.Children)
                this.FindComboBox(child, boxes);
        }

        void OnDataExportOptionToggled(object sender, EventArgs e)
        {
            if (sender == this.support_radiobutton)
            {
                this.day_label.Sensitive        = true;
                this.day_spinbutton.Sensitive   = true;
                this.Mode                       = ExportMode.CustomerSupport;

                if (this.Days == 0)
                    this.Mode = ExportMode.OnlyLogFiles;
            }
            else
            {
                this.day_label.Sensitive        = false;
                this.day_spinbutton.Sensitive   = false;              
                this.Mode = ExportMode.Backup;
            }

            this.SetFileName();
            this.SetSpinButtonToolTip();
        } //

        private void OnDaySpinbuttonValueChanged(object sender, EventArgs  e)
        {
            try
            {
                this.Days = Int32.Parse(this.day_spinbutton.Text, System.Globalization.NumberStyles.AllowLeadingSign);
                if (this.Days < -1)
                    this.Days = -1;
            }
            catch (Exception) { }

            if (this.support_radiobutton.Active == true)
            {
                if(this.Days == 0)
                    this.Mode = ExportMode.OnlyLogFiles;
                else
                    this.Mode = ExportMode.CustomerSupport;
            }
            this.SetFileName();
            this.SetSpinButtonToolTip();
        }

        private void OnDaySpinbuttonFocusedOut(object sender, EventArgs e)
        {
            //this.day_spinbutton.Value = this.Days;
        }

        private void SetSpinButtonToolTip()
        {
            switch (this.Days)
            {
                case -1:
                    this.day_spinbutton.TooltipText =
                            Catalog.GetString("All data will be exported.");
                    break;

                case 0:
                    this.day_spinbutton.TooltipText =
                            Catalog.GetString("Only logfiles will be exported.");
                    break;

                default:
                    this.day_spinbutton.TooltipText = String.Format(
                            Catalog.GetPluralString("Data of the last day will be exported.",
                                                    "Data of the last {0} days will be exported.",
                                                    this.Days), 
                                                    this.Days);
                    break;
            }
        }

        private void SetFileName()
        {
            string name = String.Empty;

            switch(this.Mode)
            {
                case ExportMode.Backup:
                    name = "apo_ident_backup_";
                    break;
               
                case ExportMode.OnlyLogFiles:
                    name = "apo_ident_log_files_";
                    break;

                case ExportMode.CustomerSupport:
                    name = "apo_ident_service_";
                    break;

                default:
                    break;
            }

            this.backup_filechooserdialog.CurrentName = name + DateTime.Now.ToString("yyyy-MM-dd_HH-mm") + ".zip";
        }


        private void ZipConfiguration(Zip zip)
        {
            zip.AddToArchiveCondition = this.ZipCompleteConfigCondition;
            zip.Add(this.Control.FileSystem.ConfigDir, Catalog.GetString(".Configurations"));
        }

        private void ZipLogData(Zip zip)
        {
            zip.AddToArchiveCondition = null;
            if (FileIO.ExistsFile(this.Control.FileSystem.ConfigFile))
                zip.Add(this.Control.FileSystem.ConfigFile);

            this.ZipLogFiles(zip, false);
            this.ZipLogFiles(zip, true);
        }

        private void ZipLogFiles(Zip zip, bool remoting)
        {
            string tmp_dir = Path.Combine(Path.GetTempPath(), Path.GetRandomFileName());

            try
            {
                FileIO.CreateDirectory(tmp_dir);

                string zip_file = Path.Combine(tmp_dir, "data.zip");

                if (remoting == false)
                {
                    LogWriter.Current.ZipLogFiles(zip_file, true);
                }
                else
                {
                    this.ZipRemoteFiles(zip_file);
                }

                if (FileIO.ExistsFile(zip_file))
                {
                    Zip.Decompress(zip_file, tmp_dir);
                    System.Threading.Thread.Sleep(100);

                    foreach (string file in FileIO.GetFiles(tmp_dir))
                    {
                        if (Path.GetExtension(file).ToLower() == ".zip")
                            continue;

                        zip.Add(file);
                    }
                }
            }
            catch(Exception ex)
            {
                Console.WriteLine("Backup:  Could not zip log files: {0}", ex);
            }
            finally
            {
                try
                {
                    FileIO.DeleteDirectory(tmp_dir, true);
                }
                catch (Exception) { }
            }
        }

        private void ZipSpectraArchives(Zip zip)
        {
            Dictionary<string, string> spectra_folder = new Dictionary<string, string>();

            zip.AddToArchiveCondition = this.ZipSpectraCondition;

            foreach (ProfileSettings profile in this.Control.ProfileManager.AllProfiles.Values)
                spectra_folder.Add(profile.Name, profile.Archive); 

            HashSet<string> compressed_items = new HashSet<string>();

            foreach(string profile_name in spectra_folder.Keys)
            {
                if (compressed_items.Add(spectra_folder[profile_name]) == false)
                    continue;

                string dir = spectra_folder[profile_name];
                if (false == FileIO.ExistsDirectory(dir))
                    continue;

                string zip_target = "_" + FileIOHelper.ConvertToValidFilename(profile_name);
                
                foreach (string sub_dir in FileIO.GetDirectories(dir))
                {
                    string sub_target = Path.Combine(zip_target, Path.GetFileName(sub_dir));
                    zip.Add(sub_dir, sub_target);
                }
                    

                foreach (string file in FileIO.GetFiles(dir))
                {
                    string sub_target = Path.Combine(zip_target, Path.GetFileName(file));
                    zip.Add(file, sub_target);
                }
                   
            }
        }

        private void GetZipSpectraArchivesFileCount()
        {
            Dictionary<string, string> spectra_folder = new Dictionary<string, string>();

            foreach (ProfileSettings profile in this.Control.ProfileManager.AllProfiles.Values)
                spectra_folder.Add(profile.Name, profile.Archive);

            HashSet<string> compressed_items = new HashSet<string>();

            foreach (string profile_name in spectra_folder.Keys)
            {
                if (compressed_items.Add(spectra_folder[profile_name]) == false)
                    continue;

                string dir = spectra_folder[profile_name];
                if (false == FileIO.ExistsDirectory(dir))
                    continue;

                foreach (string file in FileIO.GetFiles(dir, "*", SearchOption.AllDirectories))
                {
                    if (this.ZipSpectraCondition(file) == true)
                        this.FileCount++;
                }
            }

            System.Threading.Thread.Sleep(500);
        }

        private bool ZipSpectraCondition(string file)
        {
            bool is_time_stamp_file = true;

            if (Path.GetExtension(file).ToLower() != ".pdf" && 
                Path.GetExtension(file).ToLower() != ".csv" &&
                Path.GetExtension(file).ToLower() != ".xml")
                is_time_stamp_file = false;

            Match match = null;
            if (is_time_stamp_file == true)
            {
                Regex regex = new Regex("[0-9][0-9][0-9][0-9]-[0-9][0-9]-[0-9][0-9]_[0-9][0-9]-[0-9][0-9]-[0-9][0-9]");
                match = regex.Match(Path.GetFileName(file));
                is_time_stamp_file = match.Success;
            }

            if (is_time_stamp_file == true)
            { 
                int day = this.support_radiobutton.Active == true ? this.Days : -1;

                DateTime limit = DateTime.MinValue;
                if (day != -1)
                {
                    limit = DateTime.Now;
                    limit = new DateTime(limit.Year, limit.Month, limit.Day).AddDays(-day);
                }

                DateTime spectrum = DateTime.ParseExact(match.Value, "yyyy-MM-dd_HH-mm-ss", null);

                if (spectrum < limit)
                    return false;
            }

            return true;
        }

        private bool ZipCompleteConfigCondition(string file)
        {
            if (Path.GetExtension(file).ToLower() == ".txt")
            {
                Console.WriteLine(file);
                return false;
            }

            return true;
        }

        private void ZipRemoteFiles(string zip_file)
        {
            try
            {
                string dir = Path.GetDirectoryName(zip_file);
                var zip = new Zip(zip_file, true);

                List<AnalysisType> available_servers = new List<AnalysisType>();

                if (this.Control.GlobalStates.IsIdentModuleAvailable)
                    available_servers.Add(AnalysisType.Identification);

                if (this.Control.GlobalStates.IsQuantifierModuleAvailable)
                    available_servers.Add(AnalysisType.Quantification);

                foreach(AnalysisType analysis_type in available_servers)
                {
                    try
                    {
                        Dictionary<string, byte[]> log_files = this.Control.SubstanceManager.GetLogFiles(analysis_type);

                        foreach (string file in log_files.Keys)
                        {
                            byte[] stream = log_files[file];
                            if (stream != null)
                            {
                                string destination = Path.Combine(dir, file);
                                stream.ToFile(destination);
                                zip.Add(new FileInfo(destination));
                            }
                        }
                    }
#pragma warning disable RECS0022 // catch-Klausel, die System.Exception abfängt und keinen Text aufweist
                    catch { }
#pragma warning restore RECS0022 // catch-Klausel, die System.Exception abfängt und keinen Text aufweist
                }

                zip.Finish();
            }
#pragma warning disable RECS0022 // catch-Klausel, die System.Exception abfängt und keinen Text aufweist
            catch { }
#pragma warning restore RECS0022 // catch-Klausel, die System.Exception abfängt und keinen Text aufweist
        }

       
        private AppControl Control { get; set; }
        public int FileCount { get; private set; } = 0;
        public int FileProgressCount { get; private  set; } = 0;
        private int Days { get; set; } = -1;
        public ExportMode Mode { get; private set; }
    }
}
