#ifndef LIBTORRENT_TORRENT_SYSTEM_EVENT_H
#define LIBTORRENT_TORRENT_SYSTEM_EVENT_H

#include <memory>
#include <torrent/net/types.h>
#include <torrent/system/common.h>

namespace torrent::runtime {
class SocketManager;
}

namespace torrent::system {

class PollEvent;
class PollInternal;

class LIBTORRENT_EXPORT Event {
public:
  virtual             ~Event();

  bool                is_open() const;
  bool                is_polling() const;

  int                 file_descriptor() const;
  int                 socket_type_or_zero() const;

  auto                peer_address() const;
  auto                socket_address() const;

  std::string         print_name_fd_str() const;

  virtual const char* type_name() const;

  // TODO: Make these protected.
  virtual void        event_read() = 0;
  virtual void        event_write() = 0;
  virtual void        event_error() = 0;

  // TODO: Add bool event_fd_reused().

protected:
  friend class Poll;
  friend class PollInternal;
  friend class runtime::SocketManager;

  void                reset_file_descriptor();

  void                set_file_descriptor(int fd);
  void                set_socket_address(c_sa_unique_ptr address);

  bool                update_socket_address();
  bool                update_peer_address();

  // Returns true if the update failed and the address was null.
  bool                update_and_verify_socket_address();
  bool                update_and_verify_peer_address();

  std::shared_ptr<PollEvent> m_poll_event;

private:
  int                 m_fileDesc{-1};

  // TODO: Add socket type to validation.
  c_sa_unique_ptr     m_peer_address;
  c_sa_unique_ptr     m_socket_address;
};

inline bool Event::is_open() const             { return file_descriptor() != -1; }
inline bool Event::is_polling() const          { return m_poll_event != nullptr; }

inline int  Event::file_descriptor() const     { return m_fileDesc; }

inline void Event::reset_file_descriptor()     { m_fileDesc = -1; }
inline void Event::set_file_descriptor(int fd) { m_fileDesc = fd; }

inline auto Event::peer_address() const        { return m_peer_address.get(); }

inline auto Event::socket_address() const                      { return m_socket_address.get(); }
inline void Event::set_socket_address(c_sa_unique_ptr address) { m_socket_address = std::move(address); }

} // namespace torrent::system

#endif
