/*
 * Copyright (c) 2021 Stefan Strobel <stefan.strobel@shimatta.net>
 *
 * This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at https://mozilla.org/MPL/2.0/.
 */

/**
 * @file    test_integration.cpp
 * @brief   integration test implementation for some general functions
 * @author  Stefan Strobel <stefan.strobel@shimatta.net>
 */

#include "test/framework/catch.hpp"
extern "C" {
#include "shellmatta.h"
}
#include <string.h>

static uint32_t write_callCnt = 0u;
static char write_data[1024];
static uint32_t write_length;
static const char *doSomethingArguments;
static uint32_t doSomethingLength;
static char *doSomethingStdin;
static uint32_t doSomethingStdinLength;

static shellmatta_retCode_t writeFct(const char* data, uint32_t length)
{
    write_callCnt ++;
    while((length > 0) && (write_length < sizeof(write_data)))
    {
        write_data[write_length] = *data;
        data ++;
        length --;
        write_length ++;
    }

    return SHELLMATTA_OK;
}

static shellmatta_retCode_t doSomething(shellmatta_handle_t handle, const char *arguments, uint32_t length)
{
    doSomethingArguments = arguments;
    doSomethingLength = length;

    shellmatta_read(handle, &doSomethingStdin, &doSomethingStdinLength);

    shellmatta_printf(handle, "%s - length: %u", arguments, length);
    return SHELLMATTA_OK;
}
shellmatta_cmd_t doSomethingCmd = {(char*)"doSomething", (char*)"do", (char*)"Function does something", (char*)"use me, please", doSomething, NULL};

static shellmatta_retCode_t empty(shellmatta_handle_t handle, const char *arguments, uint32_t length)
{
    shellmatta_printf(handle, "empty - %s - length: %u", arguments, length);
    return SHELLMATTA_OK;
}
shellmatta_cmd_t emptyCmd = {(char*)"empty", NULL, NULL, NULL, empty, NULL};

TEST_CASE( "shellmatta empty function" ) {

    shellmatta_instance_t inst;
    shellmatta_handle_t handle;
    char buffer[1024];
    char historyBuffer[1024];
    char *dummyData =   (char*)"\r\nshellmatta->";

    shellmatta_doInit(  &inst,
                        &handle,
                        buffer,
                        sizeof(buffer),
                        historyBuffer,
                        sizeof(historyBuffer),
                        "shellmatta->",
                        NULL,
                        writeFct);

    write_callCnt = 0u;
    memset(write_data, 0, sizeof(write_data));
    write_length = 0u;

    shellmatta_processData(handle, (char*)"\r", 1);

    CHECK( write_length == 14u);
    REQUIRE( strcmp(dummyData, write_data) == 0);

}

TEST_CASE( "shellmatta heredoc test" ) {

    shellmatta_instance_t inst;
    shellmatta_handle_t handle;
    char buffer[1024];
    char historyBuffer[1024];
    char *dummyData =   (char*)"do this ";
    char *dummyStdin =  (char*)"asdf\r\n"
                        "1234";

    shellmatta_doInit(  &inst,
                        &handle,
                        buffer,
                        sizeof(buffer),
                        historyBuffer,
                        sizeof(historyBuffer),
                        "shellmatta->",
                        NULL,
                        writeFct);
    shellmatta_addCmd(handle, &doSomethingCmd);

    doSomethingArguments = NULL;
    doSomethingLength = 0u;

    shellmatta_processData(handle, (char*)"do this << EOF\r\n"
                                    "asdf\r\n"
                                    "1234\r\n"
                                    "EOF\r\n"
                                , 33);

    CHECK( doSomethingStdinLength == 10u);
    CHECK( strcmp(dummyStdin, doSomethingStdin) == 0);
    CHECK( doSomethingLength == 8u);
    REQUIRE( strcmp(dummyData, doSomethingArguments) == 0);
}

TEST_CASE( "shellmatta heredoc test empty" ) {

    shellmatta_instance_t inst;
    shellmatta_handle_t handle;
    char buffer[1024];
    char historyBuffer[1024];
    char *dummyData =   (char*)"do this ";

    shellmatta_doInit(  &inst,
                        &handle,
                        buffer,
                        sizeof(buffer),
                        historyBuffer,
                        sizeof(historyBuffer),
                        "shellmatta->",
                        NULL,
                        writeFct);
    shellmatta_addCmd(handle, &doSomethingCmd);

    doSomethingArguments = NULL;
    doSomethingLength = 0u;

    shellmatta_processData(handle, (char*)"do this << EOF\r\n"
                                    "EOF\r\n"
                                , 21);

    CHECK( doSomethingStdinLength == 0u);
    CHECK( NULL == doSomethingStdin );
    CHECK( doSomethingLength == 8u);
    REQUIRE( strcmp(dummyData, doSomethingArguments) == 0);
}

