/*
 * Copyright (c) 2022, Shenzhen CVA Innovation CO.,LTD
 * All rights reserved.
 *
 * Shenzhen CVA Innovation CO.,LTD (CVA chip) is supplying this file for use
 * exclusively with CVA's microcontroller products. This file can be freely
 * distributed within development tools that are supporting such microcontroller
 * products.
 *
 * THIS SOFTWARE IS PROVIDED "AS IS".  NO WARRANTIES, WHETHER EXPRESS, IMPLIED
 * OR STATUTORY, INCLUDING, BUT NOT LIMITED TO, IMPLIED WARRANTIES OF
 * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE APPLY TO THIS SOFTWARE.
 * CVA SHALL NOT, IN ANY CIRCUMSTANCES, BE LIABLE FOR SPECIAL, INCIDENTAL,
 * OR CONSEQUENTIAL DAMAGES, FOR ANY REASON WHATSOEVER.
 */

/*******************************************************************************
 * the includes
 ******************************************************************************/

#include <string.h>
#include "nvm_statemachine.h"
#include "nvm_queue.h"
#include "nvm_block.h"
#include "nvm_extra.h"

/*******************************************************************************
 * the defines
 ******************************************************************************/
/*! \brief Macro that defines whether to complete immediate writing.
 */
#define NVM_IMMEDIATE_WRITE_FINISH(obj) (NVM_IMMEDIATEWRITEFSM_FINISH == obj->runtime->immediateWriteJob->job)

/*! \brief Define macros that immediately fail to write.
 */
#define NVM_IMMEDIATE_WRITE_FAIL(obj)   (NVM_IMMEDIATEWRITEFSM_WRITEERROR == obj->runtime->immediateWriteJob->job)

/*! \brief Define macros that immediately write errors.
 */
#define NVM_IMMEDIATE_WRITE_FATAL(obj)  (NVM_IMMEDIATEWRITEFSM_ERROR == obj->runtime->immediateWriteJob->job)

/*! \brief Define immediate read completion macros.
 */
#define NVM_IMMEDIATE_READ_FINISH(obj)  (NVM_IMMEDIATEREADFSM_FINISH == obj->runtime->immediateReadJob->job)

/*! \brief Define macros that immediately fail to read.
 */
#define NVM_IMMEDIATE_READ_FAIL(obj)    (NVM_IMMEDIATEREADFSM_READERROR == obj->runtime->immediateReadJob->job)

/*! \brief Define macros for immediate read errors.
 */
#define NVM_IMMEDIATE_READ_FATAL(obj)   (NVM_IMMEDIATEREADFSM_ERROR == obj->runtime->immediateReadJob->job)

/*******************************************************************************
 * the typedefs
 ******************************************************************************/

/*******************************************************************************
 * the globals
 ******************************************************************************/
static void Nvm_StateMachine_Immediate_Read_Entry(const NvmType *obj, uint16_t blockIdx, uint16_t datasetIdx, uint8_t retry, NvmNotificationPtrType notificationPtr);
static void Nvm_StateMachine_Immediate_Read(const NvmType *obj);
static void Nvm_StateMachine_Immediate_Write_Entry(const NvmType *obj, uint16_t blockIdx, uint16_t datasetIdx, uint8_t retry, NvmNotificationPtrType notificationPtr);
static void Nvm_StateMachine_Immediate_Write(const NvmType *obj);

/*******************************************************************************
 * the functions
 ******************************************************************************/
void Nvm_StateMachine_Idle(const NvmType *obj)
{
    /* Check if the lower level module status is idle. */
    if(NVM_STATUS_IDLE == obj->method->getStatus())
    {
        /* Check if the job queue is empty. */
        if(NVM_RETURN_NOT_OK == Nvm_Queue_CheckEmpty(obj->runtime->queue))
        {
            /* Check if the header members of the queue are valid. */
            if(NVM_QUEUE_FISTMEMBER_VALID(obj->runtime->queue))
            {
                /* Lock the header member of the queue */
                NVM_QUEUE_LOCK_FIRST_JOB(obj->runtime->queue);
                /* Responding to job requests. */
                obj->runtime->nextState = NVM_QUEUE_FISTMEMBER_OP(obj->runtime->queue);
            }
            else
            {
                /* When the header member of the queue is an invalid request, the member will be removed. */
                Nvm_Queue_RemoveFirstItem(obj->runtime->queue);
            }
        }
    }
}

void Nvm_StateMachine_Read_Entry(const NvmType *obj)
{
    uint16_t localBlockIdx;
    uint16_t localDatasetIdx;

    localBlockIdx = NVM_QUEUE_FIRST_JOB(obj->runtime->queue).blockIdx;
    localDatasetIdx   = NVM_QUEUE_FIRST_JOB(obj->runtime->queue).datasetIdx;

    Nvm_Block_SetState(obj->blockTable, localBlockIdx, localDatasetIdx, NVM_REQ_PENDING);

    obj->runtime->readJob->blockIdx     = localBlockIdx;
    obj->runtime->readJob->datasetIdx   = localDatasetIdx;
    obj->runtime->readJob->cpyBuff      = NVM_BLOCK_GET_RAM_BUFF_PTR(obj->blockTable, localBlockIdx, localDatasetIdx);
    obj->runtime->readJob->userBuff     = NVM_QUEUE_FIRST_JOB(obj->runtime->queue).databuff;
    obj->runtime->readJob->datasize     = NVM_BLOCK_GET_DATASIZE(obj->blockTable, localBlockIdx);
}

