#ifndef HTTP_CONNECTION_HPP
#define HTTP_CONNECTION_HPP

#include <boost/asio.hpp>
#include <boost/asio/ssl/context.hpp>
#include <boost/asio/ssl/stream.hpp>
#include <boost/beast/core/tcp_stream.hpp>
#include <array>
#include <memory>
#include "reply.hpp"
#include "request.hpp"
#include "request_parser.hpp"


namespace http::server {
    using request_handler = std::function<void(const Request &req, reply &rep)>;

    class ConnectionManager;
    /// Represents a single Connection from a client. Base class
    class ConnectionBase {
    public:
        ConnectionBase(const ConnectionBase &) = delete;
        ConnectionBase &operator=(const ConnectionBase &) = delete;
        ConnectionBase() = default;

        /// Start the first asynchronous operation for the Connection.
        virtual void start() = 0;

        /// Stop all asynchronous operations associated with the Connection.
        virtual void stop() = 0;

        virtual ~ConnectionBase() = default;
    };

    class Connection final : public ConnectionBase, public std::enable_shared_from_this<Connection> {
    public:

        /// Construct a Connection with the given socket.
        explicit Connection(boost::asio::ip::tcp::socket socket, ConnectionManager &manager, request_handler handler);

        /// Start the first asynchronous operation for the Connection.
        void start() override;

        /// Stop all asynchronous operations associated with the Connection.
        void stop() override;

        ~Connection() override;
    private:
        /// Perform an asynchronous read operation.
        void doRead();

        /// Perform an asynchronous write operation.
        void doWrite();

        /// Socket for the Connection.
        boost::asio::ip::tcp::socket socket_;

        /// The manager for this Connection.
        ConnectionManager &connection_manager_;

        /// The handler used to process the incoming request.
        request_handler request_handler_;

        /// Buffer for incoming data.
        std::array<char, 2048> buffer_{};

        /// The incoming request.
        Request request_;

        /// The parser for the incoming request.
        RequestParser request_parser_;

        /// The reply to be sent back to the client.
        reply reply_;
    };

    class SslConnection final : public ConnectionBase, public std::enable_shared_from_this<SslConnection> {
    public:

        /// Construct a Connection with the given socket.
        explicit SslConnection(boost::asio::ip::tcp::socket socket, ConnectionManager &manager, request_handler handler, const std::shared_ptr<boost::asio::ssl::context>& ctx);

        /// Start the first asynchronous operation for the Connection.
        void start() override;

        /// Stop all asynchronous operations associated with the Connection.
        void stop() override;

        ~SslConnection() override;
    private:
        /// Perform an asynchronous read operation.
        void doRead();

        /// Perform an asynchronous write operation.
        void doWrite();

        /// Socket for the Connection.
        boost::asio::ssl::stream<boost::beast::tcp_stream> stream_;

        /// The manager for this Connection.
        ConnectionManager &connection_manager_;

        /// The handler used to process the incoming request.
        request_handler request_handler_;

        /// Buffer for incoming data.
        std::array<char, 2048> buffer_{};

        /// The incoming request.
        Request request_;

        /// The parser for the incoming request.
        RequestParser request_parser_;

        /// The reply to be sent back to the client.
        reply reply_;
    };

    typedef std::shared_ptr<ConnectionBase> connection_ptr;
} // namespace http::Server


#endif // HTTP_CONNECTION_HPP
