using System;
using System.Collections.Generic;
using System.Text;
using System.IO;
using System.Windows.Forms;
using System.Diagnostics;
using s3pi.Settings;
using s3pi.Package;
using s3pi.Interfaces;
using s3pi.WrapperDealer;
using CASPartResource;
using CatalogResource;
using NameMapResource;
using System.Threading;

namespace s3sr
{
    /// <summary>
    /// Class that Handle the Script Reading and Compiling Process
    /// </summary>
    class ParseScript
    {
        #region Callback Events
        public static ScriptProcessor.ChangeVersion ChangeVersion;
        public static ScriptProcessor.ChangeFile ChangeFile;
        public static ScriptProcessor.ChangeKey ChangeKey;
        #endregion

        private static string ErrorMessage = "";
        private static readonly List<string> validVersions = new List<string>(new string[] { "BASE", "EP01", "SP01", "EP02", "SP02", "EP03", "SP03", "EP04", "STORE", "CUSTOM" });

        /// <summary>
        /// Validate version parsed from Script
        /// </summary>
        /// <param name="version">Version in String Value</param>
        /// <returns>true if valid, false if not</returns>
        public static bool isVersionValid(string version)
        {
            return (validVersions.IndexOf(version.ToUpper()) == -1 ? false : true);
        }

        /// <summary>
        /// Attempts to transform a string to it's respective Enum Type
        /// </summary>
        /// <typeparam name="T">Expected Enum</typeparam>
        /// <param name="name">Value as String from respective Enum</param>
        /// <returns>Enum value as it's own Type</returns>
        public static T StringToEnum<T>(string name)
        {
            return (T)Enum.Parse(typeof(T), name);
        }