void Nvm_StateMachine_Read(const NvmType *obj)
{
    /* Verify the data validity of the block. */
    Nvm_Block_DataRepair(obj->blockTable, obj->method, obj->runtime->readJob->blockIdx, obj->runtime->readJob->datasetIdx);

    /* Copy block data to user buff. */
    memcpy((void *)obj->runtime->readJob->userBuff, obj->runtime->readJob->cpyBuff, obj->runtime->readJob->datasize);

    /* Mark the external state of the block as NVM_REQ_OK. */
    Nvm_Block_SetState(obj->blockTable, obj->runtime->readJob->blockIdx, obj->runtime->readJob->datasetIdx, NVM_REQ_OK);
    /* Remove the request. */
    Nvm_Queue_RemoveFirstItem(obj->runtime->queue);
    /* Switch nvm state to idle. */
    obj->runtime->nextState = NVM_STATE_IDLE;
}

void Nvm_StateMachine_Write_Entry(const NvmType *obj)
{
    uint16_t localBlockIdx;
    uint16_t localDatasetIdx;

    localBlockIdx     = NVM_QUEUE_FIRST_JOB(obj->runtime->queue).blockIdx;
    localDatasetIdx   = NVM_QUEUE_FIRST_JOB(obj->runtime->queue).datasetIdx;

    Nvm_Block_SetState(obj->blockTable, localBlockIdx, localDatasetIdx, NVM_REQ_PENDING);

    obj->runtime->writeJob->blockIdx      = localBlockIdx;
    obj->runtime->writeJob->datasetIdx    = localDatasetIdx;
    obj->runtime->writeJob->blockDatabuff = NVM_BLOCK_GET_TEMP_RAM_BUFF_PTR(obj->blockTable, localBlockIdx);
    obj->runtime->writeJob->userBuff      = NVM_QUEUE_FIRST_JOB(obj->runtime->queue).databuff;
    obj->runtime->writeJob->useSize       = NVM_BLOCK_GET_DATASIZE(obj->blockTable, localBlockIdx);
    obj->runtime->writeJob->job           = NVM_WRITEFSM_INIT;

    Nvm_Block_SetTempRamBuffDefaultVal(obj->blockTable, localBlockIdx, localDatasetIdx);
}

void Nvm_StateMachine_Write(const NvmType *obj)
{
    switch(obj->runtime->writeJob->job)
    {
    case NVM_WRITEFSM_INIT:
        {
            /* If the type of the block is REDUNDANT, the state machine will be marked as a redundant write state. */
            /* If not, it will be judged whether it is an immediate write state. */
            /* If not, the state machine will be marked as a normal write state. */
            /* Otherwise, it will be marked as an immediate write state. */
            memcpy((void *)obj->runtime->writeJob->blockDatabuff, (void *)obj->runtime->writeJob->userBuff, obj->runtime->writeJob->useSize);

            if(NVM_BLOCK_IS_REDUNDANT(obj->blockTable, obj->runtime->writeJob->blockIdx))
            {
                obj->runtime->writeJob->job = NVM_WRITEFSM_REDUNDANT_MAIN;
            }
            else if(NVM_BLOCK_IS_IMMEDIATE(obj->blockTable, obj->runtime->writeJob->blockIdx))
            {
                Nvm_StateMachine_Immediate_Write_Entry(obj, obj->runtime->writeJob->blockIdx, obj->runtime->writeJob->datasetIdx, NVM_BLOCK_RETRY_NUM, NULL);
                obj->runtime->writeJob->job = NVM_WRITEFSM_IMMEDIATE;
            }
            else
            {
                obj->runtime->writeJob->job = NVM_WRITEFSM_NORMAL;
            }
            break;
        }
    case NVM_WRITEFSM_IMMEDIATE:
        {
            /* If the immediate write execution is successful or an error occurs, */
            /* mark the state machine to the read back state, otherwise mark the state machine to the normal state. */
            Nvm_StateMachine_Immediate_Write(obj);
            if(NVM_IMMEDIATE_WRITE_FINISH(obj) || NVM_IMMEDIATE_WRITE_FATAL(obj))
            {
                Nvm_StateMachine_Immediate_Read_Entry(obj, obj->runtime->writeJob->blockIdx, obj->runtime->writeJob->datasetIdx, NVM_BLOCK_RETRY_NUM, NULL);
                obj->runtime->writeJob->job = NVM_WRITEFSM_READBACK;
            }
            else if(NVM_IMMEDIATE_WRITE_FAIL(obj))
            {
                obj->runtime->writeJob->job = NVM_WRITEFSM_NORMAL;
            }
            break;
        }
    case NVM_WRITEFSM_REDUNDANT_MAIN:
        {
            /* Execute the write main area of a REDUNDANT type block. */
            obj->runtime->writeJob->blockBackUp = false;
            obj->runtime->writeJob->datasetIdx = NVM_BLOCK_MAIN_IDX;
            if(NVM_BLOCK_IS_IMMEDIATE(obj->blockTable, obj->runtime->writeJob->blockIdx))
            {
                Nvm_StateMachine_Immediate_Write_Entry(obj, obj->runtime->writeJob->blockIdx, obj->runtime->writeJob->datasetIdx, NVM_BLOCK_RETRY_NUM, NULL);
                obj->runtime->writeJob->job = NVM_WRITEFSM_IMMEDIATE;
            }
            else
            {
                obj->runtime->writeJob->job = NVM_WRITEFSM_NORMAL;
            }
            break;
        }
    case NVM_WRITEFSM_REDUNDANT_BACKUP:
    {
        /* Execute the write backup area of a REDUNDANT type block. */
        obj->runtime->writeJob->blockBackUp = true;
        obj->runtime->writeJob->datasetIdx = NVM_BLOCK_BACKUP_IDX;
        if(NVM_BLOCK_IS_IMMEDIATE(obj->blockTable, obj->runtime->writeJob->blockIdx))
        {
            Nvm_StateMachine_Immediate_Write_Entry(obj, obj->runtime->writeJob->blockIdx, obj->runtime->writeJob->datasetIdx, NVM_BLOCK_RETRY_NUM, NULL);
            obj->runtime->writeJob->job = NVM_WRITEFSM_IMMEDIATE;
        }
        else
        {
            obj->runtime->writeJob->job = NVM_WRITEFSM_NORMAL;
        }
        break;
    }
    case NVM_WRITEFSM_READBACK:
        {
            /* Performing operations to read back data. */
            Nvm_StateMachine_Immediate_Read(obj);
            if(NVM_IMMEDIATE_READ_FINISH(obj) || NVM_IMMEDIATE_READ_FATAL(obj))
            {
                obj->runtime->writeJob->job = NVM_WRITEFSM_CHECK;
            }
            else if(NVM_IMMEDIATE_READ_FAIL(obj))
            {
                obj->runtime->writeJob->job = NVM_WRITEFSM_NORMAL;  
            }
            break;
        }
    case NVM_WRITEFSM_CHECK:
        {
            /* Check the validity of data within the block. */
            Nvm_Block_DataRepair(obj->blockTable, obj->method, obj->runtime->writeJob->blockIdx, obj->runtime->writeJob->datasetIdx);
            Nvm_Block_SetState(obj->blockTable, obj->runtime->writeJob->blockIdx, obj->runtime->writeJob->datasetIdx, NVM_BLOCK_GET_INT_STATE(obj->blockTable, obj->runtime->writeJob->blockIdx, obj->runtime->writeJob->datasetIdx));
            if(NVM_BLOCK_IS_REDUNDANT(obj->blockTable, obj->runtime->writeJob->blockIdx))
            {
                if(obj->runtime->writeJob->blockBackUp)
                {
                    obj->runtime->writeJob->job = NVM_WRITEFSM_FINISH;
                }else
                {
                   obj->runtime->writeJob->job = NVM_WRITEFSM_REDUNDANT_BACKUP; 
                }
            }
            else
            {
                obj->runtime->writeJob->job = NVM_WRITEFSM_FINISH;
            }
            break;
        }
    case NVM_WRITEFSM_NORMAL:
        {
            /* Write data to the ram area of the block. */
            Nvm_Block_WriteDataToRamBlock(obj->blockTable, obj->method, obj->runtime->writeJob->blockIdx, obj->runtime->writeJob->datasetIdx, obj->runtime->writeJob->userBuff);
            Nvm_Block_SetState(obj->blockTable, obj->runtime->writeJob->blockIdx, obj->runtime->writeJob->datasetIdx, NVM_REQ_OK);
            if(NVM_BLOCK_IS_REDUNDANT(obj->blockTable, obj->runtime->writeJob->blockIdx))
            {
                if(obj->runtime->writeJob->blockBackUp)
                {
                    obj->runtime->writeJob->job = NVM_WRITEFSM_FINISH;
                }
                else
                {
                    obj->runtime->writeJob->job = NVM_WRITEFSM_REDUNDANT_BACKUP;
                }
            }
            else
            {
                obj->runtime->writeJob->job = NVM_WRITEFSM_FINISH;
            }
            break;
        }
    case NVM_WRITEFSM_FINISH:
        {
            /* Complete response request. */
            Nvm_Queue_RemoveFirstItem(obj->runtime->queue);
            obj->runtime->nextState = NVM_STATE_IDLE;
            break;
        }
    case NVM_WRITEFSM_WRITEERROR:
        {
            /* Complete response request. */
            Nvm_Queue_RemoveFirstItem(obj->runtime->queue);
            obj->runtime->nextState = NVM_STATE_IDLE;
            break;
        }
    default:
        {
            break;
        }
    }
}

