// Copyright (c) 2009-2010 Satoshi Nakamoto
// Copyright (c) 2017-2019 The WaykiChain Developers
// Distributed under the MIT/X11 software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.


#include <boost/algorithm/string/predicate.hpp>
#include <boost/filesystem.hpp>
#include "init.h"
#include "main.h"
#include "rpc/core/rpcclient.h"
#include "rpc/core/rpcserver.h"
#include "commons/util/util.h"

/* Introduction text for doxygen: */

/*! \mainpage Developer documentation
 *
 * \section intro_sec Introduction
 *
 * This is the developer documentation of the reference client for a new crypto currency called WICC
 * (http://www.waykichain.com), which enables instant payments to anyone, anywhere in the world. WICC uses peer-to-peer
 * technology to operate with no central authority: managing transactions and issuing money are carried out collectively
 * by the network.
 *
 * The software is a community-driven open source project, released under the MIT license.
 *
 * \section Navigation
 * Use the buttons <code>Namespaces</code>, <code>Classes</code> or <code>Files</code> at the top of the page to start
 * navigating the code.
 */
static bool fDaemon = false;

void DetectShutdownThread(boost::thread_group* threadGroup) {
    RenameThread("coin-waitexit");
    bool fShutdown = ShutdownRequested();
    // Tell the main threads to shutdown.
    while (!fShutdown) {
        MilliSleep(200);
        fShutdown = ShutdownRequested();
    }
    if (threadGroup) {
        threadGroup->interrupt_all();
        threadGroup->join_all();
    }

    Interrupt();
}

//////////////////////////////////////////////////////////////////////////////
//
// Start
//
bool AppInit(int argc, char* argv[], boost::thread_group& threadGroup) {
    //	boost::thread* detectShutdownThread = nullptr;

    bool fRet = false;
    try {
        //
        // Parameters
        //
        // If Qt is used, parameters/coin.conf are parsed in qt/Coin.cpp's main()
        CBaseParams::LoadParamsFromConfigFile(argc, argv);

        if (SysCfg().IsArgCount("-?") || SysCfg().IsArgCount("--help")) {
            // First part of help message is specific to coind / RPC client
            std::string strUsage = _("WaykiChain Coin Daemon") + " " + _("version") + " " + FormatFullVersion() +
                                   "\n\n" + _("Usage:") + "\n" + "  coind [options]                     " +
                                   _("Start Coin Core Daemon") + "\n" + _("Usage (deprecated, use Coin-cli):") + "\n" +
                                   "  coin [options] <command> [params]  " + _("Send command to Coin Core") + "\n" +
                                   "  coin [options] help                " + _("List commands") + "\n" +
                                   "  coin [options] help <command>      " + _("Get help for a command") + "\n";

            strUsage += "\n" + HelpMessage();
            strUsage += "\n" + HelpMessageCli(false);

            fprintf(stdout, "%s", strUsage.c_str());
            return false;
        }

        // Command-line RPC
        bool fCommandLine = false;
        for (int i = 1; i < argc; i++)
            if (!IsSwitchChar(argv[i][0]) && !boost::algorithm::istarts_with(argv[i], "Coin:"))
                fCommandLine = true;

        if (fCommandLine) {
            int ret = CommandLineRPC(argc, argv);
            exit(ret);
        }
#ifndef WIN32
        fDaemon = SysCfg().GetBoolArg("-daemon", false);
        if (fDaemon) {
            fprintf(stdout, "Coin server starting\n");

            // Daemonize
            pid_t pid = fork();
            if (pid < 0) {
                fprintf(stderr, "Error: fork() returned %d errno %d\n", pid, errno);
                return false;
            }
            if (pid > 0)  // Parent process, pid is child process id
            {
                CreatePidFile(GetPidFile(), pid);
                return true;
            }
            // Child process falls through to rest of initialization

            pid_t sid = setsid();
            if (sid < 0) fprintf(stderr, "Error: setsid() returned %d errno %d\n", sid, errno);
        }
#endif
        SysCfg().SoftSetBoolArg("-rpcserver", true);

        fRet = InitLogging() && AppInit(threadGroup);
    } catch (std::exception& e) {
        PrintExceptionContinue(&e, "AppInit()");
    } catch (...) {
        PrintExceptionContinue(nullptr, "AppInit()");
    }

    return fRet;
}
std::tuple<bool, boost::thread*> RunCoin(int argc, char* argv[]) {
    boost::thread* detectShutdownThread = nullptr;
    static boost::thread_group threadGroup;
    SetupEnvironment();

    bool fRet = AppInit(argc, argv, threadGroup);

    detectShutdownThread = new boost::thread(boost::bind(&DetectShutdownThread, &threadGroup));

    if (!fRet) {
        if (detectShutdownThread)
            detectShutdownThread->interrupt();
        Interrupt();
        threadGroup.interrupt_all();

        // threadGroup.join_all(); was left out intentionally here, because we didn't re-test all of
        // the startup-failure cases to make sure they don't result in a hang due to some
        // thread-blocking-waiting-for-another-thread-during-startup case
    }
    return std::make_tuple(fRet, detectShutdownThread);
}

int main(int argc, char* argv[]) {
    std::tuple<bool, boost::thread*> ret = RunCoin(argc, argv);

    boost::thread* detectShutdownThread = std::get<1>(ret);

    bool fRet = std::get<0>(ret);

    if (detectShutdownThread) {
        detectShutdownThread->join();
        delete detectShutdownThread;
        detectShutdownThread = nullptr;
    }

    Shutdown();

    FinalLogging();

#ifndef WIN32
    fDaemon = SysCfg().GetBoolArg("-daemon", false);
#endif

    if (fRet && fDaemon) return 0;

    return (fRet ? 0 : 1);
}
