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

namespace iSTP_Core
{
    /// <summary>
    /// This class reads CSV test procedure
    /// </summary>
    public class TestProcedure
    {
        #region Private global values
        private const String CSVTimeHdr = "Time (s)";
        private const String CSVClimaStateHdr = "Climatic chamber state (on/off)";
        private const String CSVTemperatureHdr = "Temperature (deg C)";
        private const String CSVTempRateHdr = "Temperature rate (deg C/min)";
        private const String CSVHumidityHdr = "Humidity (%)";
        private const String CSVHumRateHdr = "Humidity rate (%/min)";
        private const String CSVPressureHdr = "Pressure state (on/off)";
        private const String CSVEnableAxisInnerHdr = "AxisInner state (on/off)";
        private const String CSVRotatesEnAxisInnerHdr = "Rotate axisInner (on/off)";
        private const String CSVPosAxisInnerHdr = "Position axisInner (deg)";
        private const String CSVVelAxisInnerHdr = "Velocity axisInner (deg/s)";
        private const String CSVEnableAxisOuterHdr = "AxisOuter state (on/off)";
        private const String CSVRotatesEnAxisOuterHdr = "Rotate axisOuter (on/off)";
        private const String CSVPosAxisOuterHdr = "Position axisOuter (deg)";
        private const String CSVVelAxisOuterHdr = "Velocity axisOuter (deg/s)";
        private const String CSVSensorPwrHdr = "Sensor power (on/off)";
        private const String CSVSensorLogHdr = "Logging state (on/off)";
        private const String CSVPressureStatic = "Static pressure (mbar)";
        private const String CSVPressurePitot = "Pitot pressure (mbar)";

        /// <summary>
        /// End time from loaded test in second
        /// </summary>
        private Int32 TestDuration = 0;
        private Boolean ClimaticChamberRequired = false;
        private Boolean PressureGeneratorRequired = false;
        private Boolean RateTableRequired = false;
        private Boolean TestFileLoaded = false;
        private string CSVtest;

        /// <summary>
        /// List of test commands filled with defined data for test. Index 0 equals time 0 => first step 
        /// </summary>
        private CmdSet[] TestSteps;
        private Dictionary<string, Int32> ColumnsMap;
        private Boolean ParseError = false;

        private TestController Controller;
        #endregion

        #region Public sector
        /// <summary>
        /// parsed-out setpoints from a line in the CSV test procedure
        /// </summary>
        public struct CmdSet
        {
            public Int32 TestTime;
            public DateTime ScheduledTime;
            public Boolean SensorPower;
            public Boolean LoggingReq;
            public Boolean ClimaChamberState;
            public Single Temperature;
            public Single TempRate;
            public Single Humidity;
            public Single HumRate;
            public Boolean PressureState;
            public Single PressureStatic;
            public Single PressurePitot;
            public Boolean EnableAxisInner;
            public Boolean RotateAxisInner;
            public Int32 PosAxisInner;
            public Int32 VelAxisInner;
            public Boolean EnableAxisOuter;
            public Boolean RotateAxisOuter;
            public Int32 PosAxisOuter;
            public Int32 VelAxisOuter;
        }

        /// <summary>
        /// constructor
        /// </summary>
        public TestProcedure()
        {
        
        }

        /// <summary>
        /// destructor
        /// </summary>
        ~TestProcedure() 
        {
            GC.Collect();
        }

        /// <summary>
        /// Set controller
        /// </summary>
        /// <param name="cont"></param>
        public void setController(TestController cont) 
        {
            this.Controller = cont;
        }

        /// <summary>
        /// Gets end time in seconds
        /// </summary>
        public Int32 TotalTime 
        {
            get { return (this.TestDuration); }
        }

        /// <summary>
        /// Get, Set finish time/date
        /// </summary>
        public DateTime TestFinishDateTime
        {
            get;
            set;
        }

