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

namespace iSTP_DataProcessing
{
    /// <summary>
    /// Holds current health data from all pheripheries. Sets up Global and local enviromental log.
    /// </summary>
    public class HealthData
    {
        private List<string> FromISTP;
        private List<string> FromClimCham;
        private List<string> FromRatetable;
        private List<string> FromPressure;

        private bool isISTPallow;
        private bool isCCallow;
        private bool isRTallow;
        private bool isPRESallow;
 
        private const string Filename = "EnviroLog";

        private List<float> Data;

        private int IndexCC = 0;
        private int IndexRT = 0;
        private int IndexPR = 0;
        private int IndexISTP = 0;

        private double Timestamp;
        private double TimestampDiff;

        private HoneyxWriter EnviAllhx;
        private CSVwriter EnviAllcsv;
        private HoneyxWriter Envihx;
        private CSVwriter Envicsv;

        private object GlobalLocker;

        private double GlobalTimestamp;

        /// <summary>
        /// Allocate internal memory for each allow peripherial
        /// </summary>
        /// <param name="istp">Health from ISTP</param>
        /// <param name="clim">Climatic chamber status</param>
        /// <param name="ratetab">Rate table status</param>
        /// <param name="press">Pressure controller status</param>
        public HealthData(bool istp, bool clim, bool ratetab, bool press)
        {
            GlobalLocker = new object();

            isISTPallow = istp;
            if (isISTPallow)
                FromISTP = new List<string>();
            isCCallow = clim;
            if (isCCallow)
                FromClimCham = new List<string>();
            isRTallow = ratetab;
            if (isRTallow)
                FromRatetable = new List<string>();
            isPRESallow = press;
            if (isPRESallow)
                FromPressure = new List<string>();

            Data = new List<float>();
            GlobalTimestamp = 0d;
            TimestampDiff = 0d;

            Initialize();
        }

        /// <summary>
        /// Clear memories. Free memory
        /// </summary>
        ~HealthData()
        {
            if (FromISTP != null)
            {
                FromISTP.Clear();
                FromISTP = null;
            }
            if (FromClimCham != null)
            {
                FromClimCham.Clear();
                FromClimCham = null;
            }
            if (FromRatetable != null)
            {
                FromRatetable.Clear();
                FromRatetable = null;
            }
            if (FromPressure != null)
            {
                FromPressure.Clear();
                FromPressure = null;
            }
        }

        /// <summary>
        /// Saves all headers from allow peripherials
        /// </summary>
        private void Initialize()
        {
            if (isCCallow)
            {
                FromClimCham.Clear();
                FromClimCham.Add("Climatic_chamber_state");
                FromClimCham.Add("Temperature_requested");
                FromClimCham.Add("Temperature_actual");
                FromClimCham.Add("Humidity_requested");
                FromClimCham.Add("Humidity_actual");
                IndexCC = 0;
                IndexISTP = IndexCC + 5;
            }

            if (isRTallow)
            {
                FromRatetable.Clear();
                FromRatetable.Add("AxisInner_state");
                FromRatetable.Add("AxisInner_req_position");
                FromRatetable.Add("AxisInner_req_velocity");
                FromRatetable.Add("AxisInner_act_position");
                FromRatetable.Add("AxisInner_act_velocity");
                FromRatetable.Add("AxisOuter_state");
                FromRatetable.Add("AxisOuter_req_position");
                FromRatetable.Add("AxisOuter_req_velocity");
                FromRatetable.Add("AxisOuter_act_position");
                FromRatetable.Add("AxisOuter_act_velocity");

                if (IndexCC != 0)
                    IndexRT = IndexCC + 5;
                else
                    IndexRT = 0;

                IndexISTP = IndexRT + 10;
            }

            if (isPRESallow)
            {
                FromPressure.Clear();
                FromPressure.Add("Pressure_state");
                FromPressure.Add("Pressure_Static_Requested");
                FromPressure.Add("Pressure_Static_Actual");
                FromPressure.Add("Pressure_Pitot_Requested");
                FromPressure.Add("Pressure_Pitot_Actual");
                if (IndexCC == 0 && IndexRT == 0)
                    IndexPR = 0;
                else if (IndexCC != 0 && IndexRT == 0)
                    IndexPR = IndexCC + 5;
                else
                    IndexPR = IndexRT + 10;

                IndexISTP = IndexPR + 5;
            }
        }

