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

namespace Nappat.CsCreator
{
    /// <summary>
    /// This class parses Script and divides it to logical parts.
    /// </summary>
    internal class ScriptParser
    {
        #region Private Fields
        string scriptPath;
        List<string> headers = new List<string>();
        List<char> controlSwitchesList = new List<char>();
        List<char> outputSensorsList = new List<char>();
        int secondStart = 0;
        bool referenceLineBool = true;
        string referenceLine = string.Empty;       
        List<string> headerComments = new List<string>();
        List<string> scriptBody = new List<string>();
        List<string> controlSwitchesName = new List<string>();
        List<string> outputSensorsName = new List<string>();
        #endregion

        #region Internal Properties
        internal List<string> HeaderComments
        {
            get { return headerComments; }
            set { headerComments = value; }
        }
        internal List<string> ScriptBody
        {
            get { return scriptBody; }
            set { scriptBody = value; }
        }
        internal List<string> ControlSwitchesName
        {
            get { return controlSwitchesName; }
            set { controlSwitchesName = value; }
        }
        internal List<string> OutputSensorsName
        {
            get { return outputSensorsName; }
            set { outputSensorsName = value; }
        }
        internal int HeadersLength { get; set; }
        #endregion

        #region Constructors
        internal ScriptParser(string scriptPath)
        {
            this.scriptPath = scriptPath;
            Parsing();
            ActivePinCounting();
            PinNameCreating();
        }
        
        #endregion

        #region Private Methods
        void Parsing()
        {
            if (!File.Exists(scriptPath))
                throw new FileNotFoundException();
            StreamReader reader = File.OpenText(scriptPath);
            string line = null;
            int sectorIdentifier = 0;
            int lineCounter = 0;

            while ((line = reader.ReadLine()) != null)
            {
                lineCounter++;

                if (string.IsNullOrEmpty(line))
                {
                    continue;
                }

                int firstChar = 0;
                for (int i = 0; i < line.Count(); i++)
                {
                    if (line[i] != ' ')
                    {
                        firstChar = i;
                        break;
                    }
                }

                if (line[firstChar] == ';')
                {
                    if (sectorIdentifier == 0)
                    {
                        HeaderComments.Add(line.Trim());
                        continue;
                    }
                    else if (sectorIdentifier == 1)
                    {
                        ScriptBody.Add(line.Trim());
                        continue;
                    }
                }

                else if (line[firstChar] != ';')
                {
                    if ((line[firstChar] == '0' || line[firstChar] == '1' || line[firstChar] == '!' || line[firstChar] == '.') && (sectorIdentifier == 1))
                    {
                        ScriptBody.Add(line);
                        if (referenceLineBool)
                        {
                            if (line[firstChar] == '0' || line[firstChar] == '1')
                            {
                                referenceLine = line;
                                referenceLineBool = false;
                            }
                            else if (line[firstChar] != '.' && line[firstChar] != '!')
                            {
                                throw new WrongScriptException("Wrong line. " + "'" + line[firstChar] + "'" + "is illegal.", lineCounter);
                            }
                        }
                        continue;
                    }
                    else if (char.IsLetter(line[firstChar]))
                    {
                        headers.Add(line);
                        HeadersLength++;
                        sectorIdentifier = 1;
                        continue;
                    }
                    else
                    {
                        throw new WrongScriptException("Wrong line.", lineCounter);
                    }
                }
                else
                {
                    throw new WrongScriptException("Wrong line.", lineCounter);
                }
            }
        }
        void ActivePinCounting()
        {
            char[] separators1 = new char[] { ':', ';' };
            string[] tempStrings = referenceLine.Split(separators1);

            try
            {
                for (int i = 0; i < tempStrings[0].Length; i++)
                {
                    controlSwitchesList.Add(tempStrings[0][i]);
                }

                for (int i = 0; i < tempStrings[2].Length; i++)
                {
                    outputSensorsList.Add(tempStrings[0][i]);
                }
            }
            catch
            {
                throw new WrongScriptException("First line of body probably is not correct.");
            }

            secondStart = tempStrings[0].Length + tempStrings[1].Length + 2; //two-times colon
        }
        void PinNameCreating()
        {
            List<StringBuilder> tempNamesSB = new List<StringBuilder>();

            for (int i = 0; i < headers[1].Length; i++)
            {
                StringBuilder sb = new StringBuilder();
                tempNamesSB.Add(sb);

                for (int j = 0; j < headers.Count; j++)
                {
                    tempNamesSB[tempNamesSB.Count() - 1].Append(headers[j][i]);
                }
            }

            for (int i = 0; i < controlSwitchesList.Count; i++)
            {
                if (controlSwitchesList[i] != ' ' && controlSwitchesList[i] != ':')
                {
                    ControlSwitchesName.Add(tempNamesSB[i].ToString().Trim());
                }
            }

            try
            {
                for (int i = 0; i < outputSensorsList.Count; i++)
                {
                    if (outputSensorsList[i] != ' ' && outputSensorsList[i] != ':')
                    {
                        OutputSensorsName.Add(tempNamesSB[secondStart + i].ToString().Trim());
                    }
                }
            }
            catch
            {
                throw new WrongScriptException("First line of body probably is not correct.");
            }
        } 
        #endregion
    }
}