void Nvm_StateMachine_ReadAll_Entry(const NvmType *obj)
{
    obj->runtime->readAllJob->job      = NVM_READALLFSM_READBLOCK;
    obj->runtime->readAllJob->blockIdx = 0;
}

void Nvm_StateMachine_ReadAll(const NvmType *obj)
{
    switch(obj->runtime->readAllJob->job)
    {
    case NVM_READALLFSM_READBLOCK:
        {
            /* Mark the state machine as finished after traversing all blocks. */
            if(obj->runtime->readAllJob->blockIdx < obj->blockTable->itemsNum)
            {
                obj->runtime->readAllJob->datasetIdx = 0;
                obj->runtime->readAllJob->job        = NVM_READALLFSM_READENTRY;
            }
            else
            {
                obj->runtime->readAllJob->job = NVM_READALLFSM_FINISH;
            }
            break;
        }
    case NVM_READALLFSM_READENTRY:
        {
            /* Execute the preparation program before immediate reading. */
            if(obj->runtime->readAllJob->datasetIdx < NVM_BLOCK_GET_DATASETNUM(obj->blockTable, obj->runtime->readAllJob->blockIdx))
            {
                Nvm_Block_SetState(obj->blockTable, obj->runtime->readAllJob->blockIdx, obj->runtime->readAllJob->datasetIdx, NVM_REQ_PENDING);
                Nvm_StateMachine_Immediate_Read_Entry(obj, obj->runtime->readAllJob->blockIdx, obj->runtime->readAllJob->datasetIdx, NVM_BLOCK_RETRY_NUM, NULL);
                obj->runtime->readAllJob->job = NVM_READALLFSM_READ;
            }
            else
            {
                obj->runtime->readAllJob->blockIdx++;
                obj->runtime->readAllJob->job = NVM_READALLFSM_READBLOCK;
            }
            break;
        }
    case NVM_READALLFSM_READ:
        {
            /* Execute immediate read. */
            Nvm_StateMachine_Immediate_Read(obj);
            if(NVM_IMMEDIATE_READ_FINISH(obj) || NVM_IMMEDIATE_READ_FAIL(obj) || NVM_IMMEDIATE_READ_FATAL(obj))
            {
                obj->runtime->readAllJob->job = NVM_READALLFSM_CHECK;
            }
            break;
        }
    case NVM_READALLFSM_CHECK:
        {
            /* Check the validity of data within the block. */
            Nvm_Block_DataRepair(obj->blockTable, obj->method, obj->runtime->readAllJob->blockIdx, obj->runtime->readAllJob->datasetIdx);
            Nvm_Block_SetState(obj->blockTable, obj->runtime->readAllJob->blockIdx, obj->runtime->readAllJob->datasetIdx, NVM_BLOCK_GET_INT_STATE(obj->blockTable, obj->runtime->readAllJob->blockIdx, obj->runtime->readAllJob->datasetIdx));
            obj->runtime->readAllJob->datasetIdx++;
            obj->runtime->readAllJob->job = NVM_READALLFSM_READENTRY;
            break;
        }
    case NVM_READALLFSM_FINISH:
        {
            /* Complete response request. */
            Nvm_Queue_RemoveFirstItem(obj->runtime->queue);
            obj->runtime->nextState = NVM_STATE_IDLE;
            break;
        }
    case NVM_READALLFSM_ERROR:
        {
            /* Complete response request. */
            Nvm_Queue_RemoveFirstItem(obj->runtime->queue);
            obj->runtime->nextState = NVM_STATE_IDLE;
            break;
        }
    default:
        {
            break;
        }
    }
}