        public bool isClimaRequired
        {
            get { return (this.ClimaticChamberRequired);}
        }

        public bool isRateTableRequired
        {
            get { return (this.RateTableRequired); }
        }

        public bool isPressureGeneratorRequired 
        {
            get { return (this.PressureGeneratorRequired); }
        }

        public bool IsEnvironmentControlRequired
        { 
            get { return (this.ClimaticChamberRequired || this.PressureGeneratorRequired || this.RateTableRequired); }
        }

        public string GetCSVtestFileName
        {
            get { return CSVtest; }
        }

        /// <summary>
        /// Parsing loaded user defined test profile
        /// </summary>
        /// <param name="fileName">Profile name</param>
        /// <returns>TRUE - success</returns>
        public bool ParseTestCsv(String fileName) 
        {
            StreamReader csvTestFile;
            List<CmdSet> TestRows = new List<CmdSet>();

            try
            {
                csvTestFile = new StreamReader(fileName);
            }
            catch(Exception ex)
            {
                Console.WriteLine(string.Format("Opening Test CSV: {0}", ex));
                return false;
            }
            this.ParseError = false;

            if (csvTestFile != null)
            {
                Boolean first = true;
                try
                {
                    while (csvTestFile.EndOfStream == false)
                    {
                        String[] columns = csvTestFile.ReadLine().Split(',');
                    
                            if (first)
                            {
                                first = false;
                                ParseHeader(columns);
                                TestRows = new List<CmdSet>();
                            }
                            else
                            {
                                TestRows.Add(ParseRow(columns));
                            }
                    

                    }
                    if (TestRows.Count > 0)
                        TestDuration = TestRows.Last().TestTime;
                }
                catch (Exception ex)
                {
                    Controller.MessageHandler(ex.Message, "Test CSV parse error");
                    ParseError = true;
                }
                csvTestFile.Close();
                CSVtest = fileName;
            }

            if (!this.ParseError)
            {
                this.TestFileLoaded = true;
                TestSteps = TestRows.ToArray();
                TestName = Path.GetFileName(fileName);
            }
            else
                this.TestFileLoaded = false;
            return (!this.ParseError);
        }
        
        /// <summary>
        /// Returns row from test profile
        /// </summary>
        /// <param name="index">INdex</param>
        /// <returns>row from test profile or epmty CmdSet structure</returns>
        public CmdSet GetCmdSet(Int32 index)
        {
            if (index < TestSteps.Length)
            {
                return TestSteps[index];
            }
            else
            {
                CmdSet cmd = new CmdSet();
                return cmd;
            }
        }

        public int Length
        {
            get { return (TestSteps.Length); }
        }

        public bool TestLoaded
        { get { return TestFileLoaded; } }
        #endregion

        #region Private sector
        /// <summary>
        /// Check all headers
        /// </summary>
        /// <param name="columns">Columns from test profile</param>
        private void ParseHeader(String[] columns)
        {
            string[] validHeaders = {TestProcedure.CSVTimeHdr,
                                     TestProcedure.CSVClimaStateHdr,
                                     TestProcedure.CSVTemperatureHdr,
                                     TestProcedure.CSVTempRateHdr,
                                     TestProcedure.CSVHumidityHdr,
                                     TestProcedure.CSVHumRateHdr,
                                     TestProcedure.CSVPressureHdr,
                                     TestProcedure.CSVEnableAxisInnerHdr,
                                     TestProcedure.CSVRotatesEnAxisInnerHdr,
                                     TestProcedure.CSVPosAxisInnerHdr,
                                     TestProcedure.CSVVelAxisInnerHdr,
                                     TestProcedure.CSVEnableAxisOuterHdr,
                                     TestProcedure.CSVRotatesEnAxisOuterHdr,
                                     TestProcedure.CSVPosAxisOuterHdr,
                                     TestProcedure.CSVVelAxisOuterHdr,
                                     TestProcedure.CSVSensorPwrHdr,
                                     TestProcedure.CSVSensorLogHdr,
                                     TestProcedure.CSVPressurePitot,
                                     TestProcedure.CSVPressureHdr,
                                     TestProcedure.CSVPressureStatic};

            ColumnsMap = new Dictionary<string, Int32>();

            for (Int32 i = 0; i < columns.Length; i++)
            {
                if (!validHeaders.Contains(columns[i]))
                {
                    this.ParseError = true;
                    Controller.MessageHandler("Invalid CSV header format", "Test CSV parse error");
                    //throw new Exception("Invalid CSV format");
                }
                ColumnsMap.Add(columns[i], i);
            }
            ClimaticChamberRequired = ColumnsMap.ContainsKey(TestProcedure.CSVClimaStateHdr);
            PressureGeneratorRequired = ColumnsMap.ContainsKey(TestProcedure.CSVPressureHdr);
            RateTableRequired = ColumnsMap.ContainsKey(TestProcedure.CSVEnableAxisInnerHdr);
        }

