//------------------------------------------------------------------------------
//! @file WiMODProLink.h
//! @ingroup WiMODPROLINK
//! <!------------------------------------------------------------------------->
//! @brief Declarations for the High-level Interface for WiMOD LoRaWAN ProLink firmware
//! @version 0.1
//! <!------------------------------------------------------------------------->
//!
//!
//!
//! <!--------------------------------------------------------------------------
//! Copyright (c) 2022
//! IMST GmbH
//! Carl-Friedrich Gauss Str. 2-4
//! 47475 Kamp-Lintfort
//! --------------------------------------------------------------------------->
//! @author (FB), IMST
//! <!--------------------------------------------------------------------------
//! Target OS:    none
//! Target CPU:   tbd
//! Compiler:     tbd
//! --------------------------------------------------------------------------->
//! @internal
//! @par Revision History:
//! <PRE>
//!-----------------------------------------------------------------------------
//! Version | Date       | Author | Comment
//!-----------------------------------------------------------------------------
//!
//! </PRE>
//------------------------------------------------------------------------------

/*
 * THIS IS AN EXAMPLE IMPLEMENTATION ACCORDING THE THE HCI SPEC: V1.3
 * FOR FIRMWARE: LoRaWAN PROLINK
 *
 * SEE FILE: ProLink_LoRaWAN_EndNode_Modem_HCI_Spec_v1_3.pdf for detailed information
 */

#ifndef ARDUINO_WIMODPROLINK_H_
#define ARDUINO_WIMODPROLINK_H_

//------------------------------------------------------------------------------
//
// Section Includes Files
//
//------------------------------------------------------------------------------

#include "Arduino.h"
/************
 * BUG in ardunino.h ?
 * -> https://stackoverflow.com/questions/41093090/esp8266-error-macro-min-passed-3-arguments-but-takes-just-2
 */
#undef min
#undef max

#include <string.h>

#include "SAP/WiMOD_SAP_LORAWAN_ProLink.h"
#include "SAP/WiMOD_SAP_DEVMGMT_ProLink.h"
#include "SAP/WiMOD_SAP_RadioLink_ProLink.h"

#include "SAP/WiMOD_SAP_Generic.h"
#include "utils/ComSLIP.h"
#include "HCI/WiMODLRHCI.h"
#include "utils/FreqCalc_SX127x.h"

//-----------------------------------------------------------------------------
// common defines
//-----------------------------------------------------------------------------
//! @cond Doxygen_Suppress
#define WIMOD_PROLINK_SERIAL_BAUDRATE               115200

#define WiMOD_PROLINK_TX_BUFFER_SIZE                256
//! @endcond
//-----------------------------------------------------------------------------
// types for callback functions
//-----------------------------------------------------------------------------



//-----------------------------------------------------------------------------
// API class declaration for the WiMOD ProLink LoRaWAN Stack
//
//-----------------------------------------------------------------------------
/**
 * @brief Main class representing the interface to the WiMOD running the firmware ProLink LoRaWAN Modem
 *
 * This class is the only API class a user should use for interacting with
 * a WiMOD module that runs the IMST ProLink LoRaWAN Modem firmware.
 *
 */
class WiMODProLink : public TWiMODLRHCI {
public:
    /*explicit*/ WiMODProLink(Stream& s);
    ~WiMODProLink(void);
    void begin(TProLinkLoRaWANregion region = ProLink_LoRaWAN_Region_EU868);
    void end(void);

    //! @cond Doxygen_Suppress
    void beginAndAutoSetup(void);
    void autoSetupSupportedRegion(void);
    //! @endcond

    /*
     * DevMgmt SAP
     */
    bool Ping(TWiMODLRResultCodes* hciResult = NULL, UINT8* rspStatus = NULL);
    bool Reset(TWiMODLRResultCodes* hciResult = NULL, UINT8* rspStatus = NULL);
    bool GetDeviceInfo(TWiMODLR_DevMgmt_DevInfoLoRaWan* info, TWiMODLRResultCodes* hciResult = NULL, UINT8* rspStatus = NULL);
    bool GetFirmwareInfo(TWiMODLR_DevMgmt_FwInfo* info, TWiMODLRResultCodes* hciResult = NULL, UINT8* rspStatus = NULL);
    bool GetRtc(UINT32* rtcTime,TWiMODLRResultCodes* hciResult = NULL, UINT8* rspStatus = NULL);
    bool SetRtc(const UINT32 rtcTime, TWiMODLRResultCodes* hciResult = NULL, UINT8* rspStatus = NULL);

