#pragma once

#include "../../helpers/math/Math.hpp"
#include "../../helpers/time/Time.hpp"
#include "../../managers/eventLoop/EventLoopTimer.hpp"
#include "../WaylandProtocol.hpp"
#include "./Buffer.hpp"

namespace Render {
    class ITexture;
}
class CDRMSyncPointState;
class CWLCallbackResource;
class CPresentationFeedback;
struct SReadableWaiter;

enum eLockReason : uint8_t {
    LOCK_REASON_NONE  = 0,
    LOCK_REASON_FENCE = 1 << 0,
    LOCK_REASON_FIFO  = 1 << 1,
    LOCK_REASON_TIMER = 1 << 2
};

inline eLockReason operator|(eLockReason a, eLockReason b) {
    return sc<eLockReason>(sc<uint8_t>(a) | sc<uint8_t>(b));
}
inline eLockReason operator&(eLockReason a, eLockReason b) {
    return sc<eLockReason>(sc<uint8_t>(a) & sc<uint8_t>(b));
}
inline eLockReason& operator|=(eLockReason& a, eLockReason b) {
    a = a | b;
    return a;
}
inline eLockReason& operator&=(eLockReason& a, eLockReason b) {
    a = a & b;
    return a;
}
inline eLockReason operator~(eLockReason a) {
    return sc<eLockReason>(~sc<uint8_t>(a));
}

struct SSurfaceState {
    union {
        uint32_t all = 0;
        struct {
            bool buffer : 1;
            bool damage : 1;
            bool opaque : 1;
            bool input : 1;
            bool transform : 1;
            bool scale : 1;
            bool offset : 1;
            bool viewport : 1;
            bool acquire : 1;
            bool acked : 1;
            bool frame : 1;
            bool fifo : 1;
            bool presentation : 1;
            bool xdgshell : 1;
            bool layershell : 1;
            bool subsurface : 1;
            bool alphaModifier : 1;
            bool hyprlandSurface : 1;
            bool backgroundEffect : 1;
        } bits;
    } updated;

    bool rejected = false;

    // initial values, copied from protocol text
    CHLBufferReference  buffer = {};                                  // The initial surface contents are void
    CRegion             damage, bufferDamage;                         // The initial value for pending damage is empty
    CRegion             opaque;                                       // The initial value for an opaque region is empty
    CRegion             input;                                        // The initial value for an input region is infinite
    bool                inputIsInfinite = true;                       // Tracks the input region's infinite protocol state
    wl_output_transform transform       = WL_OUTPUT_TRANSFORM_NORMAL; // A newly created surface has its buffer transformation set to normal
    int                 scale           = 1;                          // A newly created surface has its buffer scale set to 1

    // these don't have well defined initial values in the protocol, but these work
    Vector2D size, bufferSize;
    Vector2D offset;

    // for xdg_shell resizing
    Vector2D ackedSize;

    // for wl_surface::frame callbacks.
    std::vector<SP<CWLCallbackResource>> callbacks;

    // for wp_presentation feedbacks, tied to this commit.
    std::vector<WP<CPresentationFeedback>> presentationFeedbacks;

    // viewporter protocol surface state
    struct {
        bool     hasDestination = false;
        bool     hasSource      = false;
        Vector2D destination;
        CBox     source;
    } viewport;
    Vector2D sourceSize();

    // drm syncobj protocol surface state
    CDRMSyncPointState  acquire;
    WP<SReadableWaiter> acquireWaiter;
    eLockReason         lockMask = LOCK_REASON_NONE;

    // texture of surface content, used for rendering
    SP<Render::ITexture> texture;
    void                 updateSynchronousTexture(SP<Render::ITexture> lastTexture);

    // fifo
    bool barrierSet            = false;
    bool barrierWait           = false;
    bool waitingOnPresentation = false;

    // commit timing
    std::optional<Time::steady_dur> pendingTimeout;
    std::optional<Time::steady_tp>  commitTimingTarget;
    SP<CEventLoopTimer>             timer;

    // helpers
    CRegion accumulateBufferDamage();       // transforms state.damage and merges it into state.bufferDamage
    bool    consumeBufferDamage() const;    // whether accumulateBufferDamage() takes the damage rather than leaving it
    CRegion effectiveInputRegion() const;   // materializes the input region clipped to the current surface size
    void    updateFrom(SSurfaceState& ref); // updates this state based on a reference state.
    void    reset();                        // resets pending state after commit

    bool    isLocked() const;
    bool    fenceSignaled() const;
    void    mergeFrom(SSurfaceState& ref);
    void    cancelFenceWaiter();
};