void Nvm_StateMachine_WriteAll_Entry(const NvmType *obj)
{
    obj->runtime->writeAllJob->job      = NVM_WRITEALLFSM_WRITEBLOCK;
    obj->runtime->writeAllJob->blockIdx = 0;
}

void Nvm_StateMachine_WriteAll(const NvmType *obj)
{
    switch(obj->runtime->writeAllJob->job)
    {
    case NVM_WRITEALLFSM_WRITEBLOCK:
        {
            /* Mark the state machine as finished after traversing all blocks. */
            if(obj->runtime->writeAllJob->blockIdx < obj->blockTable->itemsNum)
            {
                if(NVM_BLOCK_IS_NORMAL(obj->blockTable, obj->runtime->writeAllJob->blockIdx))
                {
                    obj->runtime->writeAllJob->datasetIdx = 0;
                    obj->runtime->writeAllJob->job    = NVM_WRITEALLFSM_WRITEENTRY;
                }
                else
                {
                    obj->runtime->writeAllJob->blockIdx++;
                }
            }
            else
            {
                obj->runtime->writeAllJob->job = NVM_WRITEALLFSM_FINISH;
            }
            break;
        }
    case NVM_WRITEALLFSM_WRITEENTRY:
        {
            /* Execute the preparation program before immediate writeing. */
            if(obj->runtime->writeAllJob->datasetIdx < NVM_BLOCK_GET_DATASETNUM(obj->blockTable, obj->runtime->writeAllJob->blockIdx))
            {
                obj->runtime->writeAllJob->blockDatabuff = NVM_BLOCK_GET_TEMP_RAM_BUFF_PTR(obj->blockTable, obj->runtime->writeAllJob->blockIdx);
                if(Nvm_Block_GetWriteData(obj->blockTable, obj->runtime->writeAllJob->blockIdx, obj->runtime->writeAllJob->datasetIdx, obj->method, obj->runtime->writeAllJob->blockDatabuff))
                {
                    Nvm_Block_SetState(obj->blockTable, obj->runtime->writeAllJob->blockIdx, obj->runtime->writeAllJob->datasetIdx, NVM_REQ_PENDING);
                    Nvm_StateMachine_Immediate_Write_Entry(obj, obj->runtime->writeAllJob->blockIdx, obj->runtime->writeAllJob->datasetIdx, NVM_BLOCK_RETRY_NUM, NULL);
                    obj->runtime->writeAllJob->job = NVM_WRITEALLFSM_WRITE;
                }
                else
                {
                    obj->runtime->writeAllJob->datasetIdx++;
                }
            }
            else
            {
                obj->runtime->writeAllJob->blockIdx++;
                obj->runtime->writeAllJob->job = NVM_WRITEALLFSM_WRITEBLOCK;
            }
            break;
        }
    case NVM_WRITEALLFSM_WRITE:
        {
            /* Execute immediate write. */
            Nvm_StateMachine_Immediate_Write(obj);
            if(NVM_IMMEDIATE_WRITE_FINISH(obj) || NVM_IMMEDIATE_WRITE_FAIL(obj) || NVM_IMMEDIATE_WRITE_FATAL(obj))
            {
                Nvm_Block_SetState(obj->blockTable, obj->runtime->writeAllJob->blockIdx, obj->runtime->writeAllJob->datasetIdx, NVM_BLOCK_GET_INT_STATE(obj->blockTable, obj->runtime->writeAllJob->blockIdx, obj->runtime->writeAllJob->datasetIdx));
                obj->runtime->writeAllJob->datasetIdx++;
                obj->runtime->writeAllJob->job = NVM_WRITEALLFSM_WRITEENTRY;
            }
            break;
        }
    case NVM_WRITEALLFSM_FINISH:
        {
            /* Complete response request. */
            Nvm_Queue_RemoveFirstItem(obj->runtime->queue);
            obj->runtime->nextState = NVM_STATE_IDLE;
            break;
        }
    case NVM_WRITEALLFSM_ERROR:
        {
            /* Complete response request. */
            Nvm_Queue_RemoveFirstItem(obj->runtime->queue);
            obj->runtime->nextState = NVM_STATE_IDLE;
            break;
        }
    default:
        {
            break;
        }
    }
}

void Nvm_StateMachine_EraseAll_Entry(const NvmType *obj)
{
    obj->runtime->eraseAllJob->blockIdx = 0;
    obj->runtime->eraseAllJob->job      = NVM_ERASEALLFSM_ERASEBLOCK;
}

