#pragma once

#include <LibWeb/Compositor/Types.h>
#include <LibWeb/Page/InputEvent.h>
#include <AK/Error.h>
#include <AK/MemoryStream.h>
#include <AK/OwnPtr.h>
#include <AK/Platform.h>
#include <AK/Result.h>
#include <AK/Utf8View.h>
#include <LibIPC/Attachment.h>
#include <LibIPC/Connection.h>
#include <LibIPC/Decoder.h>
#include <LibIPC/Encoder.h>
#include <LibIPC/File.h>
#include <LibIPC/Message.h>
#include <LibIPC/Stub.h>

#if defined(AK_COMPILER_CLANG)
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wdefaulted-function-deleted"
#endif

namespace Messages::CompositorWebContentClient {

enum class MessageID : i32 {
    MouseEvent = 1,
    RequestRenderingUpdate = 2,
    DidCompleteScreenshot = 3,
    DidFailScreenshot = 4,
    DidLoseCompositor = 5,
};

class MouseEvent final : public IPC::Message {
public:
    MouseEvent(u64 page_id, Web::MouseEvent event)
        : m_page_id(move(page_id))
        , m_event(move(event))
    {
    }

    MouseEvent(MouseEvent const&) = default;
    MouseEvent(MouseEvent&&) = default;
    MouseEvent& operator=(MouseEvent const&) = default;

    static constexpr u32 ENDPOINT_MAGIC = 2233644500;

    virtual u32 endpoint_magic() const override { return ENDPOINT_MAGIC; }
    virtual i32 message_id() const override { return (int)MessageID::MouseEvent; }
    static i32 static_message_id() { return (int)MessageID::MouseEvent; }
    virtual StringView message_name() const override { return "CompositorWebContentClient::MouseEvent"sv; }

    static ErrorOr<NonnullOwnPtr<MouseEvent>> decode(Stream& stream, Queue<IPC::Attachment>& attachments)
    {
        IPC::Decoder decoder { stream, attachments };
        auto page_id = TRY((decoder.decode<u64>()));
        auto event = TRY((decoder.decode<Web::MouseEvent>()));
        return make<MouseEvent>(move(page_id), move(event));
    }

    static ErrorOr<IPC::MessageBuffer> static_encode(u64 page_id, Web::MouseEvent const& event)
    {
        IPC::MessageBuffer buffer;
        IPC::Encoder stream(buffer);
        TRY(stream.encode(ENDPOINT_MAGIC));
        TRY(stream.encode((int)MessageID::MouseEvent));
        TRY(stream.encode(page_id));
        TRY(stream.encode(event));
        return buffer;
    }

    virtual ErrorOr<IPC::MessageBuffer> encode() const override
    {
        return static_encode(m_page_id, m_event);
    }

    u64 page_id() const { return m_page_id; }

    Web::MouseEvent const& event() const { return m_event; }
    Web::MouseEvent take_event() { return move(m_event); }

private:
    u64 m_page_id;
    Web::MouseEvent m_event;
};

class RequestRenderingUpdate final : public IPC::Message {
public:
    RequestRenderingUpdate() = default;

    RequestRenderingUpdate(RequestRenderingUpdate const&) = default;
    RequestRenderingUpdate(RequestRenderingUpdate&&) = default;
    RequestRenderingUpdate& operator=(RequestRenderingUpdate const&) = default;

    static constexpr u32 ENDPOINT_MAGIC = 2233644500;

    virtual u32 endpoint_magic() const override { return ENDPOINT_MAGIC; }
    virtual i32 message_id() const override { return (int)MessageID::RequestRenderingUpdate; }
    static i32 static_message_id() { return (int)MessageID::RequestRenderingUpdate; }
    virtual StringView message_name() const override { return "CompositorWebContentClient::RequestRenderingUpdate"sv; }

    static ErrorOr<NonnullOwnPtr<RequestRenderingUpdate>> decode(Stream& stream, Queue<IPC::Attachment>& attachments)
    {
        IPC::Decoder decoder { stream, attachments };
        return make<RequestRenderingUpdate>();
    }

    static ErrorOr<IPC::MessageBuffer> static_encode()
    {
        IPC::MessageBuffer buffer;
        IPC::Encoder stream(buffer);
        TRY(stream.encode(ENDPOINT_MAGIC));
        TRY(stream.encode((int)MessageID::RequestRenderingUpdate));
        return buffer;
    }