    bool SetRtcAlarm(const TWiMODLR_DevMgmt_RtcAlarm* rtcAlarm, TWiMODLRResultCodes* hciResult = NULL, UINT8* rspStatus = NULL);
    bool GetRtcAlarm(TWiMODLR_DevMgmt_RtcAlarm* rtcAlarm, TWiMODLRResultCodes* hciResult = NULL, UINT8* rspStatus = NULL);
    bool ClearRtcAlarm(TWiMODLRResultCodes* hciResult = NULL, UINT8* rspStatus = NULL);

    bool GetOperationMode(TWiMOD_OperationMode* opMode, TWiMODLRResultCodes* hciResult = NULL, UINT8* rspStatus = NULL);
    bool SetOperationMode(const TWiMOD_OperationMode opMode, TWiMODLRResultCodes* hciResult = NULL, UINT8* rspStatus = NULL);

    void RegisterPowerUpIndicationClient(TDevMgmtPowerUpCallback cb);
    void RegisterRtcAlarmIndicationClient(TDevMgmtRtcAlarmCallback cb);

    bool GetHciConfig(TWiMODLR_DevMgmt_HciConfig* hciConfig, TWiMODLRResultCodes* hciResult = NULL, UINT8* rspStatus = NULL);
    bool SetHciConfig(TWiMODLR_DevMgmt_HciConfig& hciConfig, TWiMODLRResultCodes* hciResult = NULL, UINT8* rspStatus = NULL);


    bool GetRadioStack(TRadioStack_Mode* stackMode, TWiMODLRResultCodes* hciResult = NULL, UINT8* rspStatus = NULL);
    bool SetRadioStack(const TRadioStack_Mode& stackMode, TWiMODLRResultCodes* hciResult = NULL, UINT8* rspStatus = NULL);


    bool SetDeviceConfig(const TProLinkDeviceConfig& config, TWiMODLRResultCodes* hciResult = NULL, UINT8* rspStatus = NULL);
    bool GetDeviceConfig(TProLinkDeviceConfig* config, TWiMODLRResultCodes* hciResult = NULL, UINT8* rspStatus = NULL);
    bool ResetDeviceConfig(TWiMODLRResultCodes* hciResult = NULL, UINT8* rspStatus = NULL);

    bool GetDeviceStatus(TProLinkDeviceStatus*  devStatus, TWiMODLRResultCodes* hciResult = NULL, UINT8* rspStatus = NULL);

    /*
     * LoRaWAN SAP
     */
    bool ActivateDevice(TWiMODLORAWAN_ActivateDeviceData& activationData,TWiMODLRResultCodes* hciResult = NULL, UINT8* rspStatus = NULL);
    bool ReactivateDevice(UINT32* devAdr, TWiMODLRResultCodes* hciResult = NULL, UINT8* rspStatus = NULL);
    bool SetJoinParameter(TWiMODLORAWAN_JoinParams& joinParams, TWiMODLRResultCodes* hciResult = NULL, UINT8* rspStatus = NULL);
    bool JoinNetwork(TWiMODLRResultCodes* hciResult = NULL, UINT8* rspStatus = NULL);

    void RegisterJoinTxIndicationClient(TJoinTxIndicationCallback cb);

    bool
    convert(TWiMODLR_HCIMessage& RxMsg, TWiMODLORAWAN_RX_Data* loraWanRxData);
    bool
    convert(TWiMODLR_HCIMessage& rxMsg, TWiMODLORAWAN_TxIndData* sendIndData);

    bool
    convert(TWiMODLR_HCIMessage& RxMsg, TWiMODLORAWAN_RX_MacCmdData* loraWanMacCmdData);

    bool
    convert(TWiMODLR_HCIMessage& RxMsg,TWiMODLORAWAN_RX_JoinedNwkData* joinedNwkData);

    bool
    convert(TWiMODLR_HCIMessage& RxMsg, TWiMODLORAWAN_RX_ACK_Data* ackData);

    bool
    convert(TWiMODLR_HCIMessage& RxMsg, TWiMODLORAWAN_NoData_Data* info);

    void
    RegisterNoDataIndicationClient(TNoDataIndicationCallback cb);

    void
    RegisterTxCDataIndicationClient(TTxCDataIndicationCallback cb);

    void
    RegisterTxUDataIndicationClient(TTxUDataIndicationCallback cb);

    void
    RegisterRxUDataIndicationClient(TRxUDataIndicationCallback cb);

    void
    RegisterRxCDataIndicationClient(TRxCDataIndicationCallback cb);

    void
    RegisterRxMacCmdIndicationClient(TRxMacCmdIndicationCallback cb);

    void
    RegisterJoinedNwkIndicationClient(TJoinedNwkIndicationCallback cb);

    void
    RegisterRxAckIndicationClient(TRxAckIndicationCallback cb);

