#pragma once

#include <AK/Optional.h>
#include <AK/String.h>
#include <AK/Types.h>
#include <AK/Utf16String.h>
#include <AK/Variant.h>
#include <LibGC/Ptr.h>
#include <LibJS/Forward.h>
#include <LibJS/Runtime/NativeFunction.h>
#include <LibJS/Runtime/Object.h>
#include <LibJS/Runtime/Value.h>
#include <LibWeb/Bindings/AnimationEffect.h>
#include <LibWeb/Bindings/InterfaceObject.h>
#include <LibWeb/Forward.h>

namespace Web::Bindings {

struct AnimationEffectConstructor {
public:
    static void initialize(JS::Realm&, JS::NativeFunction&);
    static JS::ThrowCompletionOr<GC::Ref<JS::Object>> construct(InterfaceConstructor&, JS::FunctionObject&);

private:
};

struct AnimationEffectPrototype {
public:
    static void initialize(JS::Realm&, JS::Object&);
    static void define_unforgeable_attributes(JS::Realm&, JS::Object&);

private:
    JS_DECLARE_NATIVE_FUNCTION(get_timing);
    JS_DECLARE_NATIVE_FUNCTION(get_computed_timing);
    JS_DECLARE_NATIVE_FUNCTION(update_timing);
};

enum class FillMode : u8 {
    None,
    Forwards,
    Backwards,
    Both,
    Auto,
};

JS::ThrowCompletionOr<FillMode> convert_to_idl_value_for_fill_mode(JS::VM&, JS::Value);

Utf16String idl_enum_to_string(FillMode);

enum class PlaybackDirection : u8 {
    Normal,
    Reverse,
    Alternate,
    AlternateReverse,
};

JS::ThrowCompletionOr<PlaybackDirection> convert_to_idl_value_for_playback_direction(JS::VM&, JS::Value);

Utf16String idl_enum_to_string(PlaybackDirection);

struct EffectTiming {
    double delay { 0 };
    PlaybackDirection direction { PlaybackDirection::Normal };
    Variant<double, GC::Ref<CSS::CSSNumericValue>, String> duration { Variant<double, GC::Ref<CSS::CSSNumericValue>, String> { "auto"_string } };
    String easing { "linear"_string };
    double end_delay { 0 };
    FillMode fill { FillMode::Auto };
    double iteration_start { 0.0 };
    double iterations { 1.0 };
};

JS::ThrowCompletionOr<EffectTiming> convert_to_idl_value_for_effect_timing(JS::VM&, JS::Value);

struct OptionalEffectTiming {
    Optional<double> delay {};
    Optional<PlaybackDirection> direction {};
    Optional<Variant<double, String>> duration {};
    Optional<String> easing {};
    Optional<double> end_delay {};
    Optional<FillMode> fill {};
    Optional<double> iteration_start {};
    Optional<double> iterations {};
};

JS::ThrowCompletionOr<OptionalEffectTiming> convert_to_idl_value_for_optional_effect_timing(JS::VM&, JS::Value);

struct ComputedEffectTiming : public EffectTiming {
    Optional<Variant<double, GC::Ref<CSS::CSSNumericValue>>> active_duration {};
    Optional<Optional<double>> current_iteration {};
    Optional<Variant<double, GC::Ref<CSS::CSSNumericValue>>> end_time {};
    Optional<Variant<double, GC::Ref<CSS::CSSNumericValue>, Empty>> local_time {};
    Optional<Optional<double>> progress {};
};

JS::ThrowCompletionOr<ComputedEffectTiming> convert_to_idl_value_for_computed_effect_timing(JS::VM&, JS::Value);

} // namespace Web::Bindings
