#pragma once

#include <map>
#include <memory>
#include <optional>
#include <string>

/**
 * Abstract interface for defining console modules, which are handlers for
 * a group of commands. For instance, the group of commands starting with "os."
 * will all be handled by a single ConsoleModule.
 */
class ConsoleModule {
public:
    virtual ~ConsoleModule() = default;

    /**
     * Perform the action specified by the given command
     * @param command Command to run (excludes the module name prefix)
     * @return User-facing reply string. boost::none is considered failure to handle the command.
     * @todo Change the parameter to std::string_view type
     */
    virtual std::optional<std::string> HandleCommand(const std::string& command) = 0;
};

/**
 * Interface to the console.
 */
class Console {
    std::map<std::string /*module_name*/, std::unique_ptr<ConsoleModule>> modules;

public:
    virtual ~Console() = default;

    /**
     * Perform the action specified by the given command
     * @param command Command to run
     * @return User-facing reply string. boost::none is considered failure to handle the command.
     */
    std::optional<std::string> HandleCommand(const std::string& command);

    /**
     * Register the given module using the given category name.
     */
    void RegisterModule(const std::string& name, std::unique_ptr<ConsoleModule> module);
};

/**
 * Console implementation that waits for incoming connections on a given port
 * and consecutively reads console commands from any clients.
 */
class NetworkConsole : public Console {
    std::unique_ptr<class NetworkConsoleServer> server;

public:
    NetworkConsole(unsigned port);
    ~NetworkConsole();

    // Blocks until Stop() is called
    void Run();

    // Must be called before object destruction
    void Stop();
};