    virtual ErrorOr<IPC::MessageBuffer> encode() const override
    {
        return static_encode();
    }

private:
};

class DidCompleteScreenshot final : public IPC::Message {
public:
    DidCompleteScreenshot(Web::Compositor::ScreenshotRequestId request_id)
        : m_request_id(move(request_id))
    {
    }

    DidCompleteScreenshot(DidCompleteScreenshot const&) = default;
    DidCompleteScreenshot(DidCompleteScreenshot&&) = default;
    DidCompleteScreenshot& operator=(DidCompleteScreenshot const&) = default;

    template<typename WrappedReturnType>
    requires(!SameAs<WrappedReturnType, Web::Compositor::ScreenshotRequestId>)
    DidCompleteScreenshot(WrappedReturnType&& value)
        : m_request_id(forward<WrappedReturnType>(value))
    {
    }

    static constexpr u32 ENDPOINT_MAGIC = 2233644500;

    virtual u32 endpoint_magic() const override { return ENDPOINT_MAGIC; }
    virtual i32 message_id() const override { return (int)MessageID::DidCompleteScreenshot; }
    static i32 static_message_id() { return (int)MessageID::DidCompleteScreenshot; }
    virtual StringView message_name() const override { return "CompositorWebContentClient::DidCompleteScreenshot"sv; }

    static ErrorOr<NonnullOwnPtr<DidCompleteScreenshot>> decode(Stream& stream, Queue<IPC::Attachment>& attachments)
    {
        IPC::Decoder decoder { stream, attachments };
        auto request_id = TRY((decoder.decode<Web::Compositor::ScreenshotRequestId>()));
        return make<DidCompleteScreenshot>(move(request_id));
    }

    static ErrorOr<IPC::MessageBuffer> static_encode(Web::Compositor::ScreenshotRequestId const& request_id)
    {
        IPC::MessageBuffer buffer;
        IPC::Encoder stream(buffer);
        TRY(stream.encode(ENDPOINT_MAGIC));
        TRY(stream.encode((int)MessageID::DidCompleteScreenshot));
        TRY(stream.encode(request_id));
        return buffer;
    }

    virtual ErrorOr<IPC::MessageBuffer> encode() const override
    {
        return static_encode(m_request_id);
    }

    Web::Compositor::ScreenshotRequestId const& request_id() const { return m_request_id; }
    Web::Compositor::ScreenshotRequestId take_request_id() { return move(m_request_id); }

private:
    Web::Compositor::ScreenshotRequestId m_request_id;
};

class DidFailScreenshot final : public IPC::Message {
public:
    DidFailScreenshot(Web::Compositor::ScreenshotRequestId request_id)
        : m_request_id(move(request_id))
    {
    }

    DidFailScreenshot(DidFailScreenshot const&) = default;
    DidFailScreenshot(DidFailScreenshot&&) = default;
    DidFailScreenshot& operator=(DidFailScreenshot const&) = default;

    template<typename WrappedReturnType>
    requires(!SameAs<WrappedReturnType, Web::Compositor::ScreenshotRequestId>)
    DidFailScreenshot(WrappedReturnType&& value)
        : m_request_id(forward<WrappedReturnType>(value))
    {
    }

    static constexpr u32 ENDPOINT_MAGIC = 2233644500;

    virtual u32 endpoint_magic() const override { return ENDPOINT_MAGIC; }
    virtual i32 message_id() const override { return (int)MessageID::DidFailScreenshot; }
    static i32 static_message_id() { return (int)MessageID::DidFailScreenshot; }
    virtual StringView message_name() const override { return "CompositorWebContentClient::DidFailScreenshot"sv; }

    static ErrorOr<NonnullOwnPtr<DidFailScreenshot>> decode(Stream& stream, Queue<IPC::Attachment>& attachments)
    {
        IPC::Decoder decoder { stream, attachments };
        auto request_id = TRY((decoder.decode<Web::Compositor::ScreenshotRequestId>()));
        return make<DidFailScreenshot>(move(request_id));
    }

    static ErrorOr<IPC::MessageBuffer> static_encode(Web::Compositor::ScreenshotRequestId const& request_id)
    {
        IPC::MessageBuffer buffer;
        IPC::Encoder stream(buffer);
        TRY(stream.encode(ENDPOINT_MAGIC));
        TRY(stream.encode((int)MessageID::DidFailScreenshot));
        TRY(stream.encode(request_id));
        return buffer;
    }

