// Copyright (c) 2018, The TurtleCoin Developers
// 
// Please see the included LICENSE file for more information.

///////////////////////////////
#include <zedwallet/GetInput.h>
///////////////////////////////

#include "linenoise.hpp"

#include <zedwallet/Sync.h>
#include <zedwallet/Tools.h>

/* Note: this is not portable, it only works with terminals that support ANSI
   codes (e.g., not Windows) - however! due to the way linenoise-cpp works,
   it will actually convert these codes for us to the windows equivalent. <3 */
std::string yellowANSIMsg(std::string msg)
{
    const std::string CYELLOW = "\033[1;33m";
    const std::string RESET = "\033[0m";
    return CYELLOW + msg + RESET;
}

std::string getPrompt(std::shared_ptr<WalletInfo> walletInfo)
{
    const int promptLength = 20;
    const std::string extension = ".wallet";

    std::string walletName = walletInfo->walletFileName;

    /* Filename ends in .wallet, remove extension */
    if (std::equal(extension.rbegin(), extension.rend(), 
                   walletInfo->walletFileName.rbegin()))
    {
        const size_t extPos = walletInfo->walletFileName.find_last_of('.');

        walletName = walletInfo->walletFileName.substr(0, extPos);
    }

    const std::string shortName = walletName.substr(0, promptLength);

    return "[" + WalletConfig::ticker + " " + shortName + "]: ";
}

template<typename T>
std::string getInputAndWorkInBackground(const std::vector<T> &availableCommands,
                                        std::string prompt,
                                        bool backgroundRefresh,
                                        std::shared_ptr<WalletInfo> walletInfo)
{
    /* If we are in the main program, we need to check for transactions in
       the background. Unfortunately, we have to do this on the main thread,
       so the best way to do it, is to check whilst waiting for an input on
       another thread. */
    if (backgroundRefresh)
    {
        auto lastUpdated = std::chrono::system_clock::now();

        std::future<std::string> inputGetter = std::async(std::launch::async, 
        [&availableCommands, &prompt]
        {
            return getInput(availableCommands, prompt);
        });


        while (true)
        {
            /* Check if the user has inputted something yet
               (Wait for zero seconds to instantly return) */
            std::future_status status = inputGetter
                                       .wait_for(std::chrono::seconds(0));

            /* User has inputted, get what they inputted and return it */
            if (status == std::future_status::ready)
            {
                return inputGetter.get();
            }

            const auto currentTime = std::chrono::system_clock::now();

            /* Otherwise check if we need to update the wallet cache */
            if ((currentTime - lastUpdated) > std::chrono::seconds(5))
            {
                lastUpdated = currentTime;
                checkForNewTransactions(walletInfo);
            }

            /* Sleep for enough for it to not be noticeable when the user
               enters something, but enough that we're not starving the CPU */
            std::this_thread::sleep_for(std::chrono::milliseconds(50));
        }
    }
    else
    {
        return getInput(availableCommands, prompt);
    }
}

template<typename T>
std::string getInput(const std::vector<T> &availableCommands,
                     std::string prompt)
{
    linenoise::SetCompletionCallback(
    [availableCommands](const char *input, std::vector<std::string> &completions)
    {
        /* Convert to std::string */
        std::string c = input;

        for (const auto &command : availableCommands)
        {
            /* Does command begin with input? */
            if (command.commandName.compare(0, c.length(), c) == 0)
            {
                completions.push_back(command.commandName);
            }
        }
    });

    /* Linenoise is printing this out, so we can't write colours to the stream
       like we normally would - have to include the escape characters directly
       in the string. Obviously this is not platform dependent - but linenoise
       doesn't work on windows, so it's fine. */
    std::string promptMsg = yellowANSIMsg(prompt);

    /* 256 max commands in the wallet command history */
    linenoise::SetHistoryMaxLen(256);

    /* The inputted command */
    std::string command;

    bool quit = linenoise::Readline(promptMsg.c_str(), command);
	
    /* Remove any whitespace */
    trim(command);

    if (command != "")
    {
        linenoise::AddHistory(command.c_str());
    }

    /* Ctrl-C, Ctrl-D, etc */
    if (quit)
    {
        return "exit";
    }

    return command;
}

/* Template instantations that we are going to use - this allows us to have
   the template implementation in the .cpp file. */
template
std::string getInput(const std::vector<Command> &availableCommands,
                     std::string prompt);

template
std::string getInput(const std::vector<AdvancedCommand> &availableCommands,
                     std::string prompt);

template
std::string getInputAndWorkInBackground(const std::vector<Command>
                                        &availableCommands,
                                        std::string prompt,
                                        bool backgroundRefresh,
                                        std::shared_ptr<WalletInfo>
                                        walletInfo);
template
std::string getInputAndWorkInBackground(const std::vector<AdvancedCommand>
                                        &availableCommands,
                                        std::string prompt,
                                        bool backgroundRefresh,
                                        std::shared_ptr<WalletInfo>
                                        walletInfo);