void Nvm_StateMachine_EraseAll(const NvmType *obj)
{
    switch(obj->runtime->eraseAllJob->job)
    {
    case NVM_ERASEALLFSM_ERASEBLOCK:
        {
            /* Mark the state machine as finished after traversing all blocks. */
            if(obj->runtime->eraseAllJob->blockIdx < obj->blockTable->itemsNum)
            {
                obj->runtime->eraseAllJob->datasetIdx = 0;
                obj->runtime->eraseAllJob->job    = NVM_ERASEALLFSM_ERASEENTRY;
            }
            else
            {
                obj->runtime->eraseAllJob->job = NVM_ERASEALLFSM_FINISH;
            }
            break;
        }
    case NVM_ERASEALLFSM_ERASEENTRY:
        {
            /* Execute the preparation program before erasing immediately. */
            if(obj->runtime->eraseAllJob->datasetIdx < NVM_BLOCK_GET_DATASETNUM(obj->blockTable, obj->runtime->eraseAllJob->blockIdx))
            {
                obj->runtime->eraseAllJob->blockDatabuff = NVM_BLOCK_GET_TEMP_RAM_BUFF_PTR(obj->blockTable, obj->runtime->eraseAllJob->blockIdx);
                Nvm_Block_GetRomData(obj->blockTable, obj->runtime->eraseAllJob->blockIdx, obj->runtime->eraseAllJob->datasetIdx, obj->runtime->eraseAllJob->blockDatabuff);
                Nvm_Block_SetState(obj->blockTable, obj->runtime->eraseAllJob->blockIdx, obj->runtime->eraseAllJob->datasetIdx, NVM_REQ_PENDING);
                Nvm_StateMachine_Immediate_Write_Entry(obj, obj->runtime->eraseAllJob->blockIdx, obj->runtime->eraseAllJob->datasetIdx, NVM_BLOCK_RETRY_NUM, NULL);
                obj->runtime->eraseAllJob->job = NVM_ERASEALLFSM_ERASE;
            }
            else
            {
                obj->runtime->eraseAllJob->blockIdx++;
                obj->runtime->eraseAllJob->job = NVM_ERASEALLFSM_ERASEBLOCK;
            }
            break;
        }
    case NVM_ERASEALLFSM_ERASE:
        {
            /* Execute immediate erase. */
            Nvm_StateMachine_Immediate_Write(obj);
            if(NVM_IMMEDIATE_WRITE_FINISH(obj) || NVM_IMMEDIATE_WRITE_FAIL(obj) || NVM_IMMEDIATE_WRITE_FATAL(obj))
            {
                obj->runtime->eraseAllJob->job = NVM_ERASEALLFSM_READBACK;
            }
            break;
        }
    case NVM_ERASEALLFSM_READBACK:
        {
            /* Performing operations to read back data. */
            obj->runtime->eraseAllJob->blockRombuff = NVM_BLOCK_GET_ROM_BUFF_PTR(obj->blockTable, obj->runtime->eraseAllJob->blockIdx, obj->runtime->eraseAllJob->datasetIdx);
            Nvm_Block_WriteDataToRamBlock(obj->blockTable, obj->method, obj->runtime->eraseAllJob->blockIdx, obj->runtime->eraseAllJob->datasetIdx, obj->runtime->eraseAllJob->blockRombuff);
            Nvm_Block_SetState(obj->blockTable, obj->runtime->eraseAllJob->blockIdx, obj->runtime->eraseAllJob->datasetIdx, NVM_BLOCK_GET_INT_STATE(obj->blockTable, obj->runtime->eraseAllJob->blockIdx, obj->runtime->eraseAllJob->datasetIdx));
            obj->runtime->eraseAllJob->datasetIdx++;
            obj->runtime->eraseAllJob->job = NVM_ERASEALLFSM_ERASEENTRY;
            break;
        }
    case NVM_ERASEALLFSM_FINISH:
        {
            /* Complete response request. */
            Nvm_Queue_RemoveFirstItem(obj->runtime->queue);
            obj->runtime->nextState = NVM_STATE_IDLE;
            break;
        }
    case NVM_WRITEALLFSM_ERROR:
        {
            /* Complete response request. */
            Nvm_Queue_RemoveFirstItem(obj->runtime->queue);
            obj->runtime->nextState = NVM_STATE_IDLE;
            break;
        }
    default:
        {
            break;
        }
    }
}

void Nvm_StateMachine_Erase_Entry(const NvmType *obj)
{
    uint16_t localBlockIdx;
    uint16_t localDatasetIdx;

    localBlockIdx     = obj->runtime->queue->members[0].blockIdx;
    localDatasetIdx   = obj->runtime->queue->members[0].datasetIdx;

    Nvm_Block_SetState(obj->blockTable, localBlockIdx, localDatasetIdx, NVM_REQ_PENDING);

    obj->runtime->eraseJob->blockIdx      = localBlockIdx;
    obj->runtime->eraseJob->datasetIdx    = localDatasetIdx;
    obj->runtime->eraseJob->blockDatabuff = NVM_BLOCK_GET_TEMP_RAM_BUFF_PTR(obj->blockTable, localBlockIdx);
    obj->runtime->eraseJob->job           = NVM_ERASEFSM_INIT;

    Nvm_Block_GetRomData(obj->blockTable, obj->runtime->eraseJob->blockIdx, obj->runtime->eraseJob->datasetIdx, obj->runtime->eraseJob->blockDatabuff);
}

