// Created with MonoDevelop
//
//    QuickStep: Acquire, view and identify spectra 
//    Copyright (C) 2021 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.Globalization;
using System.IO;
using System.Reflection;
using Hiperscan.Common.I18n;
using Hiperscan.Extensions.ApoIdentCommon;

namespace Hiperscan.Extensions.ApoIdent
{
    public class SoftwareUpgradeWindows
    {
        public const string PROGRAM_NAME = "ApoIdentSoftwareUpgrader";


        public SoftwareUpgradeWindows(bool use_upgrader, int delay_in_days)
        {
            this.UseUpgrader = use_upgrader;
            this.DelayInDays = delay_in_days;

            try
            {
                System.Diagnostics.Process[] processes = System.Diagnostics.Process.GetProcessesByName(SoftwareUpgradeWindows.PROGRAM_NAME);
                if (processes.Length > 0)
                    this.IsProgramRuning =  true;
            }
            catch (Exception)
            {
            }

            try
            {
                string file = Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location);
                file = Path.Combine(file, SoftwareUpgradeWindows.PROGRAM_NAME + ".exe");

                Version new_version = AssemblyName.GetAssemblyName(file).Version;
                new_version = new Version(new_version.Major, new_version.Minor, new_version.Build);



                Version old_version = new Version(0, 0, 0);
                if (File.Exists(this.ProgramFileExe))
                {
                    old_version = AssemblyName.GetAssemblyName(this.ProgramFileExe).Version;
                    old_version = new Version(old_version.Major, old_version.Minor, old_version.Build);
                }

                Console.WriteLine("Software upgrade installed version; {0}", new_version);
                Console.WriteLine("Software upgrade used version;      {0}", old_version);


                if (new_version > old_version)
                    this.Copy();
            }
            catch(Exception)
            {

            }

            Console.WriteLine("Initialize software upgrader ...");
            Console.WriteLine("Installed file: {0}", this.InstalledFileExe);
            Console.WriteLine("HCL directory:  {0}", this.HclDir);
            Console.WriteLine("Copy file:      {0}", this.ProgramFileExe);
            Console.WriteLine("Copy success:   {0}", this.CopySuccess);
            Console.WriteLine("Is used:        {0}", this.UseUpgrader);
            Console.WriteLine("Is running:     {0}", this.IsProgramRuning);
            Console.WriteLine("Is executable:  {0}", this.CanRunUpgrader);
            Console.WriteLine("Delay in days:  {0}", this.DelayInDays);
        }

         private void Copy()
        {
            if (this.CanRunUpgrader == false)
                return;

            try
            {
                Console.WriteLine("Copy software upgrader...");
                string destination_file;

                if (Directory.Exists(this.ProgramDir))
                    Directory.Delete(this.ProgramDir, true);

                Directory.CreateDirectory(this.ProgramDir);
                new DirectoryInfo(Path.GetDirectoryName(this.ProgramDir)).Attributes = FileAttributes.Hidden;

                File.Copy(this.InstalledFileExe, this.ProgramFileExe);


                foreach (string file in Directory.GetFiles(this.HclDir, "*.dll"))
                {
                    destination_file = Path.Combine(this.ProgramDir, Path.GetFileName(file));
                    File.Copy(file, destination_file);
                }

                destination_file = Path.Combine(this.ProgramDir, Path.GetFileName(this.ApoIdentCommonFile));
                if (File.Exists(destination_file) == false)
                    File.Copy(this.ApoIdentCommonFile, destination_file);

                this.CopySuccess = true;
            }
            catch(Exception ex)
            {
                Console.WriteLine("Software upgrader cannot copy files: {0}", ex);
                this.CopySuccess = false;
            }
        }

        public bool LaunchForUpgrades()
        {
            if (this.CanRunUpgrader == false)
                return false;

            if (this.UseUpgrader == false)
                return false;

            try
            {
                Console.WriteLine("Launch for installable upgrades");
                string args = "--show-available-upgrades";

                this.CreateProcess(args, true, false, 5000);

                string[] lines = this.ProcessOutput.Split(new char[] { '\n', '\r' }, StringSplitOptions.RemoveEmptyEntries);

                bool upgrades = false;

                foreach (string line in lines)
                {
                    if (line.Contains(DefaultNames.SoftwareUpgrader.QUICKSTEP))
                    {
                        if (line .Contains(DefaultNames.SoftwareUpgrader.UPGRADE_AVAILABLE))
                        {

                            string[] tmp = line.Split(DefaultNames.SoftwareUpgrader.SPLITTER, StringSplitOptions.RemoveEmptyEntries);
                            this.AvailableQuickStepUpgrade = tmp[tmp.Length - 1];
                            upgrades = true;
                        }
                    }

                    if (line.Contains(DefaultNames.SoftwareUpgrader.IDENTMODULE))
                    {
                        if (line.Contains(DefaultNames.SoftwareUpgrader.UPGRADE_AVAILABLE))
                        {

                            string[] tmp = line.Split(DefaultNames.SoftwareUpgrader.SPLITTER, StringSplitOptions.RemoveEmptyEntries);
                            this.AvailableIdentModuleUpgrade = tmp[tmp.Length - 1];
                            upgrades = true;
                        }
                    }
                }

                return upgrades;
            }
            catch(Exception ex)
            {
                Console.WriteLine("Cannot launch for upgrades: {0}", ex);
                return false;
            }
        }