    bool SendUData(TWiMODProLinkLORAWAN_TX_Data* data, TWiMODLRResultCodes* hciResult = NULL, UINT8* rspStatus = NULL);
    bool SendCData(TWiMODProLinkLORAWAN_TX_Data* data,TWiMODLRResultCodes* hciResult = NULL, UINT8* rspStatus = NULL);
    bool SetRadioStackConfig(TWiMODProLinkLORAWAN_RadioStackConfig* data,TWiMODLRResultCodes* hciResult = NULL, UINT8* rspStatus = NULL);
    bool GetRadioStackConfig(TWiMODProLinkLORAWAN_RadioStackConfig* data, TWiMODLRResultCodes* hciResult = NULL, UINT8* rspStatus = NULL);
    bool DeactivateDevice(TWiMODLRResultCodes* hciResult = NULL, UINT8* rspStatus = NULL);
    bool FactoryReset(TWiMODLRResultCodes* hciResult = NULL, UINT8* rspStatus = NULL);
    bool SetDeviceEUI(const UINT8* deviceEUI, TWiMODLRResultCodes* hciResult = NULL, UINT8* rspStatus = NULL);
    bool GetDeviceEUI(UINT8* deviceEUI, TWiMODLRResultCodes* hciResult = NULL, UINT8* rspStatus = NULL);
    bool GetNwkStatus(TWiMODLORAWAN_NwkStatus_Data* nwkStatus, TWiMODLRResultCodes* hciResult = NULL, UINT8* rspStatus = NULL); // new implementation for spec. V1.14
    bool SendMacCmd(const TWiMODLORAWAN_MacCmd* cmd, TWiMODLRResultCodes* hciResult = NULL, UINT8* rspStatus = NULL);
    bool SetCustomConfig(const INT8 rfGain, TWiMODLRResultCodes* hciResult = NULL, UINT8* rspStatus = NULL);
    bool GetCustomConfig(INT8* rfGain, TWiMODLRResultCodes* hciResult = NULL, UINT8* rspStatus = NULL);
    bool GetSupportedBands(TWiMODLORAWAN_SupportedBands* supportedBands, TWiMODLRResultCodes*  hciResult = NULL, UINT8* rspStatus = NULL);
    bool GetTxPowerLimitConfig(TWiMODLORAWAN_TxPwrLimitConfig* txPwrLimitCfg, TWiMODLRResultCodes*  hciResult = NULL, UINT8* rspStatus = NULL);
    bool SetTxPowerLimitConfig(TWiMODLORAWAN_TxPwrLimitConfig& txPwrLimitCfg, TWiMODLRResultCodes*  hciResult = NULL, UINT8* rspStatus = NULL);

    bool SetBatteryLevelStatus(UINT8 battStatus, TWiMODLRResultCodes*  hciResult = NULL, UINT8* rspStatus = NULL);

    bool SetDeviceNonce(const UINT16 devNonce, TWiMODLRResultCodes*  hciResult = NULL, UINT8* rspStatus = NULL);
    bool GetDeviceNonce(UINT16* devNonce, TWiMODLRResultCodes*  hciResult = NULL, UINT8* rspStatus = NULL);


    bool SetMulticastConfig(TWiMODLORAWAN_McastConfig& mcastCfg, TWiMODLRResultCodes*  hciResult = NULL, UINT8* rspStatus = NULL);
    bool GetMulticastConfig(TWiMODLORAWAN_McastConfig* mcastCfg, TWiMODLRResultCodes*  hciResult = NULL, UINT8* rspStatus = NULL);
    bool RemoveMulticastConfig(const UINT8 mcastIndex, TWiMODLRResultCodes*  hciResult = NULL, UINT8* rspStatus = NULL);

    void
    RegisterResetDevNonceIndicationClient(TResetDevNonceIndicationCallback cb);

    bool SetJoinNonce(const UINT16 joinNonce, TWiMODLRResultCodes*  hciResult = NULL, UINT8* rspStatus = NULL);
    bool GetJoinNonce(UINT16* joinNonce, TWiMODLRResultCodes*  hciResult = NULL, UINT8* rspStatus = NULL);

    void
    RegisterLinkDiconnectIndicationClient(TLinkDisconnectIndicationCallback cb);

    bool SendNwkTimeRequest(TWiMODLORAWAN_DevTimeReqInfo* devTimeInfo, TWiMODLRResultCodes*  hciResult = NULL, UINT8* rspStatus = NULL);

    void
    RegisterNwkTimeAnsIndicationClient(TNwkDeviceTimeAnsIndicationCallback cb);

    void
    RegisterMulticastDataIndicationClient(TMCastDataIndicationCallback cb);

    void
    RegisterMulticastInvalidDataIndicationClient(TMCastInvalidDataIndicationCallback cb);

