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="";

        [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);
            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);
            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" +
                "-newVal and another position specified on -valRelation parameter (basically starts with (newVal-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 -newVal \"1 1 1\" -valRelation \"4 4 4\" -valElement Translation -valParentElement" +
                "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);
            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);
            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);
            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);
            List<string> filesToProcess = getFilesToProcess(globalFileName);
            foreach (string currentFile in filesToProcess)
            {
                if (valPositions != null)
                {
                    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 (forceInFiles != "")
            {
                myPatch = new XmlPatch(globalFileName, forceInFiles);
            }
            else
            {
                myPatch = new XmlPatch(globalFileName);
            }

            myPatch.startPatch();
        }

        [Verb]
        public static void version()
        {
            Console.WriteLine("xmlTools v" + Program.toolsVersion);
            Console.WriteLine("\nWritten by s10k");
        }

        // Global Parameters
        [Global]
        public static void fileName(
             [Parameter(Required = true, 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.")] string filename // xml filename. Wildcards accepted.
            )
        {
            globalFileName = filename;
        }

        [Global]
        public static void element(
             [Parameter(Required = true, Description = "Element to apply the operation.")] string element
            )
        {
            globalElement = element;
        }

        [Global]
        public static void parElement(
             [Parameter(Required = true, Description = "Parent of the Element to apply the operation.")] string parentElement
            )
        {
            globalParentElement = parentElement;
        }

        //[PreVerbExecution]
        //private static void InitialChecks(PreVerbExecutionContext context)
        //{
        //    bool elementFound = false;
        //    foreach(ParameterAndValue param in context.Parameters){
        //        if (param.Parameter.Names[0] == "element")
        //        {
        //            if(!String.IsNullOrEmpty(param.Value.ToString().Trim())){
        //                elementFound = true;
        //            }
        //            break;
        //        }
        //    }
        //    if (!elementFound)
        //    {
        //        Console.Error.WriteLine("You must specify the element parameter where the operations will be processed.");
        //        Console.ReadLine();
        //        System.Environment.Exit(1);
        //    }
        //}

        // Private functions
        private static List<String> getFilesToProcess(String filename)
        {
            List<String> filesToProccess = new List<String>();

            if (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);
            }

        }
    }
}