        /// <summary>
        /// Parse each row
        /// </summary>
        /// <param name="columns">Columns from test profile</param>
        /// <returns>Struct CmdSet filled with information from each row</returns>
        private CmdSet ParseRow(String[] columns)
        {
            CmdSet CSVRowCmdSet = new CmdSet();
            try
            {
                CSVRowCmdSet.TestTime = Convert.ToInt32(columns[ColumnsMap[TestProcedure.CSVTimeHdr]]);
                CSVRowCmdSet.SensorPower = (Convert.ToInt32(columns[ColumnsMap[TestProcedure.CSVSensorPwrHdr]]) == 1 ? true : false);
                CSVRowCmdSet.LoggingReq = (Convert.ToInt32(columns[ColumnsMap[TestProcedure.CSVSensorLogHdr]]) == 1 ? true : false);

                if (ClimaticChamberRequired)
                {
                    try
                    {
                        CSVRowCmdSet.ClimaChamberState = (Convert.ToInt32(columns[ColumnsMap[TestProcedure.CSVClimaStateHdr]]) == 1 ? true : false);
                        CSVRowCmdSet.Humidity = Convert.ToSingle(columns[ColumnsMap[TestProcedure.CSVHumidityHdr]]);
                        CSVRowCmdSet.Temperature = Convert.ToSingle(columns[ColumnsMap[TestProcedure.CSVTemperatureHdr]]);
                        CSVRowCmdSet.HumRate = Convert.ToSingle(columns[ColumnsMap[TestProcedure.CSVHumRateHdr]]);
                        CSVRowCmdSet.TempRate = Convert.ToSingle(columns[ColumnsMap[TestProcedure.CSVTempRateHdr]]);
                    }
                    catch
                    {

                        throw(new Exception("Missing Climatic Chamber commands (status, temperature, humidity, rates)."));
                    }
                }
                if (RateTableRequired)
                {
                    try
                    {
                        CSVRowCmdSet.EnableAxisInner = (Convert.ToInt32(columns[ColumnsMap[TestProcedure.CSVEnableAxisInnerHdr]]) == 1 ? true : false);
                        CSVRowCmdSet.RotateAxisInner = (Convert.ToInt32(columns[ColumnsMap[TestProcedure.CSVRotatesEnAxisInnerHdr]]) == 1 ? true : false);
                        CSVRowCmdSet.PosAxisInner = Convert.ToInt32(columns[ColumnsMap[TestProcedure.CSVPosAxisInnerHdr]]);
                        CSVRowCmdSet.VelAxisInner = Convert.ToInt32(columns[ColumnsMap[TestProcedure.CSVVelAxisInnerHdr]]);
                        CSVRowCmdSet.EnableAxisOuter = (Convert.ToInt32(columns[ColumnsMap[TestProcedure.CSVEnableAxisOuterHdr]]) == 1 ? true : false);
                        CSVRowCmdSet.RotateAxisOuter = (Convert.ToInt32(columns[ColumnsMap[TestProcedure.CSVRotatesEnAxisOuterHdr]]) == 1 ? true : false);
                        CSVRowCmdSet.PosAxisOuter = Convert.ToInt32(columns[ColumnsMap[TestProcedure.CSVPosAxisOuterHdr]]);
                        CSVRowCmdSet.VelAxisOuter = Convert.ToInt32(columns[ColumnsMap[TestProcedure.CSVVelAxisOuterHdr]]);
                        CSVRowCmdSet.Temperature = Convert.ToSingle(columns[ColumnsMap[TestProcedure.CSVTemperatureHdr]]);
                        CSVRowCmdSet.ClimaChamberState = (Convert.ToInt32(columns[ColumnsMap[TestProcedure.CSVClimaStateHdr]]) == 1 ? true : false);
                        CSVRowCmdSet.Humidity = Convert.ToSingle(columns[ColumnsMap[TestProcedure.CSVHumidityHdr]]);
                    }
                    catch(Exception ex)
                    {
                        Console.WriteLine(ex.ToString());
                        throw (new Exception("Missing Rate-table commands (state of axis and rotation, rate, position, climate state, temperature, humidity)."));
                    }
                }
                if (PressureGeneratorRequired)
                {
                    try
                    {
                        CSVRowCmdSet.PressureState = (Convert.ToInt32(columns[ColumnsMap[TestProcedure.CSVPressureHdr]]) == 1 ? true : false);
                        CSVRowCmdSet.PressureStatic = Convert.ToSingle(columns[ColumnsMap[TestProcedure.CSVPressureStatic]]);
                        CSVRowCmdSet.PressurePitot = Convert.ToSingle(columns[ColumnsMap[TestProcedure.CSVPressurePitot]]);
                    }
                    catch (Exception ex)
                    {
                        Console.WriteLine(ex.ToString());
                        throw (new Exception("Missing pressure commands (Static - 'Static pressure (mbar)', Pitot - 'Pitot pressure (mbar)')."));
                    }
                }
            }
            catch (Exception ex)
            {
                this.ParseError = true;
                throw (ex);
            }
            return CSVRowCmdSet;
        }
        #endregion

