using System;
using System.Diagnostics;
using System.Threading;

namespace MicronSync
{
    public class LMZAParser : IDisposable
    {     
        public Process proc = new Process();
        bool abortProcess = false;
        public enum endResult
        {
            MakePackage_Aborted,
            ExtractPackage_Aborted,
            ExtractPackage_Error,
            MakePackage_Error,
            Aborted,
            Error,
            Default,
            TestArchive_Aborted,
            TestArchive_Error
        }
        public static string szExePath { get; set; }
        public static string szWorkDir { get; set; }

        public endResult MakePackage(string fileName, string sourceDir, string destDir, int compressLvl, string password, bool sfx)
        {
            endResult _endResult = endResult.Default;

            string argMethod = " a ";
            Func<string, string> argSourceDir = SourceDir;
            Func<string, string, string> argDestDir = DestDir;
            Func<int, string> argCompressLvl = CompressLvl;
            Func<string, string> argPassword = Password;
            Func<bool, string> argSfx = Sfx;

            _endResult = RunProcess(argMethod +
                    argSfx(sfx) +
                    argDestDir(destDir, fileName) +
                    argPassword(password) +
                    argSourceDir(sourceDir) +
                    argCompressLvl(compressLvl));

            // Convert generic error to MakePackage error.
            switch (_endResult)
            {
                case endResult.Aborted:
                    _endResult = endResult.MakePackage_Aborted;
                    break;
                case endResult.Error:
                    _endResult = endResult.MakePackage_Error;
                    break;
                default:
                    break;
            }

            return _endResult;
        }
        #region MakePackage

        private string Sfx(bool arg)
        {
            // Appends SFX parameter to archive.
            string result = null;
            if (arg)
            {
                result = "-sfx";
            }

            return result;
        }

        private string CompressLvl(int arg)
        {
            return String.Format("-mx={0}", arg.ToString());
        }

        private string DestDir(string path, string file)
        {
            // Combines the path and filename into one.
            return String.Format("\"{0}\\{1}\" ", path, file);
        }

        private string SourceDir(string arg)
        {
            return String.Format("\"{0}\\*\" ", arg);
        }

        private string Password(string pass)
        {
            string result = null;
            if (pass != "") { result += String.Format("-p{0} -mhe ", pass); }

            return result;
        }

        #endregion

        public endResult ExtractPackage(string fileName, string inDir, string outDir, string password)
        {
            endResult _endResult = endResult.Default;

            // Test archive before attempting to extract it
            // DO NOT DO THIS WITH HUGE PACKAGES!
            //exitCode += TestArchive(fileName, inDir, password);

            string argMethod = " x ";
                //Func<string, string> argSourceDir = SourceDir;
                //Func<string, string, string> argDestDir = DestDir;
                Func<string, string, string> argInDir = InDir;
                Func<string, string> argOutDir = OutDir;
                Func<string, string> argOutPassword = OutPassword;

                _endResult = RunProcess(argMethod +
                        argInDir(inDir, fileName) +
                        argOutPassword(password) +
                        argOutDir(outDir));

            // Convert generic error to ExtractPackage error.
            switch (_endResult)
            {
                case endResult.Aborted:
                    _endResult = endResult.ExtractPackage_Aborted;
                    break;
                case endResult.Error:
                    _endResult = endResult.ExtractPackage_Error;
                    break;
                default:
                    break;
            }

            return _endResult;
        }
        #region ExtractPackage

        private string OutDir(string path)
        {
            return String.Format("-o\"{0}\" ", path);
        }

        private string InDir(string path, string file)
        {
            return String.Format("\"{0}\\{1}\" -aoa ", path, file);
        }

        private string OutPassword(string pass)
        {
            return String.Format("-p{0} ", pass);
        }

        #endregion

        public endResult TestArchive(string fileName, string inDir, string password)
        {
            endResult _endResult = endResult.Default;

            string argMethod = " t ";
            Func<string, string, string> argInDir = InDir;
            Func<string, string> argOutPassword = OutPassword;

            _endResult = RunProcess(argMethod +
                        argInDir(inDir, fileName) +
                        argOutPassword(password));

            // Convert generic error to TestArchive error.
            switch (_endResult)
            {
                case endResult.Aborted:
                    _endResult = endResult.TestArchive_Aborted;
                    break;
                case endResult.Error:
                    _endResult = endResult.TestArchive_Error;
                    break;
                default:
                    break;
            }

            return _endResult;
        }

        /// <summary>
        /// Run 7-zip with command line arguments.
        /// </summary>
        /// <param name="args"></param>
        /// <returns></returns>
        private endResult RunProcess(string args)
        {
            endResult _endResult = endResult.Default;

            try
            {
                proc.StartInfo.FileName = szExePath;
                proc.StartInfo.WorkingDirectory = szWorkDir;
                proc.StartInfo.WindowStyle = ProcessWindowStyle.Hidden;
                ///
                proc.StartInfo.Arguments = args;

                proc.Start();
                while (!proc.HasExited)
                {
                    Thread.Sleep(1000);
                    Console.WriteLine("DEBUG: LMZA process is alive...");
                    if (abortProcess)
                    {
                        _endResult = endResult.Aborted;
                        proc.Kill();
                        Console.WriteLine("DEBUG: LMZA process aborted");
                    }
                }

                if (proc.ExitCode > 0)
                    _endResult = endResult.Error;
            }
            catch (Exception)
            {
                _endResult = endResult.Error;
                proc.Dispose();
            }

            return _endResult;
        }

        public void Abort() { abortProcess = true; }

        public void Dispose()
        {
            if (!proc.HasExited)
                proc.Kill();

            proc.Dispose();
        }
    }
}