TEST_CASE( "shellmatta remove function" ) {

    shellmatta_instance_t inst;
    shellmatta_handle_t handle;
    char buffer[1024];
    char historyBuffer[1024];
    char *dummyData =   (char*)"?\r\n"
                        "doSomething  do  Function does something\r\n"
                        "help         ?   help [command] - print help or usage information\r\n"
                        "\r\nshellmatta->";

    shellmatta_doInit(  &inst,
                        &handle,
                        buffer,
                        sizeof(buffer),
                        historyBuffer,
                        sizeof(historyBuffer),
                        "shellmatta->",
                        NULL,
                        writeFct);
    shellmatta_addCmd(handle, &doSomethingCmd);

    write_callCnt = 0u;
    memset(write_data, 0, sizeof(write_data));
    write_length = 0u;

    shellmatta_processData(handle, (char*)"?\r", 2);

    CHECK( write_length == strlen(dummyData));
    CHECK( strcmp(dummyData, write_data) == 0);


    write_callCnt = 0u;
    memset(write_data, 0, sizeof(write_data));
    write_length = 0u;

    dummyData =     (char*)"? 564 321 56 465 46\r\n"
                    "doSomething  do  Function does something\r\n"
                    "help         ?   help [command] - print help or usage information\r\n"
                    "\r\nshellmatta->";

    shellmatta_processData(handle, (char*)"? 564 321 56 465 46\r", 20);

    CHECK( write_length == strlen(dummyData));
    CHECK( strcmp(dummyData, write_data) == 0);

    write_callCnt = 0u;
    memset(write_data, 0, sizeof(write_data));
    write_length = 0u;

    shellmatta_removeCmd(handle, &doSomethingCmd);
    shellmatta_processData(handle, (char*)"? 564 321 56 465 46\r", 20);

    dummyData =     (char*)"? 564 321 56 465 46\r\n"
                    "help  ?  help [command] - print help or usage information\r\n"
                    "\r\nshellmatta->";

    CHECK( write_length == strlen(dummyData));
    REQUIRE( strcmp(dummyData, write_data) == 0);
}

TEST_CASE( "shellmatta reset no prompt" ) {

    shellmatta_instance_t inst;
    shellmatta_handle_t handle;
    char buffer[1024];
    char historyBuffer[1024];
    char *dummyData =   (char*)"dkfg hdlsfkgh ldksfjhg lkdjfsh glkd?\r\n"
                        "doSomething  do  Function does something\r\n"
                        "help         ?   help [command] - print help or usage information\r\n"
                        "\r\nshellmatta->";

    shellmatta_doInit(  &inst,
                        &handle,
                        buffer,
                        sizeof(buffer),
                        historyBuffer,
                        sizeof(historyBuffer),
                        "shellmatta->",
                        NULL,
                        writeFct);
    shellmatta_addCmd(handle, &doSomethingCmd);

    write_callCnt = 0u;
    memset(write_data, 0, sizeof(write_data));
    write_length = 0u;

    shellmatta_processData(handle, (char*)"dkfg hdlsfkgh ldksfjhg lkdjfsh glkd", 35);
    shellmatta_resetShell(handle, false);
    shellmatta_processData(handle, (char*)"?\r", 2);

    CHECK( write_length == strlen(dummyData));
    REQUIRE( strcmp(dummyData, write_data) == 0);
}

TEST_CASE( "shellmatta reset with prompt" ) {

    shellmatta_instance_t inst;
    shellmatta_handle_t handle;
    char buffer[1024];
    char historyBuffer[1024];
    char *dummyData =   (char*)"dkfg hdlsfkgh ldksfjhg lkdjfsh glkd"
                        "\r\nshellmatta->?\r\n"
                        "doSomething  do  Function does something\r\n"
                        "help         ?   help [command] - print help or usage information\r\n"
                        "\r\nshellmatta->";

    shellmatta_doInit(  &inst,
                        &handle,
                        buffer,
                        sizeof(buffer),
                        historyBuffer,
                        sizeof(historyBuffer),
                        "shellmatta->",
                        NULL,
                        writeFct);
    shellmatta_addCmd(handle, &doSomethingCmd);

    write_callCnt = 0u;
    memset(write_data, 0, sizeof(write_data));
    write_length = 0u;

    shellmatta_processData(handle, (char*)"dkfg hdlsfkgh ldksfjhg lkdjfsh glkd", 35);
    shellmatta_resetShell(handle, true);
    shellmatta_processData(handle, (char*)"?\r", 2);

    CHECK( write_length == strlen(dummyData));
    REQUIRE( strcmp(dummyData, write_data) == 0);
}