void Nvm_StateMachine_Erase(const NvmType *obj)
{
    switch(obj->runtime->eraseJob->job)
    {
    case NVM_ERASEFSM_INIT:
        {
            /* If the type of the block is REDUNDANT, the state machine will be marked as a redundant state. */
            /* If not, it will be judged whether it is an immediate state. */
            /* Otherwise, it will be marked as an immediate state. */
            if(NVM_BLOCK_IS_REDUNDANT(obj->blockTable, obj->runtime->eraseJob->blockIdx))
            {
                obj->runtime->eraseJob->job = NVM_ERASEFSM_REDUNDANT_MAIN;   
            }
            else
            {
                Nvm_StateMachine_Immediate_Write_Entry(obj, obj->runtime->eraseJob->blockIdx, obj->runtime->eraseJob->datasetIdx, NVM_BLOCK_RETRY_NUM, NULL);
                obj->runtime->eraseJob->job = NVM_ERASEFSM_IMMEDIATE; 
            }
            break;
        }
    case NVM_ERASEFSM_IMMEDIATE:
        {
            
            /* If the immediate execution is successful or an error occurs, */
            /* mark the state machine to the read back state.*/
            Nvm_StateMachine_Immediate_Write(obj);
            if(NVM_IMMEDIATE_WRITE_FINISH(obj) || NVM_IMMEDIATE_WRITE_FAIL(obj) || NVM_IMMEDIATE_WRITE_FATAL(obj))
            {
                Nvm_StateMachine_Immediate_Read_Entry(obj, obj->runtime->eraseJob->blockIdx, obj->runtime->eraseJob->datasetIdx, NVM_BLOCK_RETRY_NUM, NULL);
                obj->runtime->eraseJob->job = NVM_ERASEFSM_READBACK;
            }
            break;
        }
    case  NVM_ERASEFSM_REDUNDANT_MAIN:
        {
            /* Execute the erase main area of a REDUNDANT type block. */
            obj->runtime->writeJob->blockBackUp = false;
            obj->runtime->writeJob->datasetIdx = NVM_BLOCK_MAIN_IDX;
            Nvm_StateMachine_Immediate_Write_Entry(obj, obj->runtime->writeJob->blockIdx, obj->runtime->writeJob->datasetIdx, NVM_BLOCK_RETRY_NUM, NULL);
            obj->runtime->writeJob->job = NVM_WRITEFSM_IMMEDIATE;
            break;
        }
    case NVM_ERASEFSM_REDUNDANT_BACKUP:
        {
            /* Execute the erase backup area of a REDUNDANT type block. */
            obj->runtime->writeJob->blockBackUp = true;
            obj->runtime->writeJob->datasetIdx = NVM_BLOCK_BACKUP_IDX;
            Nvm_StateMachine_Immediate_Write_Entry(obj, obj->runtime->writeJob->blockIdx, obj->runtime->writeJob->datasetIdx, NVM_BLOCK_RETRY_NUM, NULL);
            obj->runtime->writeJob->job = NVM_WRITEFSM_IMMEDIATE;
            break;
        }
    case NVM_ERASEFSM_READBACK:
        {
            /* Performing operations to read back data. */
            Nvm_StateMachine_Immediate_Read(obj);
            if(NVM_IMMEDIATE_READ_FINISH(obj) || NVM_IMMEDIATE_READ_FAIL(obj) || NVM_IMMEDIATE_READ_FATAL(obj))
            {
                obj->runtime->eraseJob->job = NVM_ERASEFSM_CHECK;
            }
            break;
        }
    case NVM_ERASEFSM_CHECK:
        {
            /* Check the validity of data within the block. */
            Nvm_Block_DataRepair(obj->blockTable, obj->method, obj->runtime->eraseJob->blockIdx, obj->runtime->eraseJob->datasetIdx);
            Nvm_Block_SetState(obj->blockTable, obj->runtime->eraseJob->blockIdx, obj->runtime->eraseJob->datasetIdx, NVM_BLOCK_GET_INT_STATE(obj->blockTable, obj->runtime->eraseJob->blockIdx, obj->runtime->eraseJob->datasetIdx));
            /* Determine the type of block. */
            if(NVM_BLOCK_IS_REDUNDANT(obj->blockTable, obj->runtime->eraseJob->blockIdx))
            {
                if(obj->runtime->eraseJob->blockBackUp)
                {
                    obj->runtime->eraseJob->job = NVM_ERASEFSM_FINISH;
                }
                else
                {
                    obj->runtime->eraseJob->job = NVM_ERASEFSM_REDUNDANT_BACKUP;   
                }
            }
            else
            {
                obj->runtime->eraseJob->job = NVM_ERASEFSM_FINISH;
            }
            break;
        }
    case NVM_ERASEFSM_FINISH:
        {
            /* Complete response request. */
            Nvm_Queue_RemoveFirstItem(obj->runtime->queue);
            obj->runtime->nextState = NVM_STATE_IDLE;
            break;
        }
    case NVM_ERASEFSM_ERASEERROR:
        {
            /* Complete response request. */
            Nvm_Queue_RemoveFirstItem(obj->runtime->queue);
            obj->runtime->nextState = NVM_STATE_IDLE;
            break;
        }
    default:
        {
            break;
        }
    }
}

void Nvm_StateMachine_Immediate_Read_Entry(const NvmType *obj, uint16_t blockIdx, uint16_t datasetIdx, uint8_t retry, NvmNotificationPtrType notificationPtr)
{
    obj->runtime->immediateReadJob->job = NVM_IMMEDIATEREADFSM_MAIN;

    Nvm_Block_SetInternalState(obj->blockTable, blockIdx, datasetIdx, NVM_REQ_PENDING);

    Nvm_Block_ClrRamBuff(obj->blockTable, blockIdx, datasetIdx);

    obj->runtime->immediateReadJob->mainRetryCnt    = retry;
    obj->runtime->immediateReadJob->backupRetryCnt  = retry;
    obj->runtime->immediateReadJob->notificationPtr = notificationPtr;
    obj->runtime->immediateReadJob->blockIdx        = blockIdx;
    obj->runtime->immediateReadJob->datasetIdx      = datasetIdx;
    obj->runtime->immediateReadJob->blockNumber     = obj->blockTable->items[blockIdx].number + datasetIdx;
    obj->runtime->immediateReadJob->blockDatabuff   = NVM_BLOCK_GET_TEMP_RAM_BUFF_PTR(obj->blockTable, blockIdx);
    obj->runtime->immediateReadJob->blockUseSize    = ALIGN8BYTE(Nvm_Block_CalcUseSize(obj->blockTable, blockIdx));
}

