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

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

#include <config/CliHeader.h>
#include <Common/SignalHandler.h>
#include <CryptoNoteCore/Currency.h>
#include <Logging/FileLogger.h>
#include <Logging/LoggerManager.h>

#ifdef _WIN32
#include <windows.h>
#endif

#include <Utilities/ColouredMsg.h>
#include <zedwallet/Menu.h>
#include <zedwallet/ParseArguments.h>
#include <zedwallet/Tools.h>
#include <config/WalletConfig.h>

int main(int argc, char **argv)
{
    /* On ctrl+c the program seems to throw "zedwallet.exe has stopped
       working" when calling exit(0)... I'm not sure why, this is a bit of
       a hack, it disables that - possibly some deconstructers calling
       terminate() */
    #ifdef _WIN32
    SetErrorMode(SEM_FAILCRITICALERRORS | SEM_NOGPFAULTERRORBOX);
    #endif

    Config config = parseArguments(argc, argv);

    std::cout << InformationMsg(CryptoNote::getProjectCLIHeader()) << std::endl;

    const auto logManager = std::make_shared<Logging::LoggerManager>();

    if (config.debug)
    {
        logManager->setMaxLevel(Logging::DEBUGGING);

        Logging::FileLogger fileLogger;

        fileLogger.init(WalletConfig::walletName + ".log");
        logManager->addLogger(fileLogger);
    }

    /* Currency contains our coin parameters, such as decimal places, supply */
    const CryptoNote::Currency currency 
        = CryptoNote::CurrencyBuilder(logManager).currency();

    System::Dispatcher localDispatcher;
    System::Dispatcher *dispatcher = &localDispatcher;

    /* Our connection to turtlecoind */
    std::unique_ptr<CryptoNote::INode> node(
        new CryptoNote::NodeRpcProxy(config.host, config.port, logManager)
    );

    std::promise<std::error_code> errorPromise;

    /* Once the function is complete, set the error value from the promise */
    auto callback = [&errorPromise](std::error_code e)
    {
        errorPromise.set_value(e);
    };

    /* Get the future of the result */
    auto initNode = errorPromise.get_future();

    node->init(callback);

    /* Connection took to long to remote node, let program continue regardless
       as they could perform functions like export_keys without being
       connected */
    if (initNode.wait_for(std::chrono::seconds(20)) != std::future_status::ready)
    {
        if (config.host != "127.0.0.1")
        {
            std::cout << WarningMsg("Unable to connect to remote node, "
                                    "connection timed out.")
                      << std::endl
                      << WarningMsg("Confirm the remote node is functioning, "
                                    "or try a different remote node.")
                      << std::endl << std::endl;
        }
        else
        {
            std::cout << WarningMsg("Unable to connect to node, "
                                    "connection timed out.")
                      << std::endl << std::endl;
        }
    }

    /*
      This will check to see if the node responded to /feeinfo and actually
      returned something that it expects us to use for convenience charges
      for using that node to send transactions.
    */
    if (node->feeAmount() != 0 && !node->feeAddress().empty()) {
      std::stringstream feemsg;

      feemsg << std::endl << "You have connected to a node that charges " <<
             "a fee to send transactions." << std::endl << std::endl
             << "The fee for sending transactions is: " << 
             formatAmount(node->feeAmount()) << 
             " per transaction." << std::endl << std::endl <<
             "If you don't want to pay the node fee, please " <<
             "relaunch " << WalletConfig::walletName <<
             " and specify a different node or run your own." <<
             std::endl;

      std::cout << WarningMsg(feemsg.str()) << std::endl;
    }

    /* Create the wallet instance */
    CryptoNote::WalletGreen wallet(*dispatcher, currency, *node, logManager);

    /* Run the interactive wallet interface */
    run(wallet, *node, config);
}

void run(CryptoNote::WalletGreen &wallet, CryptoNote::INode &node,
         Config &config)
{
    auto [quit, walletInfo] = selectionScreen(config, wallet, node);

    bool alreadyShuttingDown = false;

    if (!quit)
    {
        /* Call shutdown on ctrl+c */
        /* walletInfo = walletInfo - workaround for
           https://stackoverflow.com/a/46115028/8737306 - standard &
           capture works in newer compilers. */
        Tools::SignalHandler::install([&walletInfo = walletInfo, &node,
                                       &alreadyShuttingDown]
        {
            /* If we're already shutting down let control flow continue
               as normal */
            if (shutdown(walletInfo, node, alreadyShuttingDown))
            {
                exit(0);
            }
        });

        mainLoop(walletInfo, node);
    }

    shutdown(walletInfo, node, alreadyShuttingDown);
}