TEST_CASE( "shellmatta reset no prompt history buffer" ) {

    shellmatta_instance_t inst;
    shellmatta_handle_t handle;
    char buffer[1024];
    char historyBuffer[1024];
    char *dummyData =   (char*)"?\r\n"
                        "doSomething  do  Function does something\r\n"
                        "help         ?   help [command] - print help or usage information\r\n"
                        "\r\nshellmatta->";

    shellmatta_doInit(  &inst,
                        &handle,
                        buffer,
                        sizeof(buffer),
                        historyBuffer,
                        sizeof(historyBuffer),
                        "shellmatta->",
                        NULL,
                        writeFct);
    shellmatta_addCmd(handle, &doSomethingCmd);

    shellmatta_processData(handle, (char*)"dkfg hdlsfkgh ldksfjhg lkdjfsh gl\r\n", 35u);
    shellmatta_processData(handle, (char*)"dkfg hdlsfkgh ldksfjhg lkdjfsh gl\r\n", 35u);
    shellmatta_processData(handle, (char*)"dkfg hdlsfkgh ldksfjhg lkdjfsh gl\r\n", 35u);

    write_callCnt = 0u;
    memset(write_data, 0, sizeof(write_data));
    write_length = 0u;

    shellmatta_resetShell(handle, false);

    /* process arrow key up - if reset worked this should deliver nothing */
    shellmatta_processData(handle, (char*)"\033[A", 3u);
    shellmatta_processData(handle, (char*)"?\r", 2);

    CHECK( write_length == strlen(dummyData));
    REQUIRE( strcmp(dummyData, write_data) == 0);
}

TEST_CASE( "shellmatta reset no prompt heredoc" ) {

    shellmatta_instance_t inst;
    shellmatta_handle_t handle;
    char buffer[1024];
    char historyBuffer[1024];
    char *dummyData =   (char*)"?\r\n"
                        "doSomething  do  Function does something\r\n"
                        "help         ?   help [command] - print help or usage information\r\n"
                        "\r\nshellmatta->";

    shellmatta_doInit(  &inst,
                        &handle,
                        buffer,
                        sizeof(buffer),
                        historyBuffer,
                        sizeof(historyBuffer),
                        "shellmatta->",
                        NULL,
                        writeFct);
    shellmatta_addCmd(handle, &doSomethingCmd);

    shellmatta_processData(handle, (char*)"dummy cmd << EOF\r\n", 18u);
    shellmatta_processData(handle, (char*)"dkfg hdlsfkgh ldksfjhg lkdjfsh gl\r\n", 35u);
    shellmatta_processData(handle, (char*)"dkfg hdlsfkgh ldksfjhg lkdjfsh gl\r\n", 35u);

    write_callCnt = 0u;
    memset(write_data, 0, sizeof(write_data));
    write_length = 0u;

    /* end the heredoc session by resetting the shell */
    shellmatta_resetShell(handle, false);

    /* now the new command should be processed */
    shellmatta_processData(handle, (char*)"?\r", 2);

    CHECK( write_length == strlen(dummyData));
    REQUIRE( strcmp(dummyData, write_data) == 0);
}