        /// <summary>
        /// Creates main enviromental logs in root folder
        /// </summary>
        /// <param name="rootdest">Full path to root folder</param>
        private void OpenMainEnvi(string rootdest)
        {
            try 
            {
                lock (GlobalLocker)
                {
                    string headers = "Time[s] ";
                    int column = 0;

                    if (FromClimCham != null && FromClimCham.Count > 0)
                        if (FromClimCham.Count > 0)
                        {
                            headers += string.Join(" ", FromClimCham.ToArray());
                            column += FromClimCham.Count;
                        }

                    if (FromRatetable != null && FromRatetable.Count > 0)
                        if (FromRatetable.Count > 0)
                        {
                            headers += string.Join(" ", FromRatetable.ToArray());
                            column += FromRatetable.Count;
                        }

                    if (FromPressure != null && FromPressure.Count > 0)
                        if (FromPressure.Count > 0)
                        {
                            headers += string.Join(" ", FromPressure.ToArray());
                            column += FromPressure.Count;
                        }

                    if (FromISTP != null && FromISTP.Count > 0)
                        if (FromISTP.Count > 0)
                        {
                            headers += string.Join(" ", FromISTP.ToArray());
                            column += FromISTP.Count;
                        }

                    EnviAllhx = new HoneyxWriter(string.Format("{0}{1}{2}.hx", rootdest, Path.DirectorySeparatorChar, Filename));
                    EnviAllhx.CreateFileWithLabels(HoneyxWriter.HoneyXIO_doubleTimeSingleData, 1, column, headers);

                    EnviAllcsv = new CSVwriter(new FileStream(string.Format("{0}{1}{2}.csv", rootdest, Path.DirectorySeparatorChar, Filename), FileMode.Create, FileAccess.Write));
                    foreach (string s in headers.Split(' '))
                        EnviAllcsv.WriteValue(s);
                    EnviAllcsv.WriteNewLine();
                }
            }
            catch (Exception ex)
            {
                DataProcessingError.Add("HealthData.cs", "Can't create global Enviromental log", ex.ToString());
            }
        }

        /// <summary>
        /// Close main enviromental logs 
        /// </summary>
        private void CloseMainEnvi()
        {
            lock (GlobalLocker)
            {
                if (EnviAllhx != null)
                {
                    EnviAllhx.CloseFile();
                    EnviAllhx = null;
                }

                if (EnviAllcsv != null)
                {
                    EnviAllcsv.BaseStream.Close();
                    EnviAllcsv = null;
                }
            }
        }

        /// <summary>
        /// Creates cycle enviromental logs input path
        /// </summary>
        /// <param name="cycledest">Path to cycle folder</param>
        private void OpenCycleEnvi(string cycledest)
        {
            try
            {
                lock (GlobalLocker)
                {
                    string headers = "Time[s] ";
                    int column = 0;

                    if (FromClimCham != null && FromClimCham.Count > 0)
                        if (FromClimCham.Count > 0)
                        {
                            headers += string.Join(" ", FromClimCham.ToArray());
                            column += FromClimCham.Count;
                        }

                    if (FromRatetable != null && FromRatetable.Count > 0)
                        if (FromRatetable.Count > 0)
                        {
                            headers += string.Join(" ", FromRatetable.ToArray());
                            column += FromRatetable.Count;
                        }

                    if (FromPressure != null && FromPressure.Count > 0)
                        if (FromPressure.Count > 0)
                        {
                            headers += string.Join(" ", FromPressure.ToArray());
                            column += FromPressure.Count;
                        }

                    if (FromISTP != null && FromISTP.Count > 0)
                        if (FromISTP.Count > 0)
                        {
                            headers += string.Join(" ", FromISTP.ToArray());
                            column += FromISTP.Count;
                        }

                    Envihx = new HoneyxWriter(string.Format("{0}{1}{2}.hx", cycledest, Path.DirectorySeparatorChar, Filename));
                    Envihx.CreateFileWithLabels(HoneyxWriter.HoneyXIO_doubleTimeSingleData, 1, column, headers);

                    Envicsv = new CSVwriter(new FileStream(string.Format("{0}{1}{2}.csv", cycledest, Path.DirectorySeparatorChar, Filename), FileMode.Create, FileAccess.Write));
                    foreach (string s in headers.Split(' '))
                        Envicsv.WriteValue(s);
                    Envicsv.WriteNewLine();
                }
            }
            catch (Exception ex)
            {
                DataProcessingError.Add("HealthData.cs", "Can't create cycle Enviromental log", ex.ToString());
            }
        }

