#pragma once

#include <memory>

#include <boost/asio/io_service.hpp>
#include <boost/asio/ip/tcp.hpp>

#include "server_types.hpp"
#include "session.hpp"

namespace hyrise {

/* In the following a short description of the classes used for the server implementation.

*  Server - Opens and binds a server socket. Starts a new session per client.
*  Session - Creates a data socket for client server communication. It is responsible for the message flow and holds
*            session-specific data.
*  PostgresProtocolHandler - This class operates on the message level. It serializes and de-serializes information from
*                            messages.
*  PostgresMessageTypes - Set of different message types supported by Hyrise.
*  ReadBuffer - Dedicated ring buffer for reading information from the network socket. Also does network to host byte
*               conversion for integer types.
*  WriteBuffer - Dedicated ring buffer for writing information to the network socket. Does host to network byte
*                conversion for integer types.
*  RingBufferIterator - Implements ring buffer logic used by the two buffers.
*  QueryHandler - Interface between the server and the database logic. Operations, such as creating an SQLPipeline or
*                 PQP are handled in this class.
*  ResultSerializer - Converts query result information (such as ColumnTypes, but also the data itself) to appropriate
*                     PostgreSQL protocol types and writes them to the network socket.
*/

class Server {
 public:
  Server(const boost::asio::ip::address& address, const uint16_t port, const SendExecutionInfo send_execution_info);

  // Start server to accept new sessions.
  void run();

  // Return the port the server is running on.
  uint16_t server_port() const;

  // Get the current address the server is running. This is important especially for multi-NIC devices.
  boost::asio::ip::address server_address() const;

  // Shutdown Hyrise server.
  void shutdown();

  // Indicates if setup is completed.
  bool is_initialized() const;

 private:
  void _accept_new_session();

  void _start_session(const std::shared_ptr<Session>& new_session, const boost::system::error_code& error);

  std::atomic_uint64_t _num_running_sessions{0};
  boost::asio::io_service _io_service;
  boost::asio::ip::tcp::acceptor _acceptor;
  const SendExecutionInfo _send_execution_info;
  std::atomic_bool _is_initialized{false};
};
}  // namespace hyrise