        /// <summary>
        /// Begin the Script Reading and Compiling Process of the scripts passed to <paramref name="path_Scripts"/>.
        /// </summary>
        /// <param name="path_Scripts">Array containing the Scripts Paths to run</param>
        public static void Start(string[] path_Scripts)
        {
            #region Check for Updates
            if (Settings.checkUpdate == Settings.CheckUpdateState.Always)
                if (Updater.CheckVersion("https://awtmk.svn.sourceforge.net/svnroot/ts3tools/s3sr/s3sr_Version.txt"))
                    (new frmUpdate()).ShowDialog();
                    //if (MessageBox.Show("Since there was a update available would you like to abort processing this script?", "Abort Script", MessageBoxButtons.YesNo, MessageBoxIcon.Question) == DialogResult.Yes)
            //Environment.Exit(0);
            #endregion

            #region Validate Path Settings

            #region BASE
            if (string.IsNullOrEmpty(Settings.path_BASE.Trim()))
            {
                MessageBox.Show("Base Game path wasn't specified.\nOperation aborted.", "Attention", MessageBoxButtons.OK, MessageBoxIcon.Exclamation);
                Environment.Exit(0);
            }
            #endregion

            #region EP01
            if (Settings.hasEP01 & string.IsNullOrEmpty(Settings.path_EP01.Trim()))
            {
                switch (MessageBox.Show("You have EP01, but it's path wasn't specified correctly.\nDo you want to abort the operation?\nIf you don't, the script shall continue but will not read EP01 operations, in case it exists.", "Attention", MessageBoxButtons.YesNo, MessageBoxIcon.Exclamation))
                {
                    case DialogResult.Yes:
                        Environment.Exit(0);
                        break;
                }
            }
            #endregion

            #region SP01
            if (Settings.hasSP01 & string.IsNullOrEmpty(Settings.path_SP01.Trim()))
            {
                switch (MessageBox.Show("You have SP01, but it's path wasn't specified.\nDo you want to abort the operation?\nIf you don't, the script shall continue but will not read SP01 operations, in case it exists.", "Attention", MessageBoxButtons.YesNo, MessageBoxIcon.Exclamation))
                {
                    case DialogResult.Yes:
                        Environment.Exit(0);
                        break;
                }
            }
            #endregion

            #region EP02
            if (Settings.hasEP02 & string.IsNullOrEmpty(Settings.path_EP02.Trim()))
            {
                switch (MessageBox.Show("You have EP02, but it's path wasn't specified.\nDo you want to abort the operation?\nIf you don't, the script shall continue but will not read EP02 operations, in case it exists.", "Attention", MessageBoxButtons.YesNo, MessageBoxIcon.Exclamation))
                {
                    case DialogResult.Yes:
                        Environment.Exit(0);
                        break;
                }
            }
            #endregion

            #region SP02
            if (Settings.hasSP02 & string.IsNullOrEmpty(Settings.path_SP02.Trim()))
            {
                switch (MessageBox.Show("You have SP02, but it's path wasn't specified.\nDo you want to abort the operation?\nIf you don't, the script shall continue but will not read SP02 operations, in case it exists.", "Attention", MessageBoxButtons.YesNo, MessageBoxIcon.Exclamation))
                {
                    case DialogResult.Yes:
                        Environment.Exit(0);
                        break;
                }
            }
            #endregion

            #region EP03
            if (Settings.hasEP03 & string.IsNullOrEmpty(Settings.path_EP03.Trim()))
            {
                switch (MessageBox.Show("You have EP03, but it's path wasn't specified.\nDo you want to abort the operation?\nIf you don't, the script shall continue but will not read EP03 operations, in case it exists.", "Attention", MessageBoxButtons.YesNo, MessageBoxIcon.Exclamation))
                {
                    case DialogResult.Yes:
                        Environment.Exit(0);
                        break;
                }
            }
            #endregion

            #region SP03
            if (Settings.hasSP03 & string.IsNullOrEmpty(Settings.path_SP03.Trim()))
            {
                switch (MessageBox.Show("You have SP03, but it's path wasn't specified.\nDo you want to abort the operation?\nIf you don't, the script shall continue but will not read SP03 operations, in case it exists.", "Attention", MessageBoxButtons.YesNo, MessageBoxIcon.Exclamation))
                {
                    case DialogResult.Yes:
                        Environment.Exit(0);
                        break;
                }
            }
            #endregion

            #region EP04
            if (Settings.hasEP04 & string.IsNullOrEmpty(Settings.path_EP04.Trim()))
            {
                switch (MessageBox.Show("You have EP04, but it's path wasn't specified.\nDo you want to abort the operation?\nIf you don't, the script shall continue but will not read EP04 operations, in case it exists.", "Attention", MessageBoxButtons.YesNo, MessageBoxIcon.Exclamation))
                {
                    case DialogResult.Yes:
                        Environment.Exit(0);
                        break;
                }
            }
            #endregion

            #region Output
            if (string.IsNullOrEmpty(Settings.path_Output.Trim()))
            {
                MessageBox.Show("Output Package Path isn't specified.\nRun the main application again to do so.\n\nOperation aborted.", "Attention", MessageBoxButtons.OK, MessageBoxIcon.Exclamation);
                Environment.Exit(0);
            }
            #endregion

            #endregion

            string ScriptVersion = "BASE";
            NameMapResource.NameMapResource _KEYs = new NameMapResource.NameMapResource(0, null);
            List<string> _ScriptLines = new List<string>();
            List<Script> _Script = new List<Script>();
            List<List<string>> _paths = new List<List<string>>();

            try
            {
                foreach(string VersionValue in validVersions)
                    _Script.Add(new Script(StringToEnum<Enums.Version>(VersionValue)));

                #region Arrange Package Files from Folders
                foreach (string s in validVersions)
                    _paths.Add(new List<string>());               

                if (!string.IsNullOrEmpty(Settings.path_BASE.Trim()))
                    _paths[(int)Enums.Version.BASE].AddRange(new string[] { 
                            Settings.path_BASE + @"\GameData\Shared\Packages\DeltaBuild0.package",
                            Settings.path_BASE + @"\GameData\Shared\Packages\FullBuild0.package",
                        });

                if (!string.IsNullOrEmpty(Settings.path_EP01))
                    _paths[(int)Enums.Version.EP01].Add(Settings.path_EP01 + @"\GameData\Shared\Packages\FullBuildep1.package");

                if (!string.IsNullOrEmpty(Settings.path_SP01))
                    _paths[(int)Enums.Version.SP01].Add(Settings.path_SP01 + @"\GameData\Shared\Packages\FullBuild_p03.package");

                if (!string.IsNullOrEmpty(Settings.path_EP02))
                    _paths[(int)Enums.Version.EP02].Add(Settings.path_EP02 + @"\GameData\Shared\Packages\FullBuild_p04.package");

                if (!string.IsNullOrEmpty(Settings.path_SP02))
                    _paths[(int)Enums.Version.SP02].Add(Settings.path_SP02 + @"\GameData\Shared\Packages\FullBuild_p05.package");

                if (!string.IsNullOrEmpty(Settings.path_EP03))
                    _paths[(int)Enums.Version.EP03].Add(Settings.path_EP03 + @"\GameData\Shared\Packages\FullBuild_p06.package");

                if (!string.IsNullOrEmpty(Settings.path_SP03))
                    _paths[(int)Enums.Version.SP03].Add(Settings.path_SP03 + @"\GameData\Shared\Packages\FullBuild_p07.package");

                if (!string.IsNullOrEmpty(Settings.path_Store))
                    _paths[(int)Enums.Version.STORE].AddRange(Directory.GetFiles(Settings.path_Store));

                if (!string.IsNullOrEmpty(Settings.path_StoreAlternative))
                {
                    if (!Settings.useStoreAlternativePath)
                        goto Label_ContinueFS;

                    if(Settings.searchSubFiles_Store)
                        _paths[(int)Enums.Version.STORE].AddRange(Directory.GetFiles(Settings.path_StoreAlternative, "*.*", SearchOption.AllDirectories));
                    else
                        _paths[(int)Enums.Version.STORE].AddRange(Directory.GetFiles(Settings.path_StoreAlternative));
                }

            Label_ContinueFS:
                if (_paths[(int)Enums.Version.STORE].Count > 0)
                    _paths[(int)Enums.Version.STORE].RemoveAll(
                        delegate(string s)
                        {
                            return !((s.ToLower().EndsWith(".package")) | (s.ToLower().EndsWith(".dbc")) | (s.ToLower().EndsWith(".ebc")) | (s.ToLower().EndsWith(".nhd")));
                        });

                if (!string.IsNullOrEmpty(Settings.path_Custom))
                {
                    if(Settings.searchSubFiles_Custom)
                        _paths[(int)Enums.Version.CUSTOM].AddRange(Directory.GetFiles(Settings.path_Custom, "*.*", SearchOption.AllDirectories));
                    else
                        _paths[(int)Enums.Version.CUSTOM].AddRange(Directory.GetFiles(Settings.path_Custom));

                    _paths[(int)Enums.Version.CUSTOM].RemoveAll(
                        delegate(string s)
                        {
                            return !((s.ToLower().EndsWith(".package")) | (s.ToLower().EndsWith(".dbc")) | (s.ToLower().EndsWith(".ebc")) | (s.ToLower().EndsWith(".nhd")));
                        });
                }
                #endregion

                #region Begin to Loop Scripts
                for (int i = 0; i < path_Scripts.Length; i++)
                {
                    _ScriptLines = new List<string>(File.ReadAllLines(path_Scripts[i]));
                    if ((new FileInfo(path_Scripts[i])).Length == 0)
                    {
                        MessageBox.Show(string.Format("Error while parsing script \"{0}\".\n\nScript is null.", path_Scripts[i])
                            , "Error", MessageBoxButtons.OK, MessageBoxIcon.Error);
                        continue;
                    }

                    #region Begin Reading Script
                    try
                    {
                        for (int x = 0; x < _ScriptLines.Count; x++)
                        {
                            #region Jump Blank/Comment lines
                            if (string.IsNullOrEmpty(_ScriptLines[x].Trim()) || _ScriptLines[x].Trim().StartsWith("::"))
                                continue;
                            #endregion

                            #region Verify Script Version
                            if (_ScriptLines[x].Trim().StartsWith("["))
                            {
                                ScriptVersion = _ScriptLines[x].Trim().ToUpper().Replace("[", string.Empty).Replace("]", string.Empty);
                                if (!isVersionValid(ScriptVersion))
                                {
                                    MessageBox.Show(string.Format("Error while parsing script.\nInvalid version found at line {0}:\n\"{1}\"\n\nOperation aborted.", new string[] { (x + 1).ToString(), ScriptVersion }), "Error", MessageBoxButtons.OK, MessageBoxIcon.Error);
                                    return;
                                }
                                continue;
                            }
                            #endregion

                            #region Handle KeyName Reference & Script Line
                            string[] parsingScript;
                            if (_ScriptLines[x].Trim().StartsWith("{"))
                                parsingScript = new string[] { _ScriptLines[x], _ScriptLines[++x] };
                            else
                                parsingScript = new string[] { _ScriptLines[x] };

                            if (!(_Script[(int)StringToEnum<Enums.Version>(ScriptVersion)].AddCommand(parsingScript, x)))
                                return;
                            #endregion

                            Application.DoEvents();
                        }
                    }
                    catch (Exception ex)
                    {
                        MessageBox.Show(ex.ToString(), "Exception while Reading Script", MessageBoxButtons.OK, MessageBoxIcon.Error);
                        ErrorReport.CreateExceptionReport(ex, "Exception @ \"UGFyc2VTY3JpcHQuY3M= 222\";");
                        return;
                    }
                    #endregion

                    #region Look for Duplicated Script Lines
                    try
                    {
                        foreach (string currentVersion in validVersions)
                            for (int iDuplicate = 0; iDuplicate < _Script[(int)StringToEnum<Enums.Version>(currentVersion)].Commands.Count; iDuplicate++)
                            {
                                int[] duplicates = new int[0];
                                if ((duplicates = _Script[(int)StringToEnum<Enums.Version>(currentVersion)].FindDuplicates()).Length > 0)
                                {
                                    MessageBox.Show(string.Format("Found Duplicated Lines on the [{0}] Script Section.\nReference: Lines({1}, {2})\n\nOperation aborted.", new string[] { StringToEnum<Enums.Version>(currentVersion).ToString(), duplicates[0].ToString(), duplicates[1].ToString() }), "Error");
                                    return;
                                }
                            }
                    }
                    catch (Exception ex)
                    {
                        MessageBox.Show(ex.ToString(), "Exception while Looking for Duplicated Script Lines", MessageBoxButtons.OK, MessageBoxIcon.Error);
                        ErrorReport.CreateExceptionReport(ex, "Exception @ \"UGFyc2VTY3JpcHQuY3M= 263\";");
                        return;
                    }
                    #endregion
                    
                    FileInfo _ScriptInfo = new FileInfo(path_Scripts[i]);

                    #region Begin Script Rules on the Packages
                    int PackageCount = _paths[(int)Enums.Version.BASE].Count;
                    IPackage srcPackage = null; ;
                    IPackage outPackage = Package.NewPackage(0);
                    IList<IResourceIndexEntry> lResources = null;
                    int ResourceCount = 0;

                    for (int iVersion = 0; iVersion < validVersions.Count; iVersion++)
                    {
                        #region Verify if User have the Current Game Version
                        switch (iVersion)
                        {
                            case (int)Enums.Version.EP01:
                                if (!Settings.hasEP01)
                                    continue;
                                break;
                            case (int)Enums.Version.SP01:
                                if (!Settings.hasSP01)
                                    continue;
                                break;
                            case (int)Enums.Version.EP02:
                                if (!Settings.hasEP02)
                                    continue;
                                break;
                            case (int)Enums.Version.SP02:
                                if (!Settings.hasSP02)
                                    continue;
                                break;
                            case (int)Enums.Version.EP03:
                                if (!Settings.hasEP03)
                                    continue;
                                break;
                            case (int)Enums.Version.SP03:
                                if (!Settings.hasSP03)
                                    continue;
                                break;
                        }
                        #endregion

                        PackageCount = _paths[iVersion].Count;

                        #region Verify if the Current Game Version paths isn't null
                        if (PackageCount == 0 || _Script[iVersion].Commands.Count == 0)
                            continue;
                        #endregion

                        for (int iPackage = 0; iPackage < PackageCount; iPackage++)
                        {
                            #region Verify if the Reading Package really exists
                            if (!File.Exists(_paths[iVersion][iPackage]))
                            {
                                MessageBox.Show(string.Format("File \"{0}\" does not exist.\nMake sure the path setting for \"{1}\" section is at the right location.",
                                    new string[] { _paths[iVersion][iPackage], ((Enums.Version)iVersion).ToString() }),
                                    "Error", MessageBoxButtons.OK, MessageBoxIcon.Error);
                                continue;
                            }
                            #endregion

                            List<Catalog> iRemove = new List<Catalog>();

                            FileInfo CurrentPackage = new FileInfo(_paths[iVersion][iPackage]);
                            
                            #region Decrypt Store DBPP Package
                            if (iVersion == validVersions.Count - 2)
                            {
                                bool AttemptDecrypt = false;
                            Label_AttemptOpenPackage:

                                if (AttemptDecrypt)
                                {
                                    MessageBox.Show(string.Format(
                                            "Seems like the file \"{0}\" is still encrypted.\nApplication will avoid this file, whatever lurks inside it is still a mistery.", CurrentPackage.ToString())
                            , "Encrypted File", MessageBoxButtons.OK, MessageBoxIcon.Information);
                                    continue;
                                }

                                try
                                {
                                    srcPackage = Package.OpenPackage(0, CurrentPackage.ToString());
                                }
                                catch (InvalidDataException ex)
                                {
                                    if (ex.Message.Contains("DBPP"))
                                    {
                                        if (!Settings.decryptStore)
                                            continue;

                                        if (MessageBox.Show(
                                            string.Format(
                                            "File \"{0}\" is encrypted.\nDo you want to decrypt it?\n\nThe application will not be able to read it unless you do.", CurrentPackage.ToString())
                            , "Encrypted File", MessageBoxButtons.YesNo, MessageBoxIcon.Question)
                                            == DialogResult.Yes)
                                        {
                                            if (MessageBox.Show(
                                                string.Format(
                                                "Would you like the application to make a backup of the file first?\nFile: \"{0}\"", CurrentPackage.ToString()),
                                                "Backup File", MessageBoxButtons.YesNo, MessageBoxIcon.Question) == DialogResult.Yes)
                                                FileUtil.Backup(CurrentPackage.ToString());

                                        Label_AttemptDecrypt:
                                            string s3rcPath = Application.StartupPath + @"\s3rc.EXE";
                                            if (!File.Exists(s3rcPath))
                                            {
                                                switch (MessageBox.Show(string.Format(
                                                    "Couldn't find s3rc on the application folder:\n{0}",
                                                    s3rcPath), "Error", MessageBoxButtons.AbortRetryIgnore, MessageBoxIcon.Error))
                                                {
                                                    case DialogResult.Abort:
                                                        Environment.Exit(0);
                                                        break;
                                                    case DialogResult.Retry:
                                                        goto Label_AttemptDecrypt;
                                                    case DialogResult.Ignore:
                                                        continue;
                                                }
                                            }

                                            try
                                            {
                                                string tmpPackage = System.IO.Path.GetTempFileName(); //System.IO.Path.GetTempPath() + Guid.NewGuid().ToString() + ".package";
                                                File.Copy(CurrentPackage.ToString(), tmpPackage);
                                                Process s3rcProcess = new Process();
                                                s3rcProcess.StartInfo.FileName = s3rcPath;
                                                s3rcProcess.StartInfo.Arguments = "-f \"" + tmpPackage + "\"";
                                                s3rcProcess.StartInfo.CreateNoWindow = true;
                                                s3rcProcess.StartInfo.WindowStyle = ProcessWindowStyle.Hidden;
                                                s3rcProcess.Start();
                                                s3rcProcess.WaitForExit();
                                                Thread.Sleep(1000);
                                            }
                                            catch (Exception s3rcException)
                                            {
                                                MessageBox.Show(s3rcException.ToString(), "Exception while running S3RC", MessageBoxButtons.OK, MessageBoxIcon.Error);
                                                ErrorReport.CreateExceptionReport(s3rcException, "Exception @ \"UGFyc2VTY3JpcHQuY3M= 379\";");
                                                return;
                                            }

                                            AttemptDecrypt = true;

                                            goto Label_AttemptOpenPackage;
                                        }
                                    }
                                    else
                                    {
                                        MessageBox.Show(ex.ToString(), "Exception trying to Open Package", MessageBoxButtons.OK, MessageBoxIcon.Error);
                                        ErrorReport.CreateExceptionReport(ex, "Exception @ \"UGFyc2VTY3JpcHQuY3M= 351|1\";");
                                        return;
                                    }
                                }
                                catch (Exception ex)
                                {
                                    MessageBox.Show(ex.ToString(), "Exception trying to Open Package", MessageBoxButtons.OK, MessageBoxIcon.Error);
                                    ErrorReport.CreateExceptionReport(ex, "Exception @ \"UGFyc2VTY3JpcHQuY3M= 351|2\";");
                                }
                            }
                            else
                            #endregion
                                srcPackage = Package.OpenPackage(0, CurrentPackage.ToString());

                            
                            for (int iScript = 0; iScript < _Script[iVersion].Commands.Count; iScript++)
                            {
                                if (iScript > _Script[iVersion].Commands.Count)
                                    continue;

                                #region Begin to look inside the Packages
                                lResources = srcPackage.FindAll(x =>
                                {
                                    #region Validate Key Reference Information
                                    if (x.ResourceType == _Script[iVersion].Commands[iScript].ResourceType
                                        //& x.ResourceGroup == _Script[iVersion].Commands[iScript].ResourceGroup
                                        // (Not necessary, seems like it's hard to match duplicated instances at this point)
                                        & x.Instance == _Script[iVersion].Commands[iScript].Instance
                                        )
                                    #endregion
                                    {
                                        #region Add Resource Key if Specified
                                        if (!string.IsNullOrEmpty(_Script[iVersion].Commands[iScript].KeyName))
                                            _KEYs.Add(x.Instance, _Script[iVersion].Commands[iScript].KeyName);
                                        #endregion

                                        try
                                        {
                                            #region Handle OBJK
                                            if (x.ResourceType == (uint)Enums.ResourceType.OBJK)
                                            {
                                                ObjKeyResource.ObjKeyResource objk = (ObjKeyResource.ObjKeyResource)WrapperDealer.GetResource(0, srcPackage, x);
                                                if (objk == null)
                                                {
                                                    ErrorMessage = "Invalid OBJK Reference while reading package.\nLine: " + _Script[iVersion].Commands[iScript].LineReference;
                                                    return false;
                                                }
                                                
                                                if (!HandleOBJKScriptRule(ref objk, _Script[iVersion].Commands[iScript]))
                                                    return false;

                                                iRemove.Add(_Script[iVersion].Commands[iScript]);

                                                outPackage.AddResource(x, objk.Stream, true);

                                                ResourceCount++;
                                                Application.DoEvents();
                                                return true;
                                            }

                                            #region Handle OBJD
                                            if (x.ResourceType == (uint)Enums.ResourceType.OBJD)
                                            {
                                                ObjectCatalogResource objd = (ObjectCatalogResource)WrapperDealer.GetResource(0, srcPackage, x);
                                                if (objd == null)
                                                {
                                                    ErrorMessage = "Invalid OBJD Reference while reading package.\nLine: " + _Script[iVersion].Commands[iScript].LineReference;
                                                    return false;
                                                }

                                                if (!HandleOBJDScriptRule(ref objd, _Script[iVersion].Commands[iScript]))
                                                    return false;

                                                iRemove.Add(_Script[iVersion].Commands[iScript]);

                                                outPackage.AddResource(x, objd.Stream, true);

                                                ResourceCount++;
                                                Application.DoEvents();
                                                return true;
                                            }
                                            #endregion

                                            #region Handle CASP
                                            else if (x.ResourceType == (uint)Enums.ResourceType.CASP)
                                            {
                                                CASPartResource.CASPartResource casp = (CASPartResource.CASPartResource)WrapperDealer.GetResource(0, srcPackage, x);
                                                if (casp == null)
                                                {
                                                    MessageBox.Show("Invalid CASP Reference while reading package.\nLine: " + _Script[iVersion].Commands[iScript].LineReference, "Error", MessageBoxButtons.OK, MessageBoxIcon.Exclamation);
                                                    return false;
                                                }

                                                if (!HandleCASPScriptRule(ref casp, _Script[iVersion].Commands[iScript]))
                                                    return false;

                                                iRemove.Add(_Script[iVersion].Commands[iScript]);

                                                outPackage.AddResource(x, casp.Stream, true);

                                                ResourceCount++;
                                                Application.DoEvents();
                                                return true;
                                            }
                                            #endregion
                                        }
                                        catch (Exception ex)
                                        {
                                            #region Resource Handling Exception
                                            Application.DoEvents();
                                            MessageBox.Show(ex.ToString(), "Exception while Handling Resource", MessageBoxButtons.OK, MessageBoxIcon.Error);
                                            ErrorReport.CreateExceptionReport(ex, "Exception @ \"UGFyc2VTY3JpcHQuY3M= 454\";\n\n" +
                                                string.Format("key:{0}:{1}:{2}",
                                                new string[] {
                                                    x.ResourceType.ToString(),
                                                    x.ResourceGroup.ToString(),
                                                    x.Instance.ToString()
                                                }));
                                            return false;
                                            #endregion
                                        }
                                        
                                    }

                                    return false;
                                });

                                Application.DoEvents();
                                #endregion
                            }

                            #region Remove Readen Script Line(s)
                            _Script[iVersion].Commands.RemoveAll(x => iRemove.Contains(x));
                            #endregion

                            Application.DoEvents();
                        }
                    }

                    if (ResourceCount > 0)
                    {
                        string outputPackagePath = string.Format(@"{0}\{1}.package",
                            Settings.path_Output,
                            _ScriptInfo.Name.Replace(_ScriptInfo.Extension, "")
                            );

                        #region Get rid of any older Output Package
                        if (File.Exists(outputPackagePath))
                            File.Delete(outputPackagePath);
                        #endregion

                        #region Create _KEY Resource
                        outPackage.AddResource(
                            (new TGIBlock(0, null, (uint)Enums.ResourceType._KEY, 0x0D45C751, (ulong)Enums.VersionHash.ALL))
                            , _KEYs.Stream, false);
                        #endregion

                        #region Save Output Package
                        outPackage.SaveAs(outputPackagePath);
                        #endregion

                        #region Finished Processing Message
                        if (Settings.alertInfo)
                        {
                            MessageBox.Show(
                                string.Format(
                                "Script \"{0}\" processed successfully.",
                                path_Scripts[i]),
                                "Script Processed", MessageBoxButtons.OK, MessageBoxIcon.Information);
                        }
                        #endregion

                        Application.DoEvents();
                    }
                    #endregion
                }
                #endregion
            }
            catch (Exception exceptionScript)
            {
                MessageBox.Show(exceptionScript.ToString(), "Exception while Parsing Script", MessageBoxButtons.OK, MessageBoxIcon.Error);
                ErrorReport.CreateExceptionReport(exceptionScript, "Exception @ \"UGFyc2VTY3JpcHQuY3M= 139\";");
                return;
            }
        }

