/*
 * Copyright (C) 2020-2021 Aloysius Indrayanto
 *
 * This file is part of the eCxx library, see LICENSE file for the license details.
 */


#ifndef ECXX_TEST_INCLUDED_FROM_SLOT_FS_COMMON_H
//
// Instantiate a raw-mode memory card reader and writer
//
static eCxx::RawMemoryCard rmc;
#endif


//
// Helper functions
//
template <typename UART_T>
static inline void printRMCResultCodeString(UART_T& uart, eCxx::RawMemoryCard::RMCResultCode rc)
{
    // Yield first
    App::yield();

    // No error
    if(rc == eCxx::RawMemoryCard::RMCResultCode::NoError) {
        uart.writeStringln( PGM_STR("SUCCESS") );
        return;
    }

    // Error
    uart.writeString  ( PGM_STR("FAILED [")                    );
    uart.writeString  ( eCxx::RawMemoryCard::resultCodeStr(rc) );
    uart.writeStringln( PGM_STR("]\nHalting here!\n")          );
    App::halt();
}


template <typename UART_T>
static inline void printResultBool(UART_T& uart, bool success, bool haltIfFailed)
{
    // Yield first
    App::yield();

    // No error
    if(success) {
        uart.writeStringln( PGM_STR("SUCCESS") );
        return;
    }

    // Error
    uart.writeStringln( PGM_STR("FAILED") );

    // Halt as needed
    if(haltIfFailed) {
        uart.writeStringln( PGM_STR("Halting here!\n") );
        App::halt();
    }
}


template <typename UART_T>
static inline void printHexSeqStringln128x4(UART_T& uart, const eCxx::uint8_t* buff512)
{
    for(eCxx::uint8_t i = 0; i < 4; ++i) {
        App::yield();
        uart.writeHexSeqStringln(buff512 + 128 * i, 128, true, false);
    }
}


#ifndef ECXX_TEST_INCLUDED_FROM_SLOT_FS_COMMON_H

