﻿using System;
using System.Collections.Generic;
using System.Reflection;
using System.Text;
using CLAP;
using CLAP.Interception;
using CLAP.Validation;

namespace xmlTools
{
    // Define a class to receive parsed values
    class ParametersParser
    {
        private static string globalFileName = "";
        private static string globalElement = "";
        private static string globalParentElement = "";
        private static bool globalNoBackups = false;

        [Verb]
        public static void addValue(
            [Parameter(Required = true, Description = "Values to add to Element. Separated multiple by spaces.")] string value
            )
        {
            initialChecks();
            XmlTools myTools = new XmlTools(globalElement, globalParentElement, globalNoBackups);
            List<string> filesToProcess = getFilesToProcess(globalFileName);
            foreach (string currentFile in filesToProcess)
            {
                myTools.addValues(currentFile, value);
            }
            printProcessedMessage(filesToProcess.Count, MethodBase.GetCurrentMethod().Name);
        }

        [Verb]
        public static void removeValue(
            [Parameter(Required = true, Description = "Values to remove of Element. Separated multiple by spaces.")] string value
            )
        {
            initialChecks();
            XmlTools myTools = new XmlTools(globalElement, globalParentElement, globalNoBackups);
            List<string> filesToProcess = getFilesToProcess(globalFileName);
            foreach (string currentFile in filesToProcess)
            {
                myTools.removeValues(currentFile, value);
            }
            printProcessedMessage(filesToProcess.Count, MethodBase.GetCurrentMethod().Name);
        }

        [Verb]
        public static void updateChainValues(
            [Parameter(Required = true, Description = "The new first value of the chain. All the chain will be updated based on this value")] string newValue,
            [Parameter(Description = "Value which have some kind of relation with -newVal \n Together with -newVal updates all the values based on the" +
                "-newvalue and another position specified on -valrelation parameter (basically starts with (newvalue-valrelation) ) Is especially useful when" +
                "updating multiple related chains (on different files), like multiple objects from one position to another. Don't use with -filename, because" +
                "it will only update one file. \nExample: xmlTools.exe updatechainvalues -filename:OBANheli_body_center.xml -newvalue:\"1 1 1\" -valrelation:\"4 4 4\" -element:Translation -parelement:" +
                "OBANKeyFrame")] string valRelation,
            [Parameter(Description = "Only update specific positions. Positions starts with 0, separted multiple positions with space. Example: valpositions=\"0 1 4\"")] [MoreThan(-1)]  string valPositions
            )
        {
            initialChecks();
            XmlTools myTools = new XmlTools(globalElement, globalParentElement, globalNoBackups);
            List<string> filesToProcess = getFilesToProcess(globalFileName);
            foreach (string currentFile in filesToProcess)
            {
                myTools.changeValue(currentFile, newValue, valRelation, valPositions);
            }
            printProcessedMessage(filesToProcess.Count, MethodBase.GetCurrentMethod().Name);
        }

        [Verb(Description = "Inverts a chain (like an OBAN animation). Example: xmlTools.exe -filename OBANheli_rotorblades08.xml -invert -valElement Translation -valParentElement OBANKeyFrame (inverts translation chain)")]
        public static void Invert()
        {
            initialChecks();
            XmlTools myTools = new XmlTools(globalElement, globalParentElement, globalNoBackups);
            List<string> filesToProcess = getFilesToProcess(globalFileName);
            foreach (string currentFile in filesToProcess)
            {
                myTools.invert(currentFile); //Inverting the element order
            }
            printProcessedMessage(filesToProcess.Count, MethodBase.GetCurrentMethod().Name);
        }

        [Verb]
        public static void replaceValue(
            [Parameter(Required = true, Description = "Old value to replace in Element.")] string oldValue,
            [Parameter(Required = true, Description = "New value to replace in Element.")] string newValue
            )
        {
            XmlTools myTools = new XmlTools(globalElement, globalParentElement, globalNoBackups);
            List<string> filesToProcess = getFilesToProcess(globalFileName);
            foreach (string currentFile in filesToProcess)
            {
                myTools.replaceValue(currentFile, oldValue, newValue);
            }
            printProcessedMessage(filesToProcess.Count, MethodBase.GetCurrentMethod().Name);
        }

