using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;

namespace Nappat.CsCreator
{
    /// <summary>
    /// This is a main class for creating C# lines.
    /// Its contains main methods divides to logical parts:
    /// - Header,
    /// - HeaderComments,
    /// - Body,
    /// - Footer,
    /// - HelpMethods.
    /// The class request parts gain from ScriptParser.cs.
    /// </summary>
    internal class PartWriter
    {
        #region Private Fields
        private static int indent = 0;
        private static Dictionary<string, int> timeVariables = new Dictionary<string, int>();
        private static List<string> headerComments = new List<string>();
        private static List<string> scriptBody = new List<string>();
        private static List<string> controlSwitchesName = new List<string>();
        private static List<string> outputSensorsName = new List<string>();
        private static List<string> fileCreatorList = new List<string>();
        private long testTime = 0;
        #endregion

        // Indent method return amount of spaces for good-looking  C# code.
        internal static string IndentMethod()
        {
            List<String> tempList = new List<String>();
            for (Int32 i = 0; i < PartWriter.Indent * 4; i++) // 4 = common number of spaces for one indent
            {
                tempList.Add(" ");
            }
            return String.Join(String.Empty, tempList.ToArray());
        }

        #region Internal Properties
        internal static List<string> HeaderComments
        {
            get { return headerComments; }
            set { headerComments = value; }
        }
        internal static List<string> ScriptBody
        {
            get { return scriptBody; }
            set { scriptBody = value; }
        }
        internal static List<string> ControlSwitchesName
        {
            get { return controlSwitchesName; }
            set { controlSwitchesName = value; }
        }
        internal static List<string> OutputSensorsName
        {
            get { return outputSensorsName; }
            set { outputSensorsName = value; }
        }
        internal static int HeadersLength { get; set; }
        internal static Dictionary<string, int> TimeVariables
        {
            get { return timeVariables; }
            set { timeVariables = value; }
        }
        internal static int Indent
        {
            get { return indent; }
            set { indent = value; }
        }
        public static List<string> FileCreatorList
        {
            get { return fileCreatorList; }
            set { fileCreatorList = value; }
        }
        #endregion

        #region Constructors
        internal PartWriter(List<string> headerComment, List<string> scriptBody, List<string> controlSwitchesName, List<string> outputSensorsName, int headersLength)
        {
            HeaderComments = headerComment;
            ScriptBody = scriptBody;
            ControlSwitchesName = controlSwitchesName;
            OutputSensorsName = outputSensorsName;
            HeadersLength = headersLength;
            fileCreatorList.Clear();
        }        
        #endregion