static void eCxx_user_init_rmcInitAndTest()
{
    // Initialize UART
    auto& u = App::uart();
    u.begin(115200);

    // Initialize the memory card
    u.writeString( PGM_STR("Initializing memory card ... ") );
    auto rc = rmc.begin();
    printRMCResultCodeString(u, rc);
    u.write('\n');

    // Print some information about the memory card
    eCxx::uint8_t  u8a[16];
    eCxx::uint16_t u16;
    eCxx::uint32_t u32;

    u.writeString( PGM_STR("Get sector size ... ") );
    rc = rmc.getSectorSize(u16);
    printRMCResultCodeString(u, rc);
    u.writeDecStringln(u16);
    u.write('\n');

    u.writeString( PGM_STR("Get sector count ... ") );
    rc = rmc.getSectorCount(u32);
    printRMCResultCodeString(u, rc);
    u.writeDecStringln(u32);
    u.write('\n');

    u.writeString( PGM_STR("Get erase block size ... ") );
    rc = rmc.getEraseBlockSize(u32);
    printRMCResultCodeString(u, rc);
    u.writeDecStringln(u32);
    u.write('\n');

    u.writeString( PGM_STR("Get card type ... ") );
    rc = rmc.getCardType(u8a[0]);
    printRMCResultCodeString(u, rc);
    u.writeStringln( eCxx::RawMemoryCard::cardTypeStr(u8a[0]) );
    u.write('\n');

    const bool isMMC = u8a[0] & eCxx::RawMemoryCard::CT_MMC;

    u.writeString( PGM_STR("Get CSD register ... ") );
    rc = rmc.getCSD(u8a);
    printRMCResultCodeString(u, rc);
    u.writeHexSeqStringln(u8a, 16, true, true);
    u.write('\n');

    u.writeString( PGM_STR("Get CID register ... ") );
    rc = rmc.getCID(u8a);
    printRMCResultCodeString(u, rc);
    u.writeHexSeqStringln(u8a, 16, true, true);
    u.write('\n');

    u.writeString( PGM_STR("Get OCR register ... ") );
    rc = rmc.getOCR( reinterpret_cast<eCxx::uint8_t(&)[4]>(u8a) );
    printRMCResultCodeString(u, rc);
    u.writeHexSeqStringln(u8a, 4, true, true);
    u.write('\n');

    if(!isMMC) {
        eCxx::uint8_t u8a64[64];
        u.writeString( PGM_STR("Get status ... ") );
        rc = rmc.getStatus(u8a64);
        printRMCResultCodeString(u, rc);
        u.writeHexSeqStringln(u8a64, 64, true, true);
        u.write('\n');
    }

    // Test one block read
    eCxx::uint8_t buff[eCxx::RawMemoryCard::SectorSize];

    u.writeString( PGM_STR("Read one block #0 ... ") );
    rc = rmc.readOneBlock( 0, buff, sizeof(buff) );
    printRMCResultCodeString(u, rc);
    printHexSeqStringln128x4(u, buff);
    u.write('\n');

#if 1
    // Test one block write
    u.writeString( PGM_STR("Write one block #0 ... ") );
    const eCxx::uint8_t at0 = buff[0];
    const eCxx::uint8_t at8 = buff[8];
    memset( buff, 0, sizeof(buff) );
    buff[0] = at0 + 1;
    buff[8] = at8;
    rc = rmc.writeOneBlock( 0, buff, sizeof(buff) );
    printRMCResultCodeString(u, rc);

    // Test one block read
    u.writeString( PGM_STR("Read one block #0 ... ") );
    rc = rmc.readOneBlock( 0, buff, sizeof(buff) );
    printRMCResultCodeString(u, rc);
    printHexSeqStringln128x4(u, buff);
    u.write('\n');
#endif

    // Test streaming read
    u.writeString( PGM_STR("Streaming read 2 blocks from #0 ... ") );
    rc = rmc.streamingReadBegin(0, 2);
    printRMCResultCodeString(u, rc);
    for(eCxx::uint8_t b = 0; b < 2; ++b) {
        for(eCxx::uint8_t i = 0; i < 4; ++i) {
            App::yield();
            u.writeString( PGM_STR("Reading 128 bytes ... ") );
            u.writeDecStringln( rmc.streamingReadGet(buff + 128 * i, 128) );
        }
        printHexSeqStringln128x4(u, buff);
    }
    u.write('\n');

    u.writeString( PGM_STR("Streaming read 7 blocks from #0 ... ") );
    rc = rmc.streamingReadBegin(0, 7);
    printRMCResultCodeString(u, rc);
    for(eCxx::uint8_t i = 0; i < 3; ++i) {
        App::yield();
        u.writeString( PGM_STR("Reading 400 bytes ... ") );
        u.writeDecStringln( rmc.streamingReadGet(buff, 400) );
    }
    u.writeString( PGM_STR("Ending streaming read prematurely ... ") );
    rc = rmc.streamingReadEnd();
    printRMCResultCodeString(u, rc);
    u.write('\n');

    // Test one block read
    u.writeString( PGM_STR("Read one block #0 ... ") );
    rc = rmc.readOneBlock( 0, buff, sizeof(buff) );
    printRMCResultCodeString(u, rc);
    printHexSeqStringln128x4(u, buff);
    u.write('\n');

#if 1
    // Test streaming write
    u.writeString( PGM_STR("Streaming write 2 blocks from #0 ... ") );
    const eCxx::uint8_t bt0 = buff[0];
    const eCxx::uint8_t bt8 = buff[8];
    memset( buff, 0, sizeof(buff) );
    buff[0] = bt0;
    buff[8] = bt8 + 1;
    rc = rmc.streamingWriteBegin(0, 2);
    printRMCResultCodeString(u, rc);
    for(eCxx::uint8_t b = 0; b < 2; ++b) {
        for(eCxx::uint8_t i = 0; i < 4; ++i) {
            App::yield();
            u.writeString( PGM_STR("Writing 128 bytes ... ") );
            u.writeDecStringln( rmc.streamingWritePut(buff + 128 * i, 128) );
        }
    }

    // Test one block read
    u.writeString( PGM_STR("Read one block #0 ... ") );
    rc = rmc.readOneBlock( 0, buff, sizeof(buff) );
    printRMCResultCodeString(u, rc);
    printHexSeqStringln128x4(u, buff);

    u.writeString( PGM_STR("Read one block #1 ... ") );
    rc = rmc.readOneBlock( 1, buff, sizeof(buff) );
    printRMCResultCodeString(u, rc);
    printHexSeqStringln128x4(u, buff);
    u.write('\n');

    u.writeString( PGM_STR("Streaming write 7 blocks from #1024 ... ") );
    memset( buff, 0, sizeof(buff) );
    rc = rmc.streamingWriteBegin(1024, 7);
    printRMCResultCodeString(u, rc);
    for(eCxx::uint8_t i = 0; i < 3; ++i) {
        App::yield();
        u.writeString( PGM_STR("Writing 400 bytes ... ") );
        u.writeDecStringln( rmc.streamingWritePut(buff, 400) );
    }
    u.writeString( PGM_STR("Ending streaming write prematurely ... ") );
    rc = rmc.streamingWriteEnd();
    printRMCResultCodeString(u, rc);
    u.write('\n');
#endif
}

#endif
