/*!
 * \file      NvmDataMgmt.c
 *
 * \brief     NVM context management implementation
 *
 * \copyright Revised BSD License, see section \ref LICENSE.
 *
 * \code
 *                ______                              _
 *               / _____)             _              | |
 *              ( (____  _____ ____ _| |_ _____  ____| |__
 *               \____ \| ___ |    (_   _) ___ |/ ___)  _ \
 *               _____) ) ____| | | || |_| ____( (___| | | |
 *              (______/|_____)_|_|_| \__)_____)\____)_| |_|
 *              (C)2013-2017 Semtech
 *
 *               ___ _____ _   ___ _  _____ ___  ___  ___ ___
 *              / __|_   _/_\ / __| |/ / __/ _ \| _ \/ __| __|
 *              \__ \ | |/ _ \ (__| ' <| _| (_) |   / (__| _|
 *              |___/ |_/_/ \_\___|_|\_\_| \___/|_|_\\___|___|
 *              embedded.connectivity.solutions===============
 *
 * \endcode
 *
 * \author    Miguel Luis ( Semtech )
 *
 * \author    Gregory Cristian ( Semtech )
 *
 * \author    Daniel Jaeckle ( STACKFORCE )
 *
 * \author    Johannes Bruder ( STACKFORCE )
 */

#include <stdio.h>
#include "utilities.h"
#include "nvmm.h"
#include "LoRaMac.h"
#include "NvmDataMgmt.h"

/*!
 * Enables/Disables the context storage management storage.
 * Must be enabled for LoRaWAN 1.0.4 or later.
 */
#ifndef CONTEXT_MANAGEMENT_ENABLED
#define CONTEXT_MANAGEMENT_ENABLED         1
#endif


static uint16_t NvmNotifyFlags = 0;

void NvmDataMgmtEvent( uint16_t notifyFlags )
{
    NvmNotifyFlags = notifyFlags;
}

uint16_t NvmDataMgmtStore( void )
{
#if( CONTEXT_MANAGEMENT_ENABLED == 1 )
    uint16_t offset = 0;
    uint16_t dataSize = 0;
    MibRequestConfirm_t mibReq;
    mibReq.Type = MIB_NVM_CTXS;
    LoRaMacMibGetRequestConfirm( &mibReq );
    LoRaMacNvmData_t* nvm = mibReq.Param.Contexts;

    // Input checks
    if( NvmNotifyFlags == LORAMAC_NVM_NOTIFY_FLAG_NONE )
    {
        // There was no update.
        return 0;
    }
    if( LoRaMacStop( ) != LORAMAC_STATUS_OK )
    {
        return 0;
    }

    // Crypto
    if( ( NvmNotifyFlags & LORAMAC_NVM_NOTIFY_FLAG_CRYPTO ) ==
        LORAMAC_NVM_NOTIFY_FLAG_CRYPTO )
    {
        dataSize += NvmmWrite( ( uint8_t* ) &nvm->Crypto, sizeof( nvm->Crypto ),
                               offset );
    }
    offset += sizeof( nvm->Crypto );

    // MacGroup1
    if( ( NvmNotifyFlags & LORAMAC_NVM_NOTIFY_FLAG_MAC_GROUP1 ) ==
        LORAMAC_NVM_NOTIFY_FLAG_MAC_GROUP1 )
    {
        dataSize += NvmmWrite( ( uint8_t* ) &nvm->MacGroup1,
                               sizeof( nvm->MacGroup1 ), offset );
    }
    offset += sizeof( nvm->MacGroup1 );

    // MacGroup2
    if( ( NvmNotifyFlags & LORAMAC_NVM_NOTIFY_FLAG_MAC_GROUP2 ) ==
        LORAMAC_NVM_NOTIFY_FLAG_MAC_GROUP2 )
    {
        dataSize += NvmmWrite( ( uint8_t* ) &nvm->MacGroup2,
                               sizeof( nvm->MacGroup2 ), offset );
    }
    offset += sizeof( nvm->MacGroup2 );

    // Secure element
    if( ( NvmNotifyFlags & LORAMAC_NVM_NOTIFY_FLAG_SECURE_ELEMENT ) ==
        LORAMAC_NVM_NOTIFY_FLAG_SECURE_ELEMENT )
    {
        dataSize += NvmmWrite( ( uint8_t* ) &nvm->SecureElement, sizeof( nvm->SecureElement ),
                               offset );
    }
    offset += sizeof( nvm->SecureElement );

    // Region group 1
    if( ( NvmNotifyFlags & LORAMAC_NVM_NOTIFY_FLAG_REGION_GROUP1 ) ==
        LORAMAC_NVM_NOTIFY_FLAG_REGION_GROUP1 )
    {
        dataSize += NvmmWrite( ( uint8_t* ) &nvm->RegionGroup1,
                               sizeof( nvm->RegionGroup1 ), offset );
    }
    offset += sizeof( nvm->RegionGroup1 );

    // Region group 2
    if( ( NvmNotifyFlags & LORAMAC_NVM_NOTIFY_FLAG_REGION_GROUP2 ) ==
        LORAMAC_NVM_NOTIFY_FLAG_REGION_GROUP2 )
    {
        dataSize += NvmmWrite( ( uint8_t* ) &nvm->RegionGroup2,
                               sizeof( nvm->RegionGroup2 ), offset );
    }
    offset += sizeof( nvm->RegionGroup2 );

    // Class b
    if( ( NvmNotifyFlags & LORAMAC_NVM_NOTIFY_FLAG_CLASS_B ) ==
        LORAMAC_NVM_NOTIFY_FLAG_CLASS_B )
    {
        dataSize += NvmmWrite( ( uint8_t* ) &nvm->ClassB, sizeof( nvm->ClassB ),
                               offset );
    }
    offset += sizeof( nvm->ClassB );

    // Reset notification flags
    NvmNotifyFlags = LORAMAC_NVM_NOTIFY_FLAG_NONE;

    // Resume LoRaMac
    LoRaMacStart( );
    return dataSize;
#else
    return 0;
#endif
}