        public void DownloadUpgrades()
        {
            if (this.CanRunUpgrader == false)
                return;

            try
            {
                Console.WriteLine("Launch for downlable upgrades");
                string args = "--download-upgrades";


                this.CreateProcess(args, false, false);
            }
            catch (Exception ex)
            {
                Console.WriteLine("Cannot download upgrades: {0}", ex);

            }
        }

        public void InstallUpgrades()
        {
            if (this.CanRunUpgrader == false)
                return;

            if (this.UseUpgrader == false)
                return;

            try
            {
                Console.WriteLine("Install upgrades ...");
                string args = "--install-upgrades";

                this.CreateProcess(args, false, false);
            }
            catch (Exception ex)
            {
                Console.WriteLine("Cannot install upgrades: {0}", ex);

            }
        }


        private void CreateProcess(string args, bool wait_for_exit, bool show_console, int timeout_ms = -1)
        {
            this.ProcessOutput = String.Empty;

            args += " --locale " + this.CurrentLocale;
            if (this.DelayInDays > 0)
                args += " --delay " + this.DelayInDays.ToString();

            Console.WriteLine("Start upgrade process with args = {0}, wait = {1}, console = {2} timeout = {3} ",
                              args, wait_for_exit, show_console, timeout_ms);

            if (wait_for_exit)
                this.StartWatingProc(args, timeout_ms);
            else
                this.StartAsyncProc(args, show_console);
        }

        private void StartAsyncProc(string args, bool show_console)
        {
            System.Diagnostics.Process proc = new System.Diagnostics.Process();
            proc.StartInfo.FileName = this.ProgramFileExe;
            proc.StartInfo.Arguments = args;
            proc.StartInfo.CreateNoWindow = show_console == false;
            proc.StartInfo.UseShellExecute = show_console == true;

            proc.Start();
        }

        private void StartWatingProc(string args, int timeout_ms)
        {
            System.Diagnostics.Process proc = new System.Diagnostics.Process();
            proc.StartInfo.FileName = this.ProgramFileExe;
            proc.StartInfo.Arguments = args;
            proc.StartInfo.CreateNoWindow = true;
            proc.StartInfo.UseShellExecute = false;

            proc.StartInfo.RedirectStandardOutput = true;
            proc.OutputDataReceived += (sender, e) => this.ProcessOutput += (e.Data ?? String.Empty) + "\n";

            proc.Start();

            proc.BeginOutputReadLine();
            proc.WaitForExit(timeout_ms);
            if (proc.ExitCode != 0)
                throw new Exception(String.Format("Process finished with exit code {0}.", proc.ExitCode));
        }



        private string InstalledFileExe
        {
            get
            {
                Assembly assembly = Assembly.GetCallingAssembly();
                string file = Path.GetDirectoryName(assembly.Location);
                return Path.Combine(file, SoftwareUpgradeWindows.PROGRAM_NAME + ".exe");
            }
        }

        private string ApoIdentCommonFile
        {
            get
            {
                Assembly assembly = Assembly.GetCallingAssembly();
                string file = Path.GetDirectoryName(assembly.Location);
                return Path.Combine(file, "ApoIdentCommon.dll");
            }
        }


        private bool IsProgramRuning { get; set; } = false;


        private string ProgramDir
        {
            get
            {
                return Path.Combine(GlobalFiles.ApoIdentSoftwareUpggradeDir, "Program");
            }
        }

        private string ProgramFileExe
        {
            get
            {
                return Path.Combine(this.ProgramDir, SoftwareUpgradeWindows.PROGRAM_NAME + ".exe");
            }
        }

        private string HclDir
        {
            get
            {
                return Path.Combine(new FileInfo(Assembly.GetEntryAssembly().Location).Directory.FullName, "HCL");
            }
        }

        private bool CanRunUpgrader
        {
            get
            {
                return this.CopySuccess &&
                       this.IsProgramRuning == false;
            }
        }

        private Locale.LocaleType CurrentLocale
        {
            get
            {
                CultureInfo culture = CultureInfo.CurrentCulture;
                if (culture.ToString().StartsWith("de", StringComparison.InvariantCulture))
                    return Locale.LocaleType.de_DE;


                if (culture.ToString().StartsWith("cs", StringComparison.InvariantCulture))
                    return Locale.LocaleType.cs_CZ;

                return Locale.LocaleType.en_GB;
            }
        }

        private bool CopySuccess { get; set; } = true;
        private bool UseUpgrader { get; set; } = false;
        private string ProcessOutput { get; set; } = String.Empty;

        public string AvailableQuickStepUpgrade { get; private set; } = String.Empty;
        public string AvailableIdentModuleUpgrade { get; private set; } = String.Empty;
        private int DelayInDays { get; set; } = 0;
    }
}