        #region Internal Writer Methods
        internal void HeaderWriter()
        {
            Indent = 0;

            FileCreatorList.Add("using System;");
            FileCreatorList.Add("using System.Collections.Generic;");
            FileCreatorList.Add("using System.Linq;");
            FileCreatorList.Add("using System.Text;");
            FileCreatorList.Add("using System.Threading;");
            FileCreatorList.Add("");
            FileCreatorList.Add("namespace AutomatedTesting");
            FileCreatorList.Add("{");
            Indent++;
            FileCreatorList.Add(IndentMethod() + "public class " + CsCreator.TestName);
            FileCreatorList.Add(IndentMethod() + "{");
            Indent++;
            FileCreatorList.Add(IndentMethod() + "#region Testing Code");
            FileCreatorList.Add(IndentMethod() + "public void Run()");
            FileCreatorList.Add(IndentMethod() + "{");
            Indent++;
            FileCreatorList.Add(IndentMethod() + "tc.LogInfo(" + "\"" + "Starting test, reset..." + "\"" + ");");
            FileCreatorList.Add(IndentMethod() + "tc.StartNewLog(" + "\"" + CsCreator.TestName + "\"" + ");");

            foreach (string pinName in OutputSensorsName)
            { 
                if (pinName != "")
                    FileCreatorList.Add(IndentMethod() + "tc.UnlockInput(" + "\"" + CsCreator.DeviceName + "\"" + ", " + "\"" + pinName + "\"" + ");");
            }

            foreach (string pinName in ControlSwitchesName)
            {
                if (pinName != "")
                    FileCreatorList.Add(IndentMethod() + "tc.DigitalOutputs.Set(" + "\"" + CsCreator.DeviceName + "\"" + ", " + "\"" + pinName + "\"" + ", " + "false);");
            }

            FileCreatorList.Add("");
            FileCreatorList.Add(IndentMethod() + "tc.LogInfo(" + "\"" + "Waiting." + "\");");
            FileCreatorList.Add(IndentMethod() + "Thread.Sleep(" + 1000.ToString() + ");");
            FileCreatorList.Add(IndentMethod() + "tc.LogInfo(" + "\"" + "Test started." + "\");");
        }
        internal void HeaderCommentsWriter()
        {
            FileCreatorList.Add("");
            FileCreatorList.Add(IndentMethod() + "#region HeaderComments;");
            foreach (string line in HeaderComments)
            {
                FileCreatorList.Add(IndentMethod() + "tc.LogInfo(" + "@" + "\"" + line + "\"" + ");");
            }
            FileCreatorList.Add(IndentMethod() + "#endregion;");
        }
        internal void BodyWriter()
        {
            List<string> controlSwitchesSetCurrent = null;
            List<string> controlSwitchesSetPast = null;
            List<string> outputSensorsSetCurrent = null;
            List<string> outputSensorsSetPast = null;
            int waitTime = 0;
            int timeOutTime = 0;
            string lineComment = string.Empty;

            bool label = false;
            int lineCounter = 0;

            FileCreatorList.Add("");
            
            for (int i = 0; i < ScriptBody.Count; i++)
            {
                //information about line for exceptions
                int errReportLine = HeaderComments.Count + HeadersLength + i + 1;

                for (int j = 0; j < ScriptBody[i].Length; j++)
                {
                    //find firs no-white-character within the line 
                    if (ScriptBody[i][j] == ' ')
                        continue;
                    
                    //if line is comment
                    else if (ScriptBody[i][j] == ';')
                    {
                        FileCreatorList.Add(IndentMethod() + "tc.LogInfo(" + "@" + "\"" + ScriptBody[i].Trim() + "\"" + ");");
                        lineCounter++;
                        break;
                    }

                    //if line is pin-setting line
                    else if (ScriptBody[i][j] == '0' || ScriptBody[i][j] == '1')
                    {
                        char[] separators = new char[] { ':', ';' };
                        string[] tempStrings = ScriptBody[i].Split(separators, StringSplitOptions.RemoveEmptyEntries);

                        try
                        {
                            waitTime = PartWriterHelpMethods.TimeSetter(tempStrings[1].Trim());
                            timeOutTime = PartWriterHelpMethods.TimeSetter(tempStrings[3].Trim()) - waitTime;
                            testTime += PartWriterHelpMethods.TimeSetter(tempStrings[3].Trim());
                        }
                        catch (Exception e)
                        {
                            throw new WrongScriptException(e.Message, errReportLine);
                        }

                        //First line recognizer. First line of script cannot be compare find past line.
                        if (!label)
                        {
                            controlSwitchesSetCurrent = new List<string>(PartWriterHelpMethods.PinSetMethod(tempStrings[0]));
                            controlSwitchesSetPast = new List<string>(controlSwitchesSetCurrent.Count);
                            for (int k = 0; k < controlSwitchesSetPast.Capacity; k++)
                            {
                                controlSwitchesSetPast.Add("0");
                            }
                            outputSensorsSetCurrent = new List<string>(PartWriterHelpMethods.PinSetMethod(tempStrings[2]));
                            outputSensorsSetPast = new List<string>(outputSensorsSetCurrent.Count);
                            for (int k = 0; k < outputSensorsSetPast.Capacity; k++)
                            {
                                outputSensorsSetPast.Add("x");
                            }
                            label = true;
                        }
                        else
                        {
                            controlSwitchesSetPast = controlSwitchesSetCurrent;
                            controlSwitchesSetCurrent = PartWriterHelpMethods.PinSetMethod(tempStrings[0]);

                            outputSensorsSetPast = outputSensorsSetCurrent;
                            outputSensorsSetCurrent = PartWriterHelpMethods.PinSetMethod(tempStrings[2]);
                        }
                        lineComment = tempStrings[4].Trim();

                        FileCreatorList.Add(IndentMethod() + "#region " + lineComment);

                        // write out
                        try
                        {
                            FileCreatorList.AddRange(PartWriterHelpMethods.ControlSwitchesWriter(controlSwitchesSetCurrent, controlSwitchesSetPast));
                        }
                        catch (Exception e)
                        {
                            throw new WrongScriptException("Wrong Control Switches array. " + e.Message, errReportLine);
                        }
                        FileCreatorList.Add(IndentMethod() + "tc.LogInfo(" + "@" + "\"" + ScriptBody[i].Trim() + "\"" + ");");
                        FileCreatorList.Add(IndentMethod() + "Thread.Sleep(" + (waitTime * 10).ToString() + ");");

                        // write in
                        try
                        {
                            FileCreatorList.AddRange(PartWriterHelpMethods.OutputSensorsWriter(outputSensorsSetCurrent, outputSensorsSetPast, timeOutTime, lineComment));
                        }
                        catch (Exception e)
                        {
                            throw new WrongScriptException("Wrong Output Sensors array. " + e.Message, errReportLine);
                        }
                        FileCreatorList.Add(IndentMethod() + "#endregion");

                        lineCounter++;
                        break;
                    }
                    //if line is special script command
                    else if (ScriptBody[i][j] == '!')
                    {
                        char[] separators = new char[] { ' ' };
                        string[] tempStrings = ScriptBody[i].Split(separators, StringSplitOptions.RemoveEmptyEntries);

                        if (tempStrings[1].ToUpper() == "GOTO")
                        {
                            FileCreatorList.Add(IndentMethod() + "goto" + " " + tempStrings[2].Trim() + ";");
                        }

                        else if (tempStrings[1].ToUpper() == "LABEL")
                        {
                            Indent--;
                            FileCreatorList.Add(IndentMethod() + tempStrings[2].Trim() + ":");
                            Indent++;
                        }

                        else if (tempStrings[1].ToUpper() == "CYCLE")
                        {
                            string itinName = tempStrings[2].ToLower().Trim() + "itin";

                            FileCreatorList.Add(IndentMethod() + "for (int " + itinName + " = 0; " + itinName + " < " + tempStrings[3].Trim() + "; " + itinName + "++)");
                            FileCreatorList.Add(IndentMethod() + "{");
                            Indent++;
                        }

                        else if (tempStrings[1].ToUpper() == "LOOP")
                        {
                            Indent--;
                            FileCreatorList.Add(IndentMethod() + "}");
                        }

                        else if (tempStrings[1].ToUpper() == "TX")
                        {
                            FileCreatorList.Add(IndentMethod() + "tc.Send(" + "\"" + CsCreator.DeviceName + "\"" + ", " + "\"" + CsCreator.ChannelName + "\"" + ", " + PartWriterHelpMethods.EComMessageBuilderTX(tempStrings, 2) + ", -1);");
                        }

                        else if (tempStrings[1].ToUpper() == "RX")
                        {
                            FileCreatorList.Add(IndentMethod() + "tc.WaitForMessage(" + "\"" + CsCreator.DeviceName + "\"" + ", " + "\"" + CsCreator.ChannelName + "\"" + ", " + PartWriterHelpMethods.EComMessageBuilderRX(tempStrings, 3) + ", " + tempStrings[2] + ");");
                        }

                        else
                            throw new WrongScriptException("Illegal script command.", errReportLine);

                        lineCounter++;
                        break;
                    }
                    //if lines purpose is load configuration file
                    else if (ScriptBody[i][j] == '.')
                    {
                        char[] separators = new char[] { ' ' };
                        string[] tempStrings = ScriptBody[i].Split(separators, StringSplitOptions.RemoveEmptyEntries);

                        if (tempStrings[0].ToUpper() == ".LOAD")
                        {
                            string tcgPath = Path.GetDirectoryName(CsCreator.ScriptPath) + "\\" + tempStrings[1];

                            if (File.Exists(tcgPath))
                            {
                                try
                                {
                                    PartWriterHelpMethods.TCGParser(tcgPath);
                                }
                                catch (Exception e)
                                {
                                    throw new Exception("Wrong configuration file. " + e.Message);
                                }
                                FileCreatorList.Add(IndentMethod() + "tc.LogInfo(" + "\"" + "Loading TCG file: " + tempStrings[1] + "\"" + ");");
                            }
                            else
                                throw new FileNotFoundException(@"Can not load configuration file: " + "'" + tcgPath + "'");
                        }
                        else
                            throw new WrongScriptException("Illegal script command.", errReportLine);
                        lineCounter++;
                        break;
                    }
                }
            }
        }
        internal void FooterWriter()
        {
            Indent--;
            FileCreatorList.Add(IndentMethod() + "}");
            FileCreatorList.Add(IndentMethod() + "#endregion");
        }
        internal void HelpMethodsWriter()
        {
            FileCreatorList.Add(IndentMethod() + "#region Help Methods");
            FileCreatorList.Add(IndentMethod() + "private TestConfigurator tc;");
            FileCreatorList.Add(IndentMethod() + "public string[] messenger = new string[10];");
            FileCreatorList.Add(IndentMethod() + "public event EventHandler NewMessage;");
            FileCreatorList.Add(IndentMethod() + "public long testTime = " + testTime.ToString() + ";");
            FileCreatorList.Add(IndentMethod() + "public bool Init()");
            FileCreatorList.Add(IndentMethod() + "{");
            Indent++;
            FileCreatorList.Add(IndentMethod() + "tc = new TestConfigurator(" + "@\"" + CsCreator.ConfigPath + "\", " + "@\"" + CsCreator.DriverPath + "\\" + "\"," + "false" + ");");
            FileCreatorList.Add(IndentMethod() + "if (!tc.IsHealthy)");
            Indent++;
            FileCreatorList.Add(IndentMethod() + "return false;");
            Indent--;
            FileCreatorList.Add(IndentMethod() + "else");
            FileCreatorList.Add(IndentMethod() + "{");
            Indent++;
            FileCreatorList.Add(IndentMethod() + "tc.LoggerOutput += tc_LoggerOutput;");
            FileCreatorList.Add(IndentMethod() + "return true;");
            Indent--;
            FileCreatorList.Add(IndentMethod() + "}");
            Indent--;
            FileCreatorList.Add(IndentMethod() + "}");
            FileCreatorList.Add(IndentMethod() + "void OnNewMessage()");
            FileCreatorList.Add(IndentMethod() + "{");
            Indent++;
            FileCreatorList.Add(IndentMethod() + "if (NewMessage != null)");
            Indent++;
            FileCreatorList.Add(IndentMethod() + "NewMessage(this, new EventArgs());");
            Indent--;
            Indent--;
            FileCreatorList.Add(IndentMethod() + "}");
            FileCreatorList.Add(IndentMethod() + "void tc_LoggerOutput(MessageContainer Message)");
            FileCreatorList.Add(IndentMethod() + "{");
            Indent++;
            FileCreatorList.Add(IndentMethod() + "OnNewMessage();");
            FileCreatorList.Add(IndentMethod() + "messenger[0] = Message.ToString();");
            FileCreatorList.Add(IndentMethod() + "messenger[1] = Message.Time.ToString();");
            FileCreatorList.Add(IndentMethod() + "messenger[2] = Message.Category.ToString();");
            FileCreatorList.Add(IndentMethod() + "messenger[3] = Message.Severity.ToString();");
            FileCreatorList.Add(IndentMethod() + "messenger[4] = Message.Message.ToString();");
            FileCreatorList.Add(IndentMethod() + "messenger[5] = Message.Data.ToString();");
            FileCreatorList.Add(IndentMethod() + "messenger[6] = Message.CallingMethod.ToString();");
            FileCreatorList.Add(IndentMethod() + "messenger[7] = Message.CallingType.ToString();");
            Indent--;
            FileCreatorList.Add(IndentMethod() + "}");
            FileCreatorList.Add(IndentMethod() + "#endregion");
            Indent--;
            FileCreatorList.Add(IndentMethod() + "}");
            Indent--;
            FileCreatorList.Add("}");
        } 
        #endregion
    }
}