    bool
    convert(TWiMODLR_HCIMessage& RxMsg, TWiMODLORAWAN_DevTimeAnsInfo* info);
    bool
    convert(TWiMODLR_HCIMessage& RxMsg, TWiMODLORAWAN_McastData* mcastData);
    bool
    convert(TWiMODLR_HCIMessage& RxMsg, TWiMODLORAWAN_McastNoData*  mcastErrData);


    /*
     * proprietary RadioLink SAP (LR-BASE)
     */
    bool GetRadioLinkRadioConfig(TWiMODLR_ProLinkRadioLink_RadioConfig* radioCfg, TWiMODLRResultCodes*  hciResult = NULL, UINT8* rspStatus = NULL);
    bool SetRadioLinkRadioConfig(const TWiMODLR_ProLinkRadioLink_RadioConfig* radioCfg, TWiMODLRResultCodes*  hciResult = NULL, UINT8* rspStatus = NULL);
    bool ResetRadioLinkRadioConfig(TWiMODLRResultCodes*  hciResult = NULL, UINT8* rspStatus = NULL);

    bool SetRadioLinkAesKey(const UINT8* key, TWiMODLRResultCodes*  hciResult = NULL, UINT8* rspStatus = NULL);
    bool GetRadioLinkAesKey( UINT8* key,TWiMODLRResultCodes*  hciResult = NULL, UINT8* rspStatus = NULL);

    bool SendRadioLinkUData(const TWiMODLR_RadioLink_Msg* data,TWiMODLRResultCodes*  hciResult = NULL, UINT8* rspStatus = NULL);

    void RegisterRadioLinkRxUDataIndicationClient(TRadioLinkUDataRxIndicationCallback cb);

    void RegisterRadioLinkTxUDataIndicationClient(TRadioLinkUDataTxIndicationCallback cb);

    void     calcFreqToRegister(uint32_t freq, uint8_t* msb, uint8_t* mid, uint8_t* lsb);
    uint32_t calcRegisterToFreq(uint8_t msb, uint8_t mid, uint8_t lsb);

    bool convert(TWiMODLR_HCIMessage&    RxMsg, TWiMODLR_RadioLink_Msg* radioLinkMsg);
    bool convert(TWiMODLR_HCIMessage&    RxMsg, TWiMODLR_RadioLink_UdataInd* uDataTxInfo);


    /*
     * Generic Cmd
     */
    bool ExecuteGenericCmd(TWiMODLR_Generic_CmdInfo* info, TWiMODLRResultCodes* hciResult = NULL, UINT8* rspStatus = NULL);

    /*
     * Info & QuickStart Cmds
     */
    void PrintBasicDeviceInfo(Stream& s);
    void ConnectViaOTAA(const uint8_t* appEUI = NULL, const uint8_t* appKey = NULL);
    void ConvertAppEuiStrToArray(char* appEuiStr, uint8_t* appEuiArray);
    void ConvertAppKeyStrToArray(char* appKeyStr, uint8_t* appKeyArray);

    void ConvertNwkSKeyStrToArray(char* nwkSKeyStr, uint8_t* nwkSKeyArray);
    void ConvertAppSKeyStrToArray(char* appSKeyStr, uint8_t* appSKeyArray);

    TWiMODLRResultCodes  GetLastHciResult(void);
    UINT8               GetLastResponseStatus(void);

protected:
    WiMOD_SAP_DevMgmt_ProLink   SapDevMgmt;                                     /*!< Service Access Point for 'DeviceManagement' */
    WiMOD_SAP_LoRaWAN_ProLink   SapLoRaWan;                                     /*!< Service Access Point for 'LoRaWAN' */
    WiMOD_SAP_RadioLink_ProLink SapRadioLink;                                   /*!< Service Access Point for 'RadioLink (proprietary mode)' */
    WiMOD_SAP_Generic           SapGeneric;                                     /*!< dumy SAP for generic HCI command */


    virtual void       ProcessUnexpectedRxMessage(TWiMODLR_HCIMessage& rxMsg);


    bool               copyLoRaWanResultInfos(TWiMODLRResultCodes* hciResult, UINT8* rspStatus);
    bool               copyDevMgmtResultInfos(TWiMODLRResultCodes* hciResult, UINT8* rspStatus);
    bool               copyProLinkRadioLinkResultInfos(TWiMODLRResultCodes* hciResult, UINT8* rspStatus);
private:
    //! @cond Doxygen_Suppress
    UINT8               txBuffer[WiMOD_PROLINK_TX_BUFFER_SIZE];

    UINT8               localStatusRsp;
    bool                cmdResult;
    TWiMODLRResultCodes  localHciRes;

    TWiMODLRResultCodes  lastHciRes;
    UINT8               lastStatusRsp;
    //! @endcond
};










#endif /* ARDUINO_WIMODPROLINK_H_ */