        /// <summary>
        /// Close cycle enviromental logs
        /// </summary>
        private void CloseCycleEnvi()
        {
            lock (GlobalLocker)
            {
                if (Envihx != null)
                {
                    Envihx.CloseFile();
                    Envihx = null;
                }

                if (Envicsv != null)
                {
                    Envicsv.BaseStream.Close();
                    Envicsv = null;
                }
            }
        }

        /// <summary>
        /// Writes all saved data into logs (main and cycle)
        /// </summary>
        private void WriteAll()
        {
            lock (GlobalLocker)
            {
                GlobalTimestamp += Convert.ToDouble(1);
                if (EnviAllhx != null)
                {
                    EnviAllhx.WriteLine(GlobalTimestamp, Data.ToArray());
                }

                if (EnviAllcsv != null)
                {
                    EnviAllcsv.WriteValue(GlobalTimestamp.ToString());
                    foreach (float f in Data)
                        EnviAllcsv.WriteValue(f.ToString());
                    EnviAllcsv.WriteNewLine();
                }

                if (Envihx != null)
                    Envihx.WriteLine(Timestamp, Data.ToArray());

                if (Envicsv != null)
                {
                    Envicsv.WriteValue(Timestamp.ToString());
                    foreach (float f in Data)
                        Envicsv.WriteValue(f.ToString());
                    Envicsv.WriteNewLine();
                }
                Data.Clear();
                Timestamp = 0;
                TimestampDiff = 0;
            }
        }

        /// <summary>
        /// Saves actual data from climatic chamber
        /// </summary>
        /// <param name="state">Power state</param>
        /// <param name="reqtemp">Temperature setpoint</param>
        /// <param name="acttemp">Actual temperature</param>
        /// <param name="reqhumi">Humidity setpoint</param>
        /// <param name="acthumi">Actual humidity</param>
        public void AddClimChamData(bool state, float reqtemp, float acttemp, float reqhumi, float acthumi)
        {
            lock (GlobalLocker)
            {
                if (isCCallow)
                {
                    Data.Insert(IndexCC, (state) ? 1f : 0f);
                    Data.Insert(IndexCC + 1, reqtemp);
                    Data.Insert(IndexCC + 2, acttemp);
                    Data.Insert(IndexCC + 3, reqhumi);
                    Data.Insert(IndexCC + 4, acthumi);
                }
            }
        }

