////////////////////////////////////////////////////////////////////////////////
// Dynastream Innovations Inc.
// Cochrane, AB, CANADA
//
// Copyright � 2014 Dynastream Innovations Inc.
// All rights reserved. This software may not be reproduced by
// any means without express written approval of Dynastream
// Innovations Inc.
// The software is being provided on an "as-is" basis and as an accommodation,
// and therefore all warranties, representations, or guarantees of any kind
// (whether express, implied or statutory) including, without limitation,
// warranties of merchantability, non-infringement, or fitness for a particular
// purpose, are specifically disclaimed.
//
////////////////////////////////////////////////////////////////////////////////

#include "string.h"
#include "stdbool.h"
#include "stdint.h"
#include "stdlib.h"
#include "math.h"

#include "DecodeCrankTorque.h"
#include "DecodeCrankTorqueFrequency.h"
#include "DecodePowerOnly.h"
#include "DecodeWheelTorque.h"
#include "PowerDecoder.h"

static PowerRecordReceiver prrPtr;
static unsigned char ucPowerMeterChannel = 255;
static unsigned char ucPowerMeterType = 255;
static bool bResyncPowerChannel = true;
static bool bResyncPowerOnlyChannel = true;
static unsigned char ucPowerOnlyEventCount = 255;
static double dPowerOnlyBundleRxTime = -1;
static unsigned char ucNewPowerOnlyEventCount;

void InitPowerDecoder(double dRecordInterval_, double dTimeBase_, double dReSyncInterval_, PowerRecordReceiver powerRecordReceiverPtr_)
{
    prrPtr = powerRecordReceiverPtr_;
    DecodePowerOnly_Init(dRecordInterval_, dTimeBase_, dReSyncInterval_, prrPtr);
    DecodeCrankTorque_Init(dRecordInterval_, dTimeBase_, dReSyncInterval_, prrPtr);
    DecodeCrankTorqueFreq_Init(dRecordInterval_, dTimeBase_, dReSyncInterval_, prrPtr);
    DecodeWheelTorque_Init(dRecordInterval_, dTimeBase_, dReSyncInterval_, prrPtr);
}

// 16 = Power Only, 17 = Wheel Torque, 18 = Crank Torque, 32 = Crank Torque Frequency, 255 = Unknown
void SetPowerMeterType(unsigned char ucPowerMeterType_)
{
    ucPowerMeterType = ucPowerMeterType_;
}

void DecodePowerMessage(double dRxTime_, unsigned char messagePayload_[8])
{
    // Initialize the received time for power only event count bundled messages or
    // if the received times differ greatly (we may have missed messages beyond the event count rollover)
    if (dPowerOnlyBundleRxTime < 0 || (dRxTime_ - dPowerOnlyBundleRxTime) > 30)
        dPowerOnlyBundleRxTime = dRxTime_;

    // do page decoding against the expected power pages.
    switch (messagePayload_[0])
    {
        case ANT_POWERONLY:
            ucNewPowerOnlyEventCount = messagePayload_[1];

            if (ucNewPowerOnlyEventCount != ucPowerOnlyEventCount)
            {
                ucPowerOnlyEventCount = ucNewPowerOnlyEventCount;
                dPowerOnlyBundleRxTime = dRxTime_;
            }

            // Don't grab the power decoding unless we're the
            // only power message type we've received so far.
            if (ucPowerMeterType == 255)
            {
                ucPowerMeterType = messagePayload_[0];
                DecodePowerOnly_Resync(dPowerOnlyBundleRxTime, messagePayload_);
            }

            if (bResyncPowerOnlyChannel)
            {
                DecodePowerOnly_Resync(dPowerOnlyBundleRxTime, messagePayload_);
                bResyncPowerOnlyChannel = false;
            }

            // For now we will only decode the power only page if it is the only bike power page we receive
            if (ucPowerMeterType == ANT_POWERONLY)
                DecodePowerOnly_Message(dPowerOnlyBundleRxTime, messagePayload_);
            break;

        case ANT_WHEELTORQUE:
            if (ucPowerMeterType != messagePayload_[0])
            {
                // set up the power only message in addition
                // to the crank torque data stream.
                DecodePowerOnly_Resync(dRxTime_, messagePayload_);
                bResyncPowerOnlyChannel = false;

                DecodeWheelTorque_Resync(dRxTime_, messagePayload_);
                bResyncPowerChannel = false;

                ucPowerMeterType = messagePayload_[0];
            }

            // This is resolved here in order to handle decoder specific
            // resync requirements when a new message is available.
            if (bResyncPowerChannel)
            {
                DecodeWheelTorque_Resync(dRxTime_, messagePayload_);
                bResyncPowerChannel = false;
            }

            DecodeWheelTorque_Message(dRxTime_, messagePayload_);
            break;

        case ANT_CRANKTORQUE:
            if (ucPowerMeterType != messagePayload_[0])
            {
                // set up the power only message in addition
                // to the crank torque data stream.
                DecodePowerOnly_Resync(dRxTime_, messagePayload_);
                bResyncPowerOnlyChannel = false;

                DecodeCrankTorque_Resync(dRxTime_, messagePayload_);
                bResyncPowerChannel = false;

                ucPowerMeterType = messagePayload_[0];
            }

            // This is resolved here in order to handle decoder specific
            // resync requirements when a new message is available.
            if (bResyncPowerChannel)
            {
                DecodeCrankTorque_Resync(dRxTime_, messagePayload_);
                bResyncPowerChannel = false;
            }

            DecodeCrankTorque_Message(dRxTime_, messagePayload_);
            break;
        case ANT_CRANKFREQ:
            if (ucPowerMeterType != messagePayload_[0])
            {
                // set up the power only message in addition
                // to the crank torque data stream.
                DecodePowerOnly_Resync(dRxTime_, messagePayload_);
                bResyncPowerOnlyChannel = false;

                DecodeCrankTorqueFreq_Resync(dRxTime_, messagePayload_);
                bResyncPowerChannel = false;

                ucPowerMeterType = messagePayload_[0];
            }

            // This is resolved here in order to handle decoder specific
            // resync requirements when a new message is available.
            if (bResyncPowerChannel)
            {
                DecodeCrankTorqueFreq_Resync(dRxTime_, messagePayload_);
                bResyncPowerChannel = false;
            }

            DecodeCrankTorqueFreq_Message(dRxTime_, messagePayload_);
            break;

        case ANT_TEPS:
            // This is an auxiliary message, not valid unless there is an actual power message
            // to match it to.
            // We still need to correct for Rx Time because we do not know which power only event count shared message comes first.
            ucNewPowerOnlyEventCount = messagePayload_[1];

            if (ucNewPowerOnlyEventCount != ucPowerOnlyEventCount)
            {
                ucPowerOnlyEventCount = ucNewPowerOnlyEventCount;
                dPowerOnlyBundleRxTime = dRxTime_;
            }
            break;

        case ANT_CALIBRATION_MESSAGE:
            switch (ucPowerMeterType)
            {
            case ANT_CRANKFREQ:
                // The only one that really matters is the crank torque frequency meter.
                DecodeCrankTorqueFreq_Calibration(dRxTime_, messagePayload_);
                break;
            default:
                break;
            }
            break;
        default:
            // Other pages are ignored in this example.
            break;
    }
}