    virtual ErrorOr<IPC::MessageBuffer> encode() const override
    {
        return static_encode(m_request_id);
    }

    Web::Compositor::ScreenshotRequestId const& request_id() const { return m_request_id; }
    Web::Compositor::ScreenshotRequestId take_request_id() { return move(m_request_id); }

private:
    Web::Compositor::ScreenshotRequestId m_request_id;
};

class DidLoseCompositor final : public IPC::Message {
public:
    DidLoseCompositor() = default;

    DidLoseCompositor(DidLoseCompositor const&) = default;
    DidLoseCompositor(DidLoseCompositor&&) = default;
    DidLoseCompositor& operator=(DidLoseCompositor const&) = default;

    static constexpr u32 ENDPOINT_MAGIC = 2233644500;

    virtual u32 endpoint_magic() const override { return ENDPOINT_MAGIC; }
    virtual i32 message_id() const override { return (int)MessageID::DidLoseCompositor; }
    static i32 static_message_id() { return (int)MessageID::DidLoseCompositor; }
    virtual StringView message_name() const override { return "CompositorWebContentClient::DidLoseCompositor"sv; }

    static ErrorOr<NonnullOwnPtr<DidLoseCompositor>> decode(Stream& stream, Queue<IPC::Attachment>& attachments)
    {
        IPC::Decoder decoder { stream, attachments };
        return make<DidLoseCompositor>();
    }

    static ErrorOr<IPC::MessageBuffer> static_encode()
    {
        IPC::MessageBuffer buffer;
        IPC::Encoder stream(buffer);
        TRY(stream.encode(ENDPOINT_MAGIC));
        TRY(stream.encode((int)MessageID::DidLoseCompositor));
        return buffer;
    }

    virtual ErrorOr<IPC::MessageBuffer> encode() const override
    {
        return static_encode();
    }

private:
};

} // namespace Messages::CompositorWebContentClient

template<typename LocalEndpoint, typename PeerEndpoint>
class CompositorWebContentClientProxy {
public:
    // Used to disambiguate the constructor call.
    struct Tag { };

    CompositorWebContentClientProxy(IPC::Connection<LocalEndpoint, PeerEndpoint>& connection, Tag)
        : m_connection(connection)
    {
    }

    void async_mouse_event(u64 page_id, Web::MouseEvent const& event)
    {
        auto message_buffer = MUST(Messages::CompositorWebContentClient::MouseEvent::static_encode(page_id, move(event)));
        (void)m_connection.post_message(message_buffer);
    }

    void async_request_rendering_update()
    {
        auto message_buffer = MUST(Messages::CompositorWebContentClient::RequestRenderingUpdate::static_encode());
        (void)m_connection.post_message(message_buffer);
    }

    void async_did_complete_screenshot(Web::Compositor::ScreenshotRequestId const& request_id)
    {
        auto message_buffer = MUST(Messages::CompositorWebContentClient::DidCompleteScreenshot::static_encode(move(request_id)));
        (void)m_connection.post_message(message_buffer);
    }

    void async_did_fail_screenshot(Web::Compositor::ScreenshotRequestId const& request_id)
    {
        auto message_buffer = MUST(Messages::CompositorWebContentClient::DidFailScreenshot::static_encode(move(request_id)));
        (void)m_connection.post_message(message_buffer);
    }

    void async_did_lose_compositor()
    {
        auto message_buffer = MUST(Messages::CompositorWebContentClient::DidLoseCompositor::static_encode());
        (void)m_connection.post_message(message_buffer);
    }

private:
    IPC::Connection<LocalEndpoint, PeerEndpoint>& m_connection;
};

template<typename LocalEndpoint, typename PeerEndpoint>
class CompositorWebContentClientProxy;
class CompositorWebContentClientStub;

class CompositorWebContentClientEndpoint {
public:
    template<typename LocalEndpoint>
    using Proxy = CompositorWebContentClientProxy<LocalEndpoint, CompositorWebContentClientEndpoint>;
    using Stub = CompositorWebContentClientStub;

    static u32 static_magic() { return 2233644500; }