static void Nvm_StateMachine_Immediate_Read(const NvmType *obj)
{
    switch(obj->runtime->immediateReadJob->job)
    {
    case NVM_IMMEDIATEREADFSM_MAIN:
        {
            /* Obtain lower level module status. */
            if(NVM_STATUS_IDLE == obj->method->getStatus())
            {
                /* Request lower level module to read data. */
                if(obj->method->read(obj->runtime->immediateReadJob->blockNumber, NVM_BLOCK_MAIN_IDX, obj->runtime->immediateReadJob->blockDatabuff, obj->runtime->immediateReadJob->notificationPtr))
                {
                    /* Request successful, waiting for the response from the lower level module to complete. */
                    obj->runtime->immediateReadJob->job = NVM_IMMEDIATEREADFSM_WMF;
                }
                else
                {
                    /* If the request fails, it will be retried. */
                    Nvm_Block_SetInternalState(obj->blockTable, obj->runtime->immediateReadJob->blockIdx, obj->runtime->immediateReadJob->datasetIdx, NVM_REQ_NOT_OK);
                    obj->runtime->immediateReadJob->job = NVM_IMMEDIATEREADFSM_MAIN_RETRY;
                }
            }
            break;
        }
    case NVM_IMMEDIATEREADFSM_MAIN_RETRY:
        {
            /* Wait for an attempt to resend the request to the lower module. */
            if(obj->runtime->immediateReadJob->mainRetryCnt > 0)
            {
                obj->runtime->immediateReadJob->mainRetryCnt--;
                obj->runtime->immediateReadJob->job = NVM_IMMEDIATEREADFSM_MAIN;
            }
            else
            {
                /* If the retry count exceeds the threshold, mark the state machine as an error. */
                obj->runtime->immediateReadJob->job = NVM_IMMEDIATEREADFSM_READERROR;
            }
            break;
        }
    case NVM_IMMEDIATEREADFSM_WMF:
        {
            /* Waiting for lower level request status to respond successfully, response not successful, response error. */
            if(NVM_JOB_RESULT_OK == obj->method->getJobResult())
            {
                /* Wait for the lower level request status to respond successfully, then mark the internal management status of the block as FINISH. */
                Nvm_Block_WriteDataToBuff(obj->blockTable, obj->method, obj->runtime->immediateReadJob->blockIdx, obj->runtime->immediateReadJob->datasetIdx, obj->runtime->immediateReadJob->blockDatabuff);
                Nvm_Block_SetInternalState(obj->blockTable, obj->runtime->immediateReadJob->blockIdx, obj->runtime->immediateReadJob->datasetIdx, NVM_REQ_OK);
                obj->runtime->immediateReadJob->job = NVM_IMMEDIATEREADFSM_FINISH;
            }
            else if(NVM_JOB_RESULT_NOT_OK == obj->method->getJobResult())
            {
                /* If the request status at the lower level is unsuccessful, mark the internal management status of the block as ERROR. */
                Nvm_Block_ClrBlockRamBuff(obj->blockTable, obj->runtime->immediateReadJob->blockIdx, obj->runtime->immediateReadJob->datasetIdx);
                Nvm_Block_SetInternalState(obj->blockTable, obj->runtime->immediateReadJob->blockIdx, obj->runtime->immediateReadJob->datasetIdx, NVM_REQ_NOT_OK);
                obj->runtime->immediateReadJob->job = NVM_IMMEDIATEREADFSM_READERROR;
            }
            else if(NVM_JOB_RESULT_BL_INCONSISTENT == obj->method->getJobResult())
            {
                /* Wait for the lower level request status to be block integrity error, then mark the internal management status of the block as ERROR. */
                Nvm_Block_ClrBlockRamBuff(obj->blockTable, obj->runtime->immediateReadJob->blockIdx, obj->runtime->immediateReadJob->datasetIdx);
                Nvm_Block_SetInternalState(obj->blockTable, obj->runtime->immediateReadJob->blockIdx, obj->runtime->immediateReadJob->datasetIdx, NVM_REQ_INTEGRITY_FAILED);
                obj->runtime->immediateReadJob->job = NVM_IMMEDIATEREADFSM_READERROR;
            }
            else if(NVM_JOB_RESULT_BL_INVALID == obj->method->getJobResult())
            {
                /* If the request status of the lower level is an undefined block, the internal management status of the block will be marked as ERROR. */
                Nvm_Block_ClrBlockRamBuff(obj->blockTable, obj->runtime->immediateReadJob->blockIdx, obj->runtime->immediateReadJob->datasetIdx);
                Nvm_Block_SetInternalState(obj->blockTable, obj->runtime->immediateReadJob->blockIdx, obj->runtime->immediateReadJob->datasetIdx, NVM_REQ_UNINT);
                obj->runtime->immediateReadJob->job = NVM_IMMEDIATEREADFSM_READERROR;
            }
            break;
        }
    case NVM_IMMEDIATEREADFSM_FINISH:
        {
            break;
        }
    case NVM_IMMEDIATEREADFSM_READERROR:
        {
            break;
        }
    default:
        {
            break;
        }
    }
}

static void Nvm_StateMachine_Immediate_Write_Entry(const NvmType *obj, uint16_t blockIdx, uint16_t datasetIdx, uint8_t retry, NvmNotificationPtrType notificationPtr)
{
    uint16_t crcLen;
    
    
    obj->runtime->immediateWriteJob->job = NVM_IMMEDIATEWRITEFSM_MAIN;

    Nvm_Block_SetInternalState(obj->blockTable, blockIdx, datasetIdx, NVM_REQ_PENDING);

    obj->runtime->immediateWriteJob->retry           = retry;
    obj->runtime->immediateWriteJob->notificationPtr = notificationPtr;
    obj->runtime->immediateWriteJob->blockIdx        = blockIdx;
    obj->runtime->immediateWriteJob->datasetIdx      = datasetIdx;
    obj->runtime->immediateWriteJob->blockNumber     = obj->blockTable->items[blockIdx].number + datasetIdx;
    obj->runtime->immediateWriteJob->blockDatabuff   = NVM_BLOCK_GET_TEMP_RAM_BUFF_PTR(obj->blockTable, blockIdx);

    if(NVM_BLOCK_IS_CRC(obj->blockTable, obj->runtime->writeJob->blockIdx))
    {
        crcLen                                                 = NVM_BLOCK_GET_CRC_LEN(obj->blockTable, obj->runtime->writeJob->blockIdx);
        obj->runtime->immediateWriteJob->blockDatabuff[crcLen] = obj->method->crc8(obj->runtime->immediateWriteJob->blockDatabuff, crcLen);
    }
}

