/*******************************************************************************
* Copyright (C) 2016 Maxim Integrated Products, Inc., All rights Reserved.
* 
* This software is protected by copyright laws of the United States and
* of foreign countries. This material may also be protected by patent laws
* and technology transfer regulations of the United States and of foreign
* countries. This software is furnished under a license agreement and/or a
* nondisclosure agreement and may only be used or reproduced in accordance
* with the terms of those agreements. Dissemination of this information to
* any party or parties not specified in the license agreement and/or
* nondisclosure agreement is expressly prohibited.
*
* The above copyright notice and this permission notice shall be included
* in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
* OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
* IN NO EVENT SHALL MAXIM INTEGRATED BE LIABLE FOR ANY CLAIM, DAMAGES
* OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
* ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
* OTHER DEALINGS IN THE SOFTWARE.
*
* Except as contained in this notice, the name of Maxim Integrated
* Products, Inc. shall not be used except as stated in the Maxim Integrated
* Products, Inc. Branding Policy.
*
* The mere transfer of this software does not imply any licenses
* of trade secrets, proprietary technology, copyrights, patents,
* trademarks, maskwork rights, or any other form of intellectual
* property whatsoever. Maxim Integrated Products, Inc. retains all
* ownership rights.
*******************************************************************************
*/

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

namespace HealthSensorPlatform.Presenter
{
    /// <summary>
    /// R To R Calculator for referring R to R code values to ECG samples
    /// </summary>
    public class RToRCalculator
    {
        const int RToROffset = 3;

        double masterClockFrequency;
        int rToRDifferentialDelay;

        int ecgDelay;
        int rToRDelay;

        double sampleRateRToR;
        double sampleRateEcg;

        double rToRTimeMilliSecond;

        /// <summary>
        /// Error difference between rounded R To R in ECG samples and actual calculated value. Error used
        /// in calculation of next R To R in ECG samples point.
        /// </summary>
        double ecgSampleError;

        /// <summary>
        /// FMSTR clock frequency. Array index by FMSTR[1:0]
        /// </summary>
        double[] clockFreq = { 32768, 32000, 32000, 32768 * 640 / 656 }; // 31968.7804878 = 32768 * 640 / 65
        /// <summary>
        /// ECG decimation delay. Array indexed by FMSTR, ECG_RATE, ECG_DLFF (0 for disabled, 1 for enabled) 
        /// </summary>
        int[, ,] ecgDecimationDelay = new int[,,] { {{650, 1034}, {2922, 3690}, {3370, 4906}, {3370, 4906}}, 
													{{650, 1034}, {2922, 3690}, {3370, 4906}, {3370, 4906}},
													{{1242, 2202}, {1242, 2202}, {1242, 2202}, {1242, 2202}},
													{{1242, 2202}, {1242, 2202}, {1242, 2202}, {1242, 2202}}
                                                  };

        /// <summary>
        /// ECG sample rate look up based on FMSTR and ECG_RATE
        /// </summary>
        double[][] ecgSampleRates = new double[][]
        {
                new double[] {512, 256, 128, 128},
                new double[] {500, 250, 125, 125},
                new double[] {200, 200, 200, 200},
                new double[] {199.8, 199.8, 199.8, 199.8} 
        };

        /// <summary>
        /// ECG processing delay in FMSTR clocks
        /// </summary>
        public int EcgDecimationDelay
        {
            get
            {
                return ecgDelay;
            }
        }

        /// <summary>
        /// R To R path processing delay in FMSTR clocks
        /// </summary>
        public int RToRDelay
        {
            get
            {
                return rToRDelay;
            }
        }

        /// <summary>
        /// Last R To R detected time in millisecond from first ECG point
        /// </summary>
        public double RToRMilliSecond
        {
            get
            {
                return rToRTimeMilliSecond;
            }
        }
        
        /// <summary>
        /// Constructor for R to R calculator
        /// </summary>
        /// <param name="masterClockFrequencyField">Value of FMSTR[1:0]</param>
        /// <param name="sampleRateEcgField">Value of ECG_RATE[1:0]</param>
        /// <param name="ecgDigitalLowPass">Value of ECG_DLF[1:0]</param>
        /// <param name="rTorWindowField">Value of RTOR_WNDW[3:0]</param>
        public RToRCalculator(int masterClockFrequencyField, int sampleRateEcgField, int ecgDigitalLowPass, int rTorWindowField)
        {
            masterClockFrequency = clockFreq[masterClockFrequencyField];
            sampleRateRToR = masterClockFrequency / 256;
            sampleRateEcg = ecgSampleRates[masterClockFrequencyField][sampleRateEcgField];

            ecgDelay = ecgDecimationDelay[masterClockFrequencyField, sampleRateEcgField, ecgDigitalLowPass > 0 ? 1 : 0];
            rToRDelay = 5376 + 3370 + 256 * rTorWindowField;

            rToRDifferentialDelay = rToRDelay - ecgDelay;

            rToRTimeMilliSecond = 0;
        }

        /// <summary>
        /// R To R code to beats per minute
        /// </summary>
        /// <param name="rToRCode">R To R Code</param>
        /// <returns></returns>
        public double BeatsPerMinute(int rToRCode)
        {
            return 60 * 1000 / Millisecond(rToRCode);
        }

        /// <summary>
        /// R To R code to millisecond interval
        /// </summary>
        /// <param name="rToRCode">R To R Code</param>
        /// <returns></returns>
        public double Millisecond(double rToRCode)
        {
            return rToRCode * 512.0 * 1000 / (2 * masterClockFrequency);
        }

        public double Corrected(int rToR, bool first)
        {
            if (first)
                rToRTimeMilliSecond = Millisecond(rToR - rToRDifferentialDelay / 256.0);
            else
                rToRTimeMilliSecond += Millisecond(rToR);

            return rToRTimeMilliSecond;
        }

        /// <summary>
        /// R To R code converted to ECG points
        /// </summary>
        /// <param name="rToR">R To R code</param>
        /// <param name="first">first R To R sample include the processing delay between the ECG and R To R processing path</param>
        /// <returns></returns>
        public int EcgPoints(int rToR, bool first)
        {
            double rToRInEcgSamples;
            int rToRInEcgSamplesInt;

            if (first)
                rToRInEcgSamples = (rToR - rToRDifferentialDelay / 256.0) * (sampleRateEcg / sampleRateRToR) + RToROffset;
            else
                rToRInEcgSamples = rToR * (sampleRateEcg / sampleRateRToR) - ecgSampleError;

            rToRInEcgSamplesInt = (int)(rToRInEcgSamples + 0.5);
            ecgSampleError = rToRInEcgSamplesInt - rToRInEcgSamples;

            return rToRInEcgSamplesInt;
        }
    }
}
