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

namespace iSTP_DataProcessing
{
    /// <summary>
    /// Provides decimation if it is required.
    /// </summary>
    public class Decimation
    {
        #region --------------------------- Private global values ---------------------------
        private int SampleRate;
        /// <summary>
        /// Holds data from packets
        /// </summary>
        private List<float[]> DataMem = new List<float[]>();
        private long MyCounter = 0;
        /// <summary>
        /// All income packet will be copy according to this number
        /// </summary>
        private int scaleUP;
        /// <summary>
        /// Calculates average from this number of saved packets
        /// </summary>
        private int scaleDOWN;
        private bool firstpacket = true;
        private double Timestamp;
        private object GlobalLocker = new object();
        #endregion

        #region --------------------------- Public global values ---------------------------
        #endregion

        #region --------------------------- Structures&Enums ---------------------------
        #endregion

        #region --------------------------- Construcotrs&Destructor ---------------------------
        /// <summary>
        /// Saves infomration.
        /// </summary>
        /// <param name="freqFIFO">Sample frequency [Hz]</param>
        /// <param name="up">Up scale factor</param>
        /// <param name="down">Down scale factor</param>
        public Decimation(int freqFIFO, int up, int down)
        {
            this.SampleRate = freqFIFO;
            this.scaleUP = up;
            this.scaleDOWN = down;
        }
        #endregion

        #region --------------------------- Private sector ---------------------------
        /// <summary>
        /// Adds data in memory
        /// </summary>
        /// <param name="incomePck">income data packet</param>
        private void AddData2Mem(EmbeddedPacket incomePck)
        {
            float[] _localBuf = new float[incomePck.MsgLength / 4];
            for (int i = 0; i < (incomePck.MsgLength / 4); i++)
            {
                _localBuf[i] = incomePck[i];
            }

            for (int i = 0; i < scaleUP; i++)
            {
                this.DataMem.Add(_localBuf);
            }

            if (firstpacket)
            {
                Timestamp = incomePck.MsgTimeStamp;
                firstpacket = false;
            }

            return;
        }

        /// <summary>
        /// Calculates average for specific element
        /// </summary>
        /// <param name="index">Index of element</param>
        /// <param name="startindex">Starting index</param>
        /// <returns>Final value</returns>
        private float CalcAverageOnIndex(int index, int startindex)
        {
            float _value = 0;

            for (int i = startindex; i < (startindex + scaleDOWN); i++ )
            {
                _value += DataMem[i][index];
            }

            return (_value == 0) ? 0f : (_value / (float)this.scaleDOWN);
        }
        #endregion

        #region --------------------------- Protected sector ---------------------------
        #endregion

        #region --------------------------- Public sector ---------------------------
        /// <summary>
        /// Calculates decimation
        /// </summary>
        /// <param name="incomePck">Current data packet</param>
        /// <param name="dataout">Results</param>
        /// <returns>TRUE - results are ready</returns>
        public bool CalcDecimation(EmbeddedPacket incomePck, out EmbeddedPacket[] dataout)
        {
            bool _res = false;
            dataout = new EmbeddedPacket[0];

            try
            {
                lock (GlobalLocker)
                {
                    this.AddData2Mem(incomePck);
                    this.MyCounter += scaleUP;

                    if (this.MyCounter >= (this.scaleUP * this.SampleRate))
                    {       
                        this.MyCounter = 0;
                        int startindex = 0;

                        if (DataMem.Count >= (this.scaleUP * this.SampleRate))
                        {
                            dataout = new EmbeddedPacket[((this.scaleUP * this.SampleRate) / this.scaleDOWN)];
                            for (int i = 0; i < ((this.scaleUP * this.SampleRate) / this.scaleDOWN); i++)
                            {
                                double timestamp = Timestamp + (double)i / ((this.scaleUP * this.SampleRate) / this.scaleDOWN);
                                dataout[i] = new EmbeddedPacket()
                                {
                                    MsgChecksum = incomePck.MsgChecksum,
                                    MsgID = incomePck.MsgID,
                                    MsgLength = incomePck.MsgLength,
                                    MsgType = incomePck.MsgType,
                                    MsgNumber = incomePck.MsgNumber,
                                    MsgTimeStamp = timestamp,
                                };
                                dataout[i].MsgData = new byte[dataout[i].MsgLength];

                                for (int n = 0; n < incomePck.MsgLength / 4; n++)
                                {
                                    dataout[i][n] = CalcAverageOnIndex(n, startindex);
                                }

                                startindex += this.scaleDOWN;
                            }
                            _res = true;
                        }

                        firstpacket = true;
                        DataMem.Clear();
                    }
                }
            }
            catch (IndexOutOfRangeException ex)
            {
                DataProcessingError.Add("Decimation.CalcDecimation()", "Index out of range", ex.ToString());
            }
            catch (Exception ex)
            {
                DataProcessingError.Add("Decimation.CalcDecimation()", "Exception during decimation process", ex.ToString());
            }

            return _res;
        }

        /// <summary>
        /// Clear internal memory
        /// </summary>
        public void ClearInernalBuffer()
        {
            lock (GlobalLocker)
            {
                DataMem.Clear();
            }
        }
        #endregion

    }
}