static void Nvm_StateMachine_Immediate_Write(const NvmType *obj)
{
    switch(obj->runtime->immediateWriteJob->job)
    {
    case NVM_IMMEDIATEWRITEFSM_MAIN:
        {
            /* Obtain lower level module status. */
            if(NVM_STATUS_IDLE == obj->method->getStatus())
            {
                /* Request lower level module to write data. */
                if(obj->method->write(obj->runtime->immediateWriteJob->blockNumber, obj->runtime->immediateWriteJob->blockDatabuff, obj->runtime->immediateWriteJob->notificationPtr))
                {
                    /* Request successful, waiting for the response from the lower level module to complete. */
                    obj->runtime->immediateWriteJob->job = NVM_IMMEDIATEWRITEFSM_WMF;
                }
                else
                {
                    /* If the request fails, it will be retried. */
                    obj->runtime->immediateWriteJob->job = NVM_IMMEDIATEWRITEFSM_RETRY;
                }
            }
            /* If the lower level module is in a state of responding to a request, calibrate the state machine to WAITMANAGE. */
            else if(NVM_STATUS_GC == obj->method->getStatus())
            {
                obj->runtime->immediateWriteJob->job = NVM_IMMEDIATEWRITEFSM_WAITMANAGE;
            }
            else if(NVM_STATUS_READ == obj->method->getStatus())
            {
                obj->runtime->immediateWriteJob->job = NVM_IMMEDIATEWRITEFSM_WAITMANAGE;
            }
            else if(NVM_STATUS_WRITE == obj->method->getStatus())
            {
                obj->runtime->immediateWriteJob->job = NVM_IMMEDIATEWRITEFSM_WAITMANAGE;
            }
            else
            {
                obj->runtime->immediateWriteJob->job = NVM_IMMEDIATEWRITEFSM_ERROR;
            }
            break;
        }
    case NVM_IMMEDIATEWRITEFSM_RETRY:
        {
            /* Wait for an attempt to resend the request to the lower module. */
            if(obj->runtime->immediateWriteJob->retry > 0)
            {
                obj->runtime->immediateWriteJob->retry--;
                obj->runtime->immediateWriteJob->job = NVM_IMMEDIATEWRITEFSM_MAIN;
            }
            else
            {
                /* If the retry count exceeds the threshold, mark the state machine as an error. */
                obj->runtime->immediateWriteJob->job = NVM_IMMEDIATEWRITEFSM_WRITEERROR;
            }
            break;
        }
    case NVM_IMMEDIATEWRITEFSM_WMF:
        {
            /* Waiting for lower level request status to respond successfully, response not successful, response error. */
            if(NVM_JOB_RESULT_OK == obj->method->getJobResult())
            {
                /* Wait for the lower level request status to respond successfully, then mark the internal management status of the block as FINISH. */
                Nvm_Block_SetInternalState(obj->blockTable, obj->runtime->immediateWriteJob->blockIdx, obj->runtime->immediateWriteJob->datasetIdx, NVM_REQ_OK);
                obj->runtime->immediateWriteJob->job = NVM_IMMEDIATEWRITEFSM_FINISH;
            }
            else if(NVM_JOB_RESULT_NOT_OK == obj->method->getJobResult())
            {
                /* If the request status at the lower level is unsuccessful, mark the internal management status of the block as ERROR. */
                Nvm_Block_SetInternalState(obj->blockTable, obj->runtime->immediateWriteJob->blockIdx, obj->runtime->immediateWriteJob->datasetIdx, NVM_REQ_NOT_OK);
                obj->runtime->immediateWriteJob->job = NVM_IMMEDIATEWRITEFSM_WRITEERROR;
            }
            else if(NVM_JOB_RESULT_BL_INCONSISTENT == obj->method->getJobResult())
            {
                /* Wait for the lower level request status to be block integrity error, then mark the internal management status of the block as ERROR. */
                Nvm_Block_SetInternalState(obj->blockTable, obj->runtime->immediateWriteJob->blockIdx, obj->runtime->immediateWriteJob->datasetIdx, NVM_REQ_INTEGRITY_FAILED);
                obj->runtime->immediateWriteJob->job = NVM_IMMEDIATEWRITEFSM_WRITEERROR;
            }
            else if(NVM_JOB_RESULT_BL_INVALID == obj->method->getJobResult())
            {
                /* If the request status of the lower level is an undefined block, the internal management status of the block will be marked as ERROR. */
                Nvm_Block_SetInternalState(obj->blockTable, obj->runtime->immediateWriteJob->blockIdx, obj->runtime->immediateWriteJob->datasetIdx, NVM_REQ_UNINT);
                obj->runtime->immediateWriteJob->job = NVM_IMMEDIATEWRITEFSM_WRITEERROR;
            }
            break;
        }
    case NVM_IMMEDIATEWRITEFSM_FINISH:
        {
            break;
        }
    case NVM_IMMEDIATEWRITEFSM_WRITEERROR:
        {
            break;
        }
    case NVM_IMMEDIATEWRITEFSM_WAITMANAGE:
        {
            /* When waiting for the status of the lower level module to be idle, mark the status as requesting the lower level module to write. */
            if(NVM_STATUS_IDLE == obj->method->getStatus())
            {
                obj->runtime->immediateWriteJob->job = NVM_IMMEDIATEWRITEFSM_MAIN;
            }
            break;
        }
    default:
        {
            break;
        }
    }
}