        public string TestName { get; private set; }

        public void UpdateScheduledTimes()
        {
            DateTime now = DateTime.Now;
            for (int i = 0; i < TestSteps.Length; i++)
            {
                TestSteps[i].ScheduledTime = now + TimeSpan.FromSeconds(TestSteps[i].TestTime + 1); // adding 1 due to a delay before first "elapsed" event of the test
            }                                                                                       // timer which drives the test execution. Same with the finish time below.

            TestFinishDateTime = now + TimeSpan.FromSeconds(TestDuration + 1);
        }

        public string[] GetHeader_Platform()
        {
            return new string[] { CSVTimeHdr, CSVSensorPwrHdr, CSVSensorLogHdr };
        }

        public string[] GetHeader_ClimChamber()
        {
            return new string[] { CSVClimaStateHdr, CSVTemperatureHdr, CSVTempRateHdr, CSVHumidityHdr, CSVHumRateHdr };
        }

        public string[] GetHeader_RateTable()
        {
            return new string[] {CSVEnableAxisInnerHdr, CSVRotatesEnAxisInnerHdr, CSVPosAxisInnerHdr, CSVVelAxisInnerHdr, CSVEnableAxisOuterHdr, 
                                CSVRotatesEnAxisOuterHdr, CSVPosAxisOuterHdr, CSVVelAxisOuterHdr, CSVTemperatureHdr, CSVClimaStateHdr, CSVHumidityHdr};
        }

        public string[] GetHeader_PressGen()
        {
            return new string[] { CSVPressureHdr, CSVPressureStatic, CSVPressurePitot };
        }
    }
}