        #region Specific Resource Handler for Catalog

        /// <summary>
        /// Handle the <paramref name="objd"/> according to the Catalog <paramref name="_script"/>.
        /// </summary>
        /// <param name="objd">[ref] ObjectCatalogResource</param>
        /// <param name="_script">[ref] Catalog that handled a Script Line</param>
        /// <returns>true if changes on the <paramref name="objd"/> were accepted, false it they were not</returns>
        private static bool HandleOBJDScriptRule(ref ObjectCatalogResource objd, Catalog _script)
        {
            try
            {
                //objd.TopicRatings = new ObjectCatalogResource.TopicRating
                if (_script.change_isStealable)
                    objd.CommonBlock.IsStealable = _script.isStealable;
                if (_script.change_isReposessable)
                    objd.CommonBlock.IsReposessable = _script.isReposessable;
                if (_script.change_objectTypeFlags)
                    objd.ObjectTypeFlags = (ObjectCatalogResource.ObjectType) _script.objectTypeFlags;
                if (_script.change_wallPlacementFlags)
                    objd.WallPlacementFlags = (ObjectCatalogResource.WallPlacement) _script.wallPlacementFlags;
                if (_script.change_movementFlags)
                    objd.MovementFlags = (ObjectCatalogResource.Movement) _script.movementFlags;
                if (_script.change_cutoutTilesPerLevel)
                    objd.CutoutTilesPerLevel = _script.cutoutTilesPerLevel;
                if (_script.change_levels)
                    objd.Levels = _script.levels;
                if (_script.change_ambienceTypeHash)
                    objd.AmbienceTypeHash = _script.ambienceTypeHash;
                if (_script.change_roomCategoryFlags)
                    objd.RoomCategoryFlags = (ObjectCatalogResource.RoomCategory) _script.roomCategoryFlags;
                if (_script.change_functionCategoryFlags)
                    objd.FunctionCategoryFlags = (ObjectCatalogResource.FunctionCategory) _script.functionCategoryFlags;
                if (_script.change_functionSubCategoryFlags)
                    objd.FunctionSubCategoryFlags = (ObjectCatalogResource.FunctionSubCategory) _script.functionSubCategoryFlags;
                if (_script.change_roomSubCategoryFlags)
                    objd.RoomSubCategoryFlags = (ObjectCatalogResource.RoomSubCategory) _script.roomSubCategoryFlags;
                if (_script.change_buildCategoryFlags)
                    objd.BuildCategoryFlags = (ObjectCatalogResource.BuildCategory) _script.buildCategoryFlags;
                if (_script.change_slotPlacementFlags)
                    objd.SlotPlacementFlags = (ObjectCatalogResource.SlotPlacement) _script.slotPlacementFlags;
                if (_script.change_moodletGiven)
                    objd.MoodletGiven = (ObjectCatalogResource.Moodlet) _script.moodletGiven;
                if (_script.change_moodletScore)
                    objd.MoodletScore = _script.moodletScore;
                if (_script.change_price)
                    objd.CommonBlock.Price = _script.price;
                if(_script.change_FireType)
                    objd.CommonBlock.FireType = (CatalogResource.CatalogResource.Common.Fire) _script.FireType;
                if (_script.change_BuildBuyProductStatusFlags)
                    objd.CommonBlock.BuildBuyProductStatusFlags = (CatalogResource.CatalogResource.Common.BuildBuyProductStatus)_script.BuildBuyProductStatusFlags;
                if (_script.change_isVisibleInWorldBuilder)
                    objd.CommonBlock.IsVisibleInWorldBuilder = _script.isVisibleInWorldBuilder;
                if(_script.OBJDStencils.Count > 0)
                {
                    for(int i = 0; i < _script.OBJDStencils.Count; i++)
                    {
                        objd.Materials.Add();
                        int x = 0; // Hold the Array Positions

                        #region Find Matching Material
                        int MaterialMatchID = -1;
                        for (x = 0; x < objd.Materials.Count; x++)
                        {
                            if (MaterialMatchID != -1) break;

                            for (int x1 = 0; x1 < objd.Materials[x].TGIBlocks.Count; x1++)
                            {
                                if (objd.Materials[x].TGIBlocks[x1].ResourceType == (int)Enums.ResourceType._IMG)
                                    if (objd.Materials[x].TGIBlocks[x1].Instance == _script.OBJDStencils[i].IMG_ReplaceInstance)
                                    {
                                        MaterialMatchID = x;
                                        break;
                                    }
                            }
                        }
                        #endregion

                        objd.Materials[(objd.Materials.Count - 1)] = new CatalogResource.CatalogResource.Material(0, null, objd.Materials[MaterialMatchID]);
                        for (x = 0; x < objd.Materials[(objd.Materials.Count - 1)].TGIBlocks.Count; x++)
                        {
                            if (objd.Materials[(objd.Materials.Count - 1)].TGIBlocks[x].ResourceType == (int)Enums.ResourceType._IMG)
                                if (objd.Materials[(objd.Materials.Count - 1)].TGIBlocks[x].Instance == _script.OBJDStencils[i].IMG_ReplaceInstance)
                                {
                                    objd.Materials[(objd.Materials.Count - 1)].TGIBlocks[x].Instance = _script.OBJDStencils[i].IMG_Instance;
                                    break;
                                }
                        }
                        objd.Materials[(objd.Materials.Count - 1)].Unknown3 = _script.OBJDStencils[i].StencilID;
                    }
                }
            }
            catch(Exception ex)
            {
                ErrorReport.CreateExceptionReport(ex, "Exception @ \"UGFyc2VTY3JpcHQuY3M= 631\";");
                return false;
            }
            return true;
        }