        /// <summary>
        /// Saves actual data from rate table
        /// </summary>
        /// <param name="stateInner">State of inner axis</param>
        /// <param name="reqInnerpos">Position setpoint for inner axis</param>
        /// <param name="reqInnervel">Velocity setpoint for inner axis</param>
        /// <param name="actInnerpos">Actual position from inner axis</param>
        /// <param name="actInnervel">Actual velocity from inner axis</param>
        /// <param name="stateOuter">State of outer axis</param>
        /// <param name="reqOuterpos">Position setpoint for outer axis</param>
        /// <param name="reqOutervel">Velocity setpoint for outer axis</param>
        /// <param name="actOuterpos">Actual position from outer axis</param>
        /// <param name="actOutervel">Actual position from outer axis</param>
        public void AddRatetableData
            (
                bool stateInner,
                float reqInnerpos,
                float reqInnervel,
                float actInnerpos,
                float actInnervel,
                bool stateOuter,
                float reqOuterpos,
                float reqOutervel,
                float actOuterpos,
                float actOutervel
            )
        {
            lock (GlobalLocker)
            {
                if (isRTallow)
                {
                    Data.Insert(IndexRT, (stateInner) ? 1f : 0f);
                    Data.Insert(IndexRT + 1, reqInnerpos);
                    Data.Insert(IndexRT + 2, reqInnervel);
                    Data.Insert(IndexRT + 3, actInnerpos);
                    Data.Insert(IndexRT + 4, actInnervel);
                    Data.Insert(IndexRT + 5, (stateOuter) ? 1f : 0f);
                    Data.Insert(IndexRT + 6, reqOuterpos);
                    Data.Insert(IndexRT + 7, reqOutervel);
                    Data.Insert(IndexRT + 8, actOuterpos);
                    Data.Insert(IndexRT + 9, actOutervel);
                }
            }
        }

        /// <summary>
        /// Saves actual data from pressure controller
        /// </summary>
        /// <param name="state">State of pressure controller</param>
        /// <param name="reqpress">Pressure setpoint</param>
        /// <param name="actpress">Actual pressure</param>
        public void AddPressureData(bool state, float reqstatic, float actstatic, float reqpitot, float actpitot)
        {
            if (isPRESallow)
            {
                Data.Insert(IndexPR, (state) ? 1f : 0f);
                Data.Insert(IndexPR + 1, reqstatic);
                Data.Insert(IndexPR + 2, actstatic);
                Data.Insert(IndexPR + 3, reqpitot);
                Data.Insert(IndexPR + 4, actpitot);
            }
        }

        /// <summary>
        /// Saves health data from ISTP
        /// </summary>
        /// <param name="time">Embedded time</param>
        /// <param name="data">Health data</param>
        public void AddHealthData(double time, float[] data)
        {
            lock (GlobalLocker)
            {
                if (isISTPallow)
                {
                    TimestampDiff = time - Timestamp;
                    Timestamp = time;

                    for (int i = 0; i < data.Length; i++)
                    {
                        Data.Insert(IndexISTP + i, data[i]);
                    }
                }
            }
        }

        /// <summary>
        /// saves timestamp
        /// </summary>
        /// <param name="time">Timestamp</param>
        public void AddTimestamp(double time)
        {
            lock (GlobalLocker)
            {
                TimestampDiff = time - Timestamp;
                Timestamp = time;
            }
        }

        /// <summary>
        /// Creates main enviromental logs in root path
        /// </summary>
        /// <param name="cycledest">Path to root folder</param>
        public void CreateMainEnvi(string destination)
        {
            OpenMainEnvi(destination);
        }

        /// <summary>
        /// Creates cycle enviromental logs input path
        /// </summary>
        /// <param name="cycledest">Path to cycle folder</param>
        public void CreateCycleEnvi(string destination)
        {
            OpenCycleEnvi(destination);
        }

        /// <summary>
        /// Close main enviromental logs
        /// </summary>
        public void CloseMainEnvies()
        {
            CloseMainEnvi();
        }

        /// <summary>
        /// Close cycle enviromental logs
        /// </summary>
        public void CloseCycleEnvies()
        {
            CloseCycleEnvi();
        }

        /// <summary>
        /// Writes all saved data into logs and clear internal buffer
        /// </summary>
        public void WriteEnviData()
        {
            WriteAll();
        }

        /// <summary>
        /// Saved header from ISTP
        /// </summary>
        /// <param name="headers"></param>
        public void AddISTPHeader(string[] headers)
        {
            lock (GlobalLocker)
            {
                FromISTP.Clear();
                foreach (string s in headers)
                    FromISTP.Add(s);
            }
        }
    }
}