        [Verb]
        public static void replaceAll(
            [Parameter(Required = true, Description = "Value to replace in Element. Replace all values of a element by another value.")] string value,
            [Parameter(Description = "Only replace specific positions. Positions starts with 0, separted multiple positions with space. Example: valPositions=0 1 4")] [MoreThan(-1)] string valPositions
            )
        {
            initialChecks();
            XmlTools myTools = new XmlTools(globalElement, globalParentElement, globalNoBackups);
            List<string> filesToProcess = getFilesToProcess(globalFileName);
            foreach (string currentFile in filesToProcess)
            {
                if (!String.IsNullOrEmpty(valPositions))
                {
                    myTools.replaceAll(currentFile, value, valPositions);
                }
                else
                {
                    myTools.replaceAll(currentFile, value);
                }
            }
            printProcessedMessage(filesToProcess.Count, MethodBase.GetCurrentMethod().Name);
        }

        /// <summary>
        /// Patch in files can be used with wildcard or empty filename instead
        /// </summary>
        /// <param name="filename"></param>
        [Verb]
        public static void patchFile(
            [Parameter(Description = "Force the specified patch to run in specified files")] string forceInFiles
            )
        {
            XmlPatch myPatch;

            if (!String.IsNullOrEmpty(forceInFiles))
            {
                myPatch = new XmlPatch(globalFileName, forceInFiles, globalNoBackups);
            }
            else
            {
                myPatch = new XmlPatch(globalFileName, globalNoBackups);
            }

            myPatch.startPatch();
        }

        [Verb(Description = "Displays current XmlTools version.")]
        public static void version()
        {
            Console.WriteLine("xmlTools v" + Program.XmlToolsVersion);
            Console.WriteLine("\nWritten by s10k");
        }

        [Verb(Description = "Displays extra informations about XmlTools.")]
        public static void about()
        {
            version();
            Console.WriteLine();
            Console.WriteLine("For extra XmlTools support check it page at: http://wiki.oni2.net/XmlTools");
            Console.WriteLine();
            Console.WriteLine("This program uses the following free libraries: ");
            Console.WriteLine("-CLAP: for parameter parsing. (http://adrianaisemberg.github.io/CLAP/)");
            Console.WriteLine("-IronJS: for custom javascript code execution. (https://github.com/fholm/IronJS)");
            Console.WriteLine();
            Console.WriteLine("A big thanks to all the oni.bungie.org community!");
        }

        [Verb]
        public static void showErrTypes()
        {
            Array values = Enum.GetValues(typeof(Program.appErrors));

            foreach (Program.appErrors val in values)
            {
                Console.WriteLine(Enum.GetName(typeof(Program.appErrors),val) + " : " + (int)val);
            }
        }

        // Global Parameters
        [Global(Description = "Filename to apply the operations (with patchFile specifies the patch filename). Wildcards accepted for multiple files. No filename = search all .xml files in current path.")]
        public static void fileName(
             [Parameter(Required = true)] string filename // xml filename. Wildcards accepted.
            )
        {
            globalFileName = filename;
        }

        [Global(Description = "Element to apply the operation.")]
        public static void element(
             [Parameter(Required = true)] string element
            )
        {
            globalElement = element;
        }

        [Global(Description = "Parent of the Element to apply the operation.")]
        public static void parElement(
             [Parameter(Required = true)] string parentElement
            )
        {
            globalParentElement = parentElement;
        }

        [Global(Description = "Don't make backup of the files modified. Improves the overall program processing performance.")]
        public static void noBackups()
        {
            globalNoBackups = true;
        }

        // Private functions
        private static List<String> getFilesToProcess(String filename)
        {
            List<String> filesToProccess = new List<String>();

            if (String.IsNullOrEmpty(filename)) // No filename? Process everything xml file found.
            {
                List<string> allXmlFiles = Util.getAllXmlFiles();
                foreach (String file in allXmlFiles)
                {
                    filesToProccess.Add(file);
                }
            }
            else if (Util.containsWildcard(filename)) // Contains wildcards? Get all files that match it.
            {
                List<string> matchingWildcardFiles = Util.getXmlFilesWildcard(filename);
                foreach (String file in matchingWildcardFiles)
                {
                    filesToProccess.Add(file);
                }
            }
            else // Add the file specified
            {
                if (System.IO.File.Exists(filename))
                {
                    filesToProccess.Add(filename);
                }
                else
                {
                    Program.printAppError(Program.appErrors.FILE_NOT_FOUND, "The file specified: " + filename + " doesn't exists.", true);
                }
            }
            return filesToProccess;
        }

        private static void printProcessedMessage(int count, string methodName)
        {
            Console.WriteLine(count + " files processed with " + methodName + " command.");
        }

        private static void initialChecks()
        {
            if (String.IsNullOrEmpty(globalElement.Trim()))
            {
                Program.printAppError(Program.appErrors.ELEMENT_NOT_FOUND, "You must specify the element parameter where the operations will be processed.", true);
            }

        }
    }
}
