// file      : mod/module.hxx -*- C++ -*-
// copyright : Copyright (c) 2014-2018 Code Synthesis Ltd
// license   : MIT; see accompanying LICENSE file

#ifndef MOD_MODULE_HXX
#define MOD_MODULE_HXX

#include <web/module.hxx>

#include <libbrep/types.hxx>
#include <libbrep/utility.hxx>

#include <mod/options.hxx>
#include <mod/diagnostics.hxx>

namespace brep
{
  // Bring in commonly used names from the web namespace.
  //
  // @@ Maybe doing using namespace is the right way to handle this.
  //    There will, however, most likely be a conflict between
  //    web::handler and our handler. Or maybe not, need to try.
  //
  using web::status_code;
  using web::invalid_request;
  using web::sequence_error;
  using web::option_descriptions;
  using web::name_value;
  using web::name_values;
  using web::request;
  using web::response;
  using web::log;

  // This exception indicated a server error (5XX). In particular,
  // it is thrown by the fail diagnostics stream and is caught by the
  // handler implementation where it is both logged as an error and
  // returned to the user with the 5XX status code.
  //
  struct server_error
  {
    diag_data data;

    server_error (diag_data&& d): data (move (d)) {}
  };

  // Every handler member function that needs to produce any diagnostics
  // shall begin with:
  //
  // HANDLER_DIAG;
  //
  // This will instantiate the fail, error, warn, info, and trace
  // diagnostics streams with the function's name.
  //
#define HANDLER_DIAG                                                    \
  const fail_mark<server_error> fail (__PRETTY_FUNCTION__);             \
  const basic_mark error (severity::error,                              \
                          this->log_writer_,                            \
                          __PRETTY_FUNCTION__);                         \
  const basic_mark warn (severity::warning,                             \
                         this->log_writer_,                             \
                         __PRETTY_FUNCTION__);                          \
  const basic_mark info (severity::info,                                \
                         this->log_writer_,                             \
                         __PRETTY_FUNCTION__);                          \
  const basic_mark trace (severity::trace,                              \
                          this->log_writer_,                            \
                          __PRETTY_FUNCTION__)

  // Adaptation of the web::handler to our needs.
  //
  class handler: public web::handler
  {
    // Diagnostics.
    //
  protected:
    // Trace verbosity level.
    //
    // 0 - tracing disabled.
    // 1 - brief information regarding irregular situations, which not being
    //     an error can be of some interest.
    // 2 - @@ TODO: document
    //
    // While uint8 is more than enough, use uint16 for the ease of printing.
    //
    uint16_t verb_ = 0;

    template <class F> void l1 (const F& f) const {if (verb_ >= 1) f ();}
    template <class F> void l2 (const F& f) const {if (verb_ >= 2) f ();}

    // Set to true when the handler is successfully initialized.
    //
    bool initialized_ {false};

    // Implementation details.
    //
  protected:
    handler ();
    handler (const handler& );

    static name_values
    filter (const name_values&, const option_descriptions&);

    static option_descriptions
    convert (const cli::options&);

    static void
    append (option_descriptions& dst, const cli::options& src);

    static void
    append (option_descriptions& dst, const option_descriptions& src);

    // Can be used by handler implementation to parse HTTP request parameters.
    //
    class name_value_scanner: public cli::scanner
    {
    public:
      name_value_scanner (const name_values&) noexcept;

      virtual bool
      more ();

      virtual const char*
      peek ();

      virtual const char*
      next ();

      virtual void
      skip ();

    private:
      const name_values& name_values_;
      name_values::const_iterator i_;
      bool name_;
    };

  public:
    virtual const cli::options&
    cli_options () const = 0;

    virtual void
    init (cli::scanner&) = 0;

    // Can be overriden by custom request dispatcher to initialize
    // sub-handlers.
    //
    virtual void
    init (const name_values&);

    virtual void
    init (const name_values&, log&);

    virtual bool
    handle (request&, response&) = 0;

    virtual bool
    handle (request&, response&, log&);

    // web::handler interface.
    //
  public:
    // Custom request dispatcher can aggregate its own option descriptions
    // with sub-handlers option descriptions. In this case it should still call
    // the base implementation in order to include the brep::handler's options.
    //
    virtual option_descriptions
    options ();

  private:
    virtual void
    version (log&);

    // Can be overriden by the handler implementation to log version, etc.
    //
    virtual void
    version () {}

    name_values
    expand_options (const name_values&);

    // Diagnostics implementation details.
    //
  protected:
    log* log_ {nullptr}; // Diagnostics backend provided by the web server.

  private:
    // Extract function name from a __PRETTY_FUNCTION__.
    // Throw invalid_argument if fail to parse.
    //
    static string
    func_name (const char* pretty_name);

    void
    log_write (const diag_data&) const;

  protected:
    const diag_epilogue log_writer_;
  };
}

#endif // MOD_MODULE_HXX