    static ErrorOr<NonnullOwnPtr<IPC::Message>> decode_message(ReadonlyBytes buffer, [[maybe_unused]] Queue<IPC::Attachment>& attachments)
    {
        FixedMemoryStream stream { buffer };
        auto message_endpoint_magic = TRY(stream.read_value<u32>());

        if (message_endpoint_magic != static_magic())
            return Error::from_string_literal("Endpoint magic number mismatch, not my message!");

        auto message_id = TRY(stream.read_value<i32>());

        switch (message_id) {
        case (int)Messages::CompositorWebContentClient::MessageID::MouseEvent:
            return Messages::CompositorWebContentClient::MouseEvent::decode(stream, attachments);
        case (int)Messages::CompositorWebContentClient::MessageID::RequestRenderingUpdate:
            return Messages::CompositorWebContentClient::RequestRenderingUpdate::decode(stream, attachments);
        case (int)Messages::CompositorWebContentClient::MessageID::DidCompleteScreenshot:
            return Messages::CompositorWebContentClient::DidCompleteScreenshot::decode(stream, attachments);
        case (int)Messages::CompositorWebContentClient::MessageID::DidFailScreenshot:
            return Messages::CompositorWebContentClient::DidFailScreenshot::decode(stream, attachments);
        case (int)Messages::CompositorWebContentClient::MessageID::DidLoseCompositor:
            return Messages::CompositorWebContentClient::DidLoseCompositor::decode(stream, attachments);
        default:
            return Error::from_string_literal("Failed to decode CompositorWebContentClient message");
        }

        VERIFY_NOT_REACHED();
    }
};

class CompositorWebContentClientStub : public IPC::Stub {
public:
    CompositorWebContentClientStub() { }
    virtual ~CompositorWebContentClientStub() override { }

    virtual u32 magic() const override { return 2233644500; }
    virtual ByteString name() const override { return "CompositorWebContentClient"; }

    virtual ErrorOr<OwnPtr<IPC::MessageBuffer>> handle(NonnullOwnPtr<IPC::Message> message) override
    {
        switch (message->message_id()) {
        case (int)Messages::CompositorWebContentClient::MessageID::MouseEvent:
            return handle_mouse_event(*message);
        case (int)Messages::CompositorWebContentClient::MessageID::RequestRenderingUpdate:
            return handle_request_rendering_update();
        case (int)Messages::CompositorWebContentClient::MessageID::DidCompleteScreenshot:
            return handle_did_complete_screenshot(*message);
        case (int)Messages::CompositorWebContentClient::MessageID::DidFailScreenshot:
            return handle_did_fail_screenshot(*message);
        case (int)Messages::CompositorWebContentClient::MessageID::DidLoseCompositor:
            return handle_did_lose_compositor();
        default:
            return Error::from_string_literal("Unknown message ID for CompositorWebContentClient endpoint");
        }
    }

    NEVER_INLINE ErrorOr<OwnPtr<IPC::MessageBuffer>> handle_mouse_event(IPC::Message& message)
    {
        auto& request = static_cast<Messages::CompositorWebContentClient::MouseEvent&>(message);
        mouse_event(request.page_id(), request.take_event());
        return nullptr;
    }

    NEVER_INLINE ErrorOr<OwnPtr<IPC::MessageBuffer>> handle_request_rendering_update()
    {
        request_rendering_update();
        return nullptr;
    }

    NEVER_INLINE ErrorOr<OwnPtr<IPC::MessageBuffer>> handle_did_complete_screenshot(IPC::Message& message)
    {
        auto& request = static_cast<Messages::CompositorWebContentClient::DidCompleteScreenshot&>(message);
        did_complete_screenshot(request.take_request_id());
        return nullptr;
    }

    NEVER_INLINE ErrorOr<OwnPtr<IPC::MessageBuffer>> handle_did_fail_screenshot(IPC::Message& message)
    {
        auto& request = static_cast<Messages::CompositorWebContentClient::DidFailScreenshot&>(message);
        did_fail_screenshot(request.take_request_id());
        return nullptr;
    }

    NEVER_INLINE ErrorOr<OwnPtr<IPC::MessageBuffer>> handle_did_lose_compositor()
    {
        did_lose_compositor();
        return nullptr;
    }

    virtual void mouse_event(u64 page_id, Web::MouseEvent event) = 0;
    virtual void request_rendering_update() = 0;
    virtual void did_complete_screenshot(Web::Compositor::ScreenshotRequestId request_id) = 0;
    virtual void did_fail_screenshot(Web::Compositor::ScreenshotRequestId request_id) = 0;
    virtual void did_lose_compositor() = 0;
};

#if defined(AK_COMPILER_CLANG)
#pragma clang diagnostic pop
#endif