uint16_t NvmDataMgmtRestore( void )
{
#if( CONTEXT_MANAGEMENT_ENABLED == 1 )
    MibRequestConfirm_t mibReq;
    mibReq.Type = MIB_NVM_CTXS;
    LoRaMacMibGetRequestConfirm( &mibReq );
    LoRaMacNvmData_t* nvm = mibReq.Param.Contexts;
    uint16_t offset = 0;

    // Crypto
    if( NvmmCrc32Check( sizeof( LoRaMacCryptoNvmData_t ), offset ) == false )
    {
        return 0;
    }
    offset += sizeof( LoRaMacCryptoNvmData_t );

    // Mac Group 1
    if( NvmmCrc32Check( sizeof( LoRaMacNvmDataGroup1_t ), offset ) == false )
    {
        return 0;
    }
    offset += sizeof( LoRaMacNvmDataGroup1_t );

    // Mac Group 2
    if( NvmmCrc32Check( sizeof( LoRaMacNvmDataGroup2_t ), offset ) == false )
    {
        return 0;
    }
    offset += sizeof( LoRaMacNvmDataGroup2_t );

    // Secure element
    if( NvmmCrc32Check( sizeof( SecureElementNvmData_t ), offset ) == false )
    {
        return 0;
    }
    offset += sizeof( SecureElementNvmData_t );

    // Region group 1
    if( sizeof( RegionNvmDataGroup1_t ) > sizeof( uint32_t ) )
    {
        if( NvmmCrc32Check( sizeof( RegionNvmDataGroup1_t ), offset ) == false )
        {
            return 0;
        }
    }
    offset += sizeof( RegionNvmDataGroup1_t );

    // Region group 2
    if( NvmmCrc32Check( sizeof( RegionNvmDataGroup2_t ), offset ) == false )
    {
        return 0;
    }
    offset += sizeof( RegionNvmDataGroup2_t );

    // Class b
    if( NvmmCrc32Check( sizeof( LoRaMacClassBNvmData_t ), offset ) == false )
    {
        return 0;
    }
    offset += sizeof( LoRaMacClassBNvmData_t );

    if( NvmmRead( ( uint8_t* ) nvm, sizeof( LoRaMacNvmData_t ), 0 ) ==
                  sizeof( LoRaMacNvmData_t ) )
    {
        return sizeof( LoRaMacNvmData_t );
    }
#endif
    return 0;
}

bool NvmDataMgmtFactoryReset( void )
{
    uint16_t offset = 0;
#if( CONTEXT_MANAGEMENT_ENABLED == 1 )
    // Crypto
    if( NvmmReset( sizeof( LoRaMacCryptoNvmData_t ), offset ) == false )
    {
        return false;
    }
    offset += sizeof( LoRaMacCryptoNvmData_t );

    // Mac Group 1
    if( NvmmReset( sizeof( LoRaMacNvmDataGroup1_t ), offset ) == false )
    {
        return false;
    }
    offset += sizeof( LoRaMacNvmDataGroup1_t );

    // Mac Group 2
    if( NvmmReset( sizeof( LoRaMacNvmDataGroup2_t ), offset ) == false )
    {
        return false;
    }
    offset += sizeof( LoRaMacNvmDataGroup2_t );

    // Secure element
    if( NvmmReset( sizeof( SecureElementNvmData_t ), offset ) == false )
    {
        return false;
    }
    offset += sizeof( SecureElementNvmData_t );

    // Region group 1
    if( NvmmReset( sizeof( RegionNvmDataGroup1_t ), offset ) == false )
    {
        return false;
    }
    offset += sizeof( RegionNvmDataGroup1_t );

    // Region group 2
    if( NvmmReset( sizeof( RegionNvmDataGroup2_t ), offset ) == false )
    {
        return false;
    }
    offset += sizeof( RegionNvmDataGroup2_t );

    // Class b
    if( NvmmReset( sizeof( LoRaMacClassBNvmData_t ), offset ) == false )
    {
        return false;
    }
    offset += sizeof( LoRaMacClassBNvmData_t );
#endif
    return true;
}