#pragma once
///@file

#include "lix/libstore/gc-store.hh"
#include "lix/libutil/args.hh"
#include "lix/libutil/args/root.hh"
#include "lix/libmain/common-args.hh"
#include "lix/libstore/path.hh"
#include "lix/libstore/derived-path.hh"
#include "lix/libutil/processes.hh"
#include "lix/libutil/strings.hh"

#include <kj/function.h>

namespace nix {

int handleExceptions(const std::string & programName, std::function<int()> fun);

/**
 * Don't forget to call initPlugins() after settings are initialized!
 */
void initNix();

void printVersion(const std::string & programName);

/**
 * Ugh.  No better place to put this.
 */
void printGCWarning();

class Store;

kj::Promise<Result<void>> printMissing(
    ref<Store> store,
    const std::vector<DerivedPath> & paths,
    Verbosity lvl = lvlInfo);

kj::Promise<Result<void>> printMissing(ref<Store> store, const StorePathSet & willBuild,
    const StorePathSet & willSubstitute, const StorePathSet & unknown,
    uint64_t downloadSize, uint64_t narSize, Verbosity lvl = lvlInfo);

std::string getArg(const std::string & opt,
    Strings::iterator & i, const Strings::iterator & end);

template<class N> N getIntArg(const std::string & opt,
    Strings::iterator & i, const Strings::iterator & end, bool allowUnit)
{
    ++i;
    if (i == end) throw UsageError("'%1%' requires an argument", opt);
    return string2IntWithUnitPrefix<N>(*i);
}


struct LegacyArgs : public MixCommonArgs, public RootArgs
{
    AsyncIoRoot & aio_;
    AsyncIoRoot & aio() override { return aio_; }

    std::function<bool(Strings::iterator & arg, const Strings::iterator & end)> parseArg;

    LegacyArgs(AsyncIoRoot & aio, const std::string & programName,
        std::function<bool(Strings::iterator & arg, const Strings::iterator & end)> parseArg);

    bool processFlag(Strings::iterator & pos, Strings::iterator end) override;

    bool processArgs(const Strings & args, bool finish) override;
};


/**
 * Show the manual page for the specified program.
 */
void showManPage(const std::string & name);

/**
 * Represents a running pager if paging is available, or stdout if not.
 */
class Pager
{
protected:
    Pager() = default;

public:
    /**
     * Writes some data to the pager (or stdout). Only the provided data
     * is written, with no newlines are added or any formatting applied.
     */
    virtual Pager & operator<<(std::string_view data) = 0;
};

/**
 * Starts a pager if standard output is a terminal and $PAGER is set. The
 * pager is provided as an argument to the callback; only data written to
 * that object will be sent to the pager program. If no pager is started,
 * e.g. if $PAGER is cleared, the pager object writes directly to stdout.
 */
void withPager(kj::Function<void(Pager &)> fn);

/* GC helpers. */

std::string showBytes(uint64_t bytes);

struct GCResults;

struct PrintFreed
{
    GCOptions::GCAction action;

    const GCResults & results;
    PrintFreed(GCOptions::GCAction action, const GCResults & results)
        : action(action)
        , results(results)
    {
    }
    ~PrintFreed();
};


/**
 * Install a SIGSEGV handler to detect stack overflows. See also registerCrashHandler().
 */
void detectStackOverflow();

/**
 * Pluggable behavior to run in case of a stack overflow.
 *
 * Default value: defaultStackOverflowHandler.
 *
 * This is called by the handler installed by detectStackOverflow().
 *
 * This gives Lix library consumers a limit opportunity to report the error
 * condition. The handler should exit the process.
 * See defaultStackOverflowHandler() for a reference implementation.
 *
 * NOTE: Use with diligence, because this runs in the signal handler, with very
 * limited stack space and a potentially a corrupted heap, all while the failed
 * thread is blocked indefinitely. All functions called must be reentrant.
 */
extern std::function<void(siginfo_t * info, void * ctx)> stackOverflowHandler;

/**
 * The default, robust implementation of stackOverflowHandler.
 *
 * Prints an error message directly to stderr using a syscall instead of the
 * logger. Exits the process immediately after.
 */
void defaultStackOverflowHandler(siginfo_t * info, void * ctx);

}
