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


#include <eCxx/eCxx_MemoryCard.h>

#include "NodeMCU_Common.h"


//
// Include common source code for SlotFS test application
//
#include "../CommonCode/SlotFS_Common.h"


//
// The init function
//
void eCxx_user_init()
{
    // Initialize application
    initBasicNodeMCUApp(true);

    // Enable the 74LVC541 buffers on the eCxx NodeMCU development board because
    // the SPI pins are buffered
    App::setPinMode  ( eCxx::NodeMCU::D0, eCxx::Application::PinMode::Output ); // GPIO16
    App::digitalWrite( eCxx::NodeMCU::D0, false                              ); // The buffers are active low

    // Initialize and test SlotFS formatting & mounting
    eCxx_user_init_slotfsInitAndTest(true);

    // ----- Perform integrity and verification test on SlotFS -----
    auto& u = App::uart();

    eCxx::PGMCharPointer f1 = PGM_STR("Verify01"      );
    eCxx::PGMCharPointer f2 = PGM_STR("Verify02"      );
    eCxx::PGMCharPointer f3 = PGM_STR("Verify03"      );

    eCxx::PGMCharPointer d1 = PGM_STR("Description 01");
    eCxx::PGMCharPointer d2 = PGM_STR("Description 02");
    eCxx::PGMCharPointer d3 = PGM_STR("Description 03");

    // Open for writing
    printPreTextSlotUserID(u, PGM_STR("Open for writing to new"), f1);
    eCxx::SlotFS::SlotHandle h1 = slotFS.slotWriteNew(f1, d1);
    printResultBool(u, !!h1, true);
    printPreTextSlotUserID(u, PGM_STR("Open for writing to new"), f2);
    eCxx::SlotFS::SlotHandle h2 = slotFS.slotWriteNew(f2, d2);
    printResultBool(u, !!h2, true);
    printPreTextSlotUserID(u, PGM_STR("Open for writing to new"), f3);
    eCxx::SlotFS::SlotHandle h3 = slotFS.slotWriteNew(f3, d3);
    printResultBool(u, !!h3, true);
    App::yield();

    u.write('\n');

    // Preparing test data
    constexpr eCxx::uint16_t DataSize  = 106 * 3; // Must be <= 512
    constexpr eCxx::uint8_t  LoopCount = 8;

    eCxx::uint8_t content1[DataSize];
    eCxx::uint8_t content2[DataSize];
    eCxx::uint8_t content3[DataSize];

    for(eCxx::uint16_t i = 0; i < DataSize; i += 3) {
        content1[i + 0] = 1; content1[i + 1] = 2; content1[i + 2] = 3;
        content2[i + 0] = 4; content2[i + 1] = 5; content2[i + 2] = 6;
        content3[i + 0] = 7; content3[i + 1] = 8; content3[i + 2] = 9;
    }
    App::yield();

    // Write
    u.writeString( PGM_STR("Writing test bytes ...\n    ") );
    for(eCxx::uint8_t i = 0; i < LoopCount; ++i) {
        u.write('1'); slotFS.slotWritePut( h1, content1, sizeof(content1) );
        u.write('2'); slotFS.slotWritePut( h2, content2, sizeof(content2) );
        u.write('3'); slotFS.slotWritePut( h3, content3, sizeof(content3) );
        App::yield();
        u.write('1'); slotFS.slotWritePut( h1, content1, sizeof(content1) );
        u.write('1'); slotFS.slotWritePut( h1, content1, sizeof(content1) );
        u.write('2'); slotFS.slotWritePut( h2, content2, sizeof(content2) );
        App::yield();
        u.write('3'); slotFS.slotWritePut( h3, content3, sizeof(content3) );
        u.write('3'); slotFS.slotWritePut( h3, content3, sizeof(content3) );
        u.write('2'); slotFS.slotWritePut( h2, content2, sizeof(content2) );
        App::yield();
    }
    u.write('\n');

    const eCxx::uint32_t wp1 = slotFS.slotWriteGetCurPos(h1);
    const eCxx::uint32_t wp2 = slotFS.slotWriteGetCurPos(h2);
    const eCxx::uint32_t wp3 = slotFS.slotWriteGetCurPos(h3);

    const eCxx::uint32_t cz1 = slotFS.slotWriteGetCommitedSize(h1);
    const eCxx::uint32_t cz2 = slotFS.slotWriteGetCommitedSize(h2);
    const eCxx::uint32_t cz3 = slotFS.slotWriteGetCommitedSize(h3);

    u.writeString( PGM_STR("Write current position = ") );
    u.writeDecString(wp1); u.write(' ' );
    u.writeDecString(wp2); u.write(' ' );
    u.writeDecString(wp3); u.write('\n');
    u.writeString( PGM_STR("Write committed size   = ") );
    u.writeDecString(cz1); u.write(' ' );
    u.writeDecString(cz2); u.write(' ' );
    u.writeDecString(cz3); u.write('\n');

    u.write('\n');

    // Close
    bool br;

    printPreTextSlotUserID(u, PGM_STR("Closing"), f1);
    br = slotFS.slotClose(h1);
    printResultBool(u, br, true);
    printPreTextSlotUserID(u, PGM_STR("Closing"), f2);
    br = slotFS.slotClose(h2);
    printResultBool(u, br, true);
    printPreTextSlotUserID(u, PGM_STR("Closing"), f3);
    br = slotFS.slotClose(h3);
    printResultBool(u, br, true);
    App::yield();

    u.write('\n');

    // Find
    printPreTextSlotUserID(u, PGM_STR("Searching for"), f1);
    const eCxx::uint16_t sn1 = slotFS.findFirstSlotNumberByUserID(f1);
    printResultFindSlot(u, sn1, true);
    printPreTextSlotUserID(u, PGM_STR("Searching for"), f2);
    const eCxx::uint16_t sn2 = slotFS.findFirstSlotNumberByUserID(f2);
    printResultFindSlot(u, sn2, true);
    printPreTextSlotUserID(u, PGM_STR("Searching for"), f3);
    const eCxx::uint16_t sn3 = slotFS.findFirstSlotNumberByUserID(f3);
    printResultFindSlot(u, sn3, true);
    App::yield();

    u.write('\n');

    // Open for reading
    printPreTextSlotUserID(u, PGM_STR("Open for reading from"), f1);
    h1 = slotFS.slotReadOpen(sn1);
    printResultBool(u, !!h1, true);
    printPreTextSlotUserID(u, PGM_STR("Open for reading from"), f2);
    h2 = slotFS.slotReadOpen(sn2);
    printResultBool(u, !!h2, true);
    printPreTextSlotUserID(u, PGM_STR("Open for reading from"), f3);
    h3 = slotFS.slotReadOpen(sn3);
    printResultBool(u, !!h3, true);
    App::yield();

    u.write('\n');

    // Read and verify
    eCxx::uint8_t readBuff[DataSize];

    #define CMP(REF, N) u.write( memcmp(REF##N, readBuff, sizeof(readBuff) ) ? '0' : (N + '0') )

    u.writeString( PGM_STR("Reading and verifying test bytes ...\n    ") );
    for(eCxx::uint8_t i = 0; i < LoopCount; ++i) {
        slotFS.slotReadGet( h1, readBuff, sizeof(readBuff) ); CMP(content, 1);
        slotFS.slotReadGet( h2, readBuff, sizeof(readBuff) ); CMP(content, 2);
        slotFS.slotReadGet( h3, readBuff, sizeof(readBuff) ); CMP(content, 3);
        App::yield();
        slotFS.slotReadGet( h1, readBuff, sizeof(readBuff) ); CMP(content, 1);
        slotFS.slotReadGet( h1, readBuff, sizeof(readBuff) ); CMP(content, 1);
        slotFS.slotReadGet( h2, readBuff, sizeof(readBuff) ); CMP(content, 2);
        App::yield();
        slotFS.slotReadGet( h3, readBuff, sizeof(readBuff) ); CMP(content, 3);
        slotFS.slotReadGet( h3, readBuff, sizeof(readBuff) ); CMP(content, 3);
        slotFS.slotReadGet( h2, readBuff, sizeof(readBuff) ); CMP(content, 2);
    }
    u.write('\n');

    u.write('\n');

    // Close
    printPreTextSlotUserID(u, PGM_STR("Closing"), f1);
    br = slotFS.slotClose(h1);
    printResultBool(u, br, true);
    printPreTextSlotUserID(u, PGM_STR("Closing"), f2);
    br = slotFS.slotClose(h2);
    printResultBool(u, br, true);
    printPreTextSlotUserID(u, PGM_STR("Closing"), f3);
    br = slotFS.slotClose(h3);
    printResultBool(u, br, true);
    App::yield();

    u.write('\n');

    // Delete
    printPreTextSlotUserID(u, PGM_STR("Freeing"), f1);
    br = slotFS.freeSlot(sn1);
    printResultBool(u, br, true);
    printPreTextSlotUserID(u, PGM_STR("Freeing"), f2);
    br = slotFS.freeSlot(sn2);
    printResultBool(u, br, true);
    printPreTextSlotUserID(u, PGM_STR("Freeing"), f3);
    br = slotFS.freeSlot(sn3);
    printResultBool(u, br, true);
    App::yield();

    u.write('\n');
}


//
// The task function
//
void eCxx_user_task()
{
    // Process event
    eCxx::Application::processEvent();
}