TEST_CASE( "shellmatta configure disable echo" ) {

    shellmatta_instance_t inst;
    shellmatta_handle_t handle;
    shellmatta_retCode_t ret;
    char buffer[1024];
    char historyBuffer[1024];
    char *dummyData =   (char*)"help this is some dummy Text\r\n"
                        "doSomething  do  Function does something\r\n"
                        "help         ?   help [command] - print help or usage information\r\n"
                        "\r\nshellmatta->";

    char *dummyData2 =  (char*)"doSomething  do  Function does something\r\n"
                        "help         ?   help [command] - print help or usage information\r\n"
                        "\r\nshellmatta->";

    shellmatta_doInit(  &inst,
                        &handle,
                        buffer,
                        sizeof(buffer),
                        historyBuffer,
                        sizeof(historyBuffer),
                        "shellmatta->",
                        NULL,
                        writeFct);
    shellmatta_addCmd(handle, &doSomethingCmd);

    write_callCnt = 0u;
    memset(write_data, 0, sizeof(write_data));
    write_length = 0u;

    /* check with echo enabled */
    shellmatta_processData(handle, (char*)"help this is some dummy Text\r\n", 30u);

    CHECK( write_length == strlen(dummyData));
    CHECK( strcmp(dummyData, write_data) == 0);

    write_callCnt = 0u;
    memset(write_data, 0, sizeof(write_data));
    write_length = 0u;

    /* turn off echo now */
    ret = shellmatta_configure(handle, SHELLMATTA_MODE_INSERT, false, '\r');
    CHECK( ret == SHELLMATTA_OK );

    /* check with echo disabled */
    shellmatta_processData(handle, (char*)"help this is some dummy Text\r\n", 30u);

    CHECK( write_length == strlen(dummyData2));
    REQUIRE( strcmp(dummyData2, write_data) == 0);
}

TEST_CASE( "shellmatta configure mode" ) {

    shellmatta_instance_t inst;
    shellmatta_handle_t handle;
    shellmatta_retCode_t ret;
    char buffer[1024];
    char historyBuffer[1024];
    char *dummyData =   (char*)"\r\nCommand: meow this is some dum123456my Text not found\r\nshellmatta->";

    char *dummyData2 =   (char*)"\r\nCommand: meow this is some dum123456t not found\r\nshellmatta->";

    shellmatta_doInit(  &inst,
                        &handle,
                        buffer,
                        sizeof(buffer),
                        historyBuffer,
                        sizeof(historyBuffer),
                        "shellmatta->",
                        NULL,
                        writeFct);
    shellmatta_addCmd(handle, &doSomethingCmd);
    ret = shellmatta_configure(handle, SHELLMATTA_MODE_INSERT, false, '\r');

    write_callCnt = 0u;
    memset(write_data, 0, sizeof(write_data));
    write_length = 0u;

    /* check with insert mode */
    shellmatta_processData(handle, (char*)"meow this is some dummy Text\x1b[D\x1b[D\x1b[D\x1b[D\x1b[D\x1b[D\x1b[D123456\r\n", 57u);

    CHECK( write_length == strlen(dummyData));
    CHECK( strcmp(dummyData, write_data) == 0);

    write_callCnt = 0u;
    memset(write_data, 0, sizeof(write_data));
    write_length = 0u;

    /* check with overwrite mode */
    ret = shellmatta_configure(handle, SHELLMATTA_MODE_OVERWRITE, false, '\r');
    CHECK( ret == SHELLMATTA_OK );

    /* check with echo disabled */
    shellmatta_processData(handle, (char*)"meow this is some dummy Text\x1b[D\x1b[D\x1b[D\x1b[D\x1b[D\x1b[D\x1b[D123456\r\n", 57u);

    CHECK( write_length == strlen(dummyData2));
    REQUIRE( strcmp(dummyData2, write_data) == 0);
}

TEST_CASE( "shellmatta configure delimiter" ) {

    shellmatta_instance_t inst;
    shellmatta_handle_t handle;
    shellmatta_retCode_t ret;
    char buffer[1024];
    char historyBuffer[1024];
    char *dummyData =   (char*)"doSomething argument - length: 20\r\nshellmatta->";

    shellmatta_doInit(  &inst,
                        &handle,
                        buffer,
                        sizeof(buffer),
                        historyBuffer,
                        sizeof(historyBuffer),
                        "shellmatta->",
                        NULL,
                        writeFct);
    shellmatta_addCmd(handle, &doSomethingCmd);
    ret = shellmatta_configure(handle, SHELLMATTA_MODE_INSERT, false, '\r');

    write_callCnt = 0u;
    memset(write_data, 0, sizeof(write_data));
    write_length = 0u;

    /* check with insert mode */
    shellmatta_processData(handle, (char*)"doSomething argument\n", 21u);

    CHECK( write_length == 0u);

    shellmatta_resetShell(handle, false);
    write_callCnt = 0u;
    memset(write_data, 0, sizeof(write_data));
    write_length = 0u;

    /* check with changed delimiter mode */
    ret = shellmatta_configure(handle, SHELLMATTA_MODE_INSERT, false, '\n');
    CHECK( ret == SHELLMATTA_OK );

    /* check with echo disabled */
    shellmatta_processData(handle, (char*)"doSomething argument\n", 21u);

    CHECK( write_length == strlen(dummyData));
    REQUIRE( strcmp(dummyData, write_data) == 0);
}