#ifndef TILE_IO_DESCRIPTOR_H
#define TILE_IO_DESCRIPTOR_H

#pragma once

#include "tile/base/align.h"
#include "tile/base/enum.h"
#include "tile/base/handle.h"
#include "tile/base/internal/test_prod.h"
#include "tile/base/ref_ptr.h"

namespace tile {
class EventLoop;

class alignas(2 * hardware_constructive_interference_size) Descriptor : public RefCounted<Descriptor> {
public:
    enum class Event { Read = 1, Write = 1 << 1, Error = 1 << 2 };

    explicit Descriptor(Handle fd, Event events, const std::string &name = "");

    virtual ~Descriptor();

    int fd() const { return -1; }

    EventLoop *GetEventLoop() const { return read_mostly_.ev.load(std::memory_order_relaxed); }

protected:
    enum class EventAction {
        Ready,
        // `Kill()`
        Leaving,
        // `RestartReadIn`/`RestartWriteIn`
        Suppress,
    };

    enum class CleanupReason { None, HandshakeFailed, Disconnect, UserInitiated, Closing, Error };

    virtual EventAction OnReadable()             = 0;
    virtual EventAction OnWritable()             = 0;
    virtual void OnError(int err)                = 0;
    virtual void OnCleanup(CleanupReason reason) = 0;
    void RestartReadIn(std::chrono::nanoseconds after);
    void RestartWriteIn(std::chrono::nanoseconds after);
    void Kill(CleanupReason reason);
    void WaitForCleanup();

private:
    TILE_FRIEND_TEST(Descriptor, ConcurrentRestartRead);
    friend class EventLoop;
    struct SeldomlyUsed;

    void SetEventLoop(EventLoop *ev) { read_mostly_.ev.store(ev); }

    const std::string &GetName() const;

    bool HasEventMask(Event mask) const { return (read_mostly_.event_mask & static_cast<int>(mask)) != 0; }

    void SetEventMask(int mask) { read_mostly_.event_mask = mask; }

    int GetEventMask() const { return read_mostly_.event_mask; }

    void SetEnabled(bool enabled) { read_mostly_.enabled = enabled; }

    bool Enabled() const { return read_mostly_.enabled; }

    void FireEvents(int mask, std::uint64_t polled_at);
    void FireReadEvent(std::uint64_t fired_at);
    void FireWriteEvent(std::uint64_t fired_at);
    void FireErrorEvent(std::uint64_t fired_at);

    void SuppressReadAndClearReadEventCount();
    void SuppressWriteAndClearWriteEventCount();

    void RestartReadNow();
    void RestartWriteNow();

    void QueueCleanupCallbackCheck();

private:
    std::atomic<std::size_t> read_events_{}, write_events_{};

    std::atomic<bool> cleanup_pending_{false};

    std::atomic<std::size_t> restart_read_count_, restart_write_count_;

    struct alignas(hardware_destructive_interference_size) {
        Handle fd;
        std::atomic<EventLoop *> ev{nullptr};
        int event_mask;
        bool enabled{false};

        std::unique_ptr<SeldomlyUsed> seldomly_used;

    } read_mostly_;
};

TILE_DEFINE_ENUM_BITMASK_OPS(Descriptor::Event);

}// namespace tile
#endif// TILE_IO_DESCRIPTOR_H
