/*
 * Copyright (c) 2022, Ben Abraham <ben.d.abraham@gmail.com>
 *
 * SPDX-License-Identifier: BSD-2-Clause
 */

#pragma once

#include <LibWeb/Forward.h>
#include <LibWeb/HTML/AbstractWorker.h>
#include <LibWeb/HTML/Window.h>
#include <LibWeb/HTML/WorkerAgentParent.h>
#include <LibWeb/WebIDL/ExceptionOr.h>

#define ENUMERATE_WORKER_EVENT_HANDLERS(E)  \
    E(onmessage, HTML::EventNames::message) \
    E(onmessageerror, HTML::EventNames::messageerror)

namespace Web::HTML {

// https://html.spec.whatwg.org/multipage/workers.html#dedicated-workers-and-the-worker-interface
class Worker
    : public DOM::EventTarget
    , public HTML::AbstractWorker {
    WEB_PLATFORM_OBJECT(Worker, DOM::EventTarget);
    GC_DECLARE_ALLOCATOR(Worker);

public:
    static WebIDL::ExceptionOr<GC::Ref<Worker>> create(JS::Realm& realm, TrustedTypes::TrustedScriptURLOrString const& script_url, Bindings::WorkerOptions const& options);
    static WebIDL::ExceptionOr<GC::Ref<Worker>> construct_impl(JS::Realm& realm, TrustedTypes::TrustedScriptURLOrString const& script_url, Bindings::WorkerOptions const& options)
    {
        return Worker::create(realm, script_url, options);
    }

    WebIDL::ExceptionOr<void> terminate();

    WebIDL::ExceptionOr<void> post_message(JS::Value message, Bindings::StructuredSerializeOptions const&);
    WebIDL::ExceptionOr<void> post_message(JS::Value message, GC::RootVector<GC::Ref<JS::Object>> const& transfer);

    virtual ~Worker() = default;

    GC::Ptr<MessagePort> outside_message_port() { return m_outside_port; }

    void set_agent(WorkerAgentParent& agent) { m_agent = agent; }

#undef __ENUMERATE
#define __ENUMERATE(attribute_name, event_name)       \
    void set_##attribute_name(WebIDL::CallbackType*); \
    WebIDL::CallbackType* attribute_name();
    ENUMERATE_WORKER_EVENT_HANDLERS(__ENUMERATE)
#undef __ENUMERATE

protected:
    Worker(JS::Realm&, String const&, Bindings::WorkerOptions const&);

    // ^AbstractWorker
    virtual DOM::EventTarget& this_event_target() override { return *this; }

private:
    virtual void initialize(JS::Realm&) override;
    virtual void visit_edges(Cell::Visitor&) override;

    String m_script_url;
    Bindings::WorkerOptions m_options;

    GC::Ptr<MessagePort> m_outside_port;

    GC::Ptr<WorkerAgentParent> m_agent;
};

void run_a_worker(Variant<GC::Ref<Worker>, GC::Ref<SharedWorker>> worker, URL::URL& url, EnvironmentSettingsObject& outside_settings, GC::Ptr<MessagePort> outside_port, Bindings::WorkerOptions const& options);

}