        /// <summary>
        /// Handle the <paramref name="casp"/> according to the Catalog <paramref name="_script"/>.
        /// </summary>
        /// <param name="casp">[ref] CASPartResource</param>
        /// <param name="_script">[ref] Catalog that handled a Script Line</param>
        /// <returns>true if changes on the <paramref name="casp"/>  were accepted, false it they were not</returns>
        private static bool HandleCASPScriptRule(ref CASPartResource.CASPartResource casp, Catalog _script)
        {
            try
            {
                if (_script.change_Clothing)
                    casp.Clothing = (ClothingType)_script.Clothing;
                if (_script.change_AgeGender)
                    casp.AgeGender = (AgeGenderFlags)_script.AgeGender;
                if (_script.change_ClothingCategory)
                    casp.ClothingCategory = (ClothingCategoryFlags)_script.ClothingCategory;
                if (_script.change_SortingPriority)
                    casp.SortPriority = _script.SortingPriority;
            }
            catch(Exception ex)
            {
                ErrorReport.CreateExceptionReport(ex, "Exception @ \"UGFyc2VTY3JpcHQuY3M= 711\";");
                return false;
            }
            return true;
        }
        #endregion
        
        /// <summary>
        /// Handle the <paramref name="objk"/> according to the Catalog <paramref name="_script"/>.
        /// </summary>
        /// <param name="casp">[ref] ObjectKeyResource</param>
        /// <param name="_script">[ref] Catalog that handled a Script Line</param>
        /// <returns>true if changes on the <paramref name="objk"/>  were accepted, false it they were not</returns>
        private static bool HandleOBJKScriptRule(ref ObjKeyResource.ObjKeyResource objk, Catalog _script)
        {
            try
            {
                if (_script.change_ScriptClass)
                    objk.ComponentData["scriptClass"] = new ObjKeyResource.ObjKeyResource.CDTString(0, null, "scriptClass", 0, _script.ScriptClass);
            }
            catch(Exception ex)
            {
                ErrorReport.CreateExceptionReport(ex, "Exception @ \"UGFyc2VTY3JpcHQuY3M= 788\";");
                return false;
            }
            return true;
        }
        #endregion
        
        
    }
}