import { PerspectiveCamera } from "three";

import { getCameraController } from "../engine/engine_camera.js";
import { addNewComponent, getOrAddComponent } from "../engine/engine_components.js";
import { Context } from "../engine/engine_context.js";
import { ContextEvent, ContextRegistry } from "../engine/engine_context_registry.js";
import { NeedleEngineHTMLElement } from "../engine/engine_element.js";
import type { ICamera, IContext } from "../engine/engine_types.js";
import { getParam } from "../engine/engine_utils.js";
import { RGBAColor } from "../engine/js-extensions/index.js";
import { Camera, ClearFlags } from "./Camera.js";
import { OrbitControls } from "./OrbitControls.js";

const debug = getParam("debugmissingcamera");

ContextRegistry.registerCallback(ContextEvent.MissingCamera, (evt) => {
    if (debug) console.warn("Creating missing camera")
    const scene = evt.context.scene;

    const cameraObject = new PerspectiveCamera();
    cameraObject.name = "Default Fallback Camera"
    scene.add(cameraObject);

    const camInstance = new Camera();
    camInstance.sourceId = evt.files?.[0]?.src ?? "unknown"

    // Set the clearFlags to a skybox if we have one OR if the user set a skybox image attribute
    if(evt.context.domElement.getAttribute("skybox-image")?.length || 0 > 0 || (evt.context as Context).lightmaps.tryGetSkybox(camInstance.sourceId))
        camInstance.clearFlags = ClearFlags.Skybox;
    else 
        // TODO provide a nice default skybox
        camInstance.clearFlags = ClearFlags.SolidColor;
    camInstance.backgroundColor = new RGBAColor(0.5, 0.5, 0.5, 1);
    camInstance.fieldOfView = 35;

    const transparentAttribute = evt.context.domElement.getAttribute("transparent");
    if (transparentAttribute != undefined) {
        camInstance.clearFlags = ClearFlags.Uninitialized;
    }

    // TODO: can we store the backgroundBlurriness in the gltf file somewhere except inside the camera?
    // e.g. when we export a scene from blender without a camera in the scene
    camInstance.backgroundBlurriness = .2; // same as in blender 0.5
    const cam = addNewComponent(cameraObject, camInstance, true) as ICamera;

    cameraObject.position.x = 0;
    cameraObject.position.y = 1;
    cameraObject.position.z = 2;

    const engineElement = evt.context.domElement as NeedleEngineHTMLElement
    // If the camera is missing and the <needle-engine controls> is not set to false, create default camera controls
    // That way we still create controls if the attribute is not added to <needle-engine> at all
    if (engineElement?.cameraControls != false) {
        createDefaultCameraControls(evt.context, cam);
    }

    return cam;
});

ContextRegistry.registerCallback(ContextEvent.ContextCreated, (evt) => {
    if (!evt.context.mainCamera) {
        if (debug) console.log("Will not auto-fit because a default camera exists");
        return;
    }

    // check if <needle-engine camera-controls> attribute is present or enabled
    const engineElement = evt.context.domElement as NeedleEngineHTMLElement
    if (engineElement?.cameraControls == true) {

        // Check if something else already acts as a camera controller
        const existing = getCameraController(evt.context.mainCamera);
        if (existing?.isCameraController == true) {
            if (debug) console.log("Will not auto-fit because a camera controller exists");
            return;
        }
        createDefaultCameraControls(evt.context);
    }
})

function createDefaultCameraControls(context: IContext, cam?: ICamera) {

    cam = cam ?? context.mainCameraComponent;
    const cameraObject = cam?.gameObject;
    if (debug) console.log("Creating default camera controls", cam?.name)
    if (cameraObject) {
        const orbit = getOrAddComponent(cameraObject, OrbitControls) as OrbitControls;
        orbit.sourceId = cam?.sourceId ?? "unknown";
        const autoRotate = context.domElement.getAttribute("auto-rotate");
        orbit.autoRotate = autoRotate !== undefined && autoRotate !== null && (autoRotate != "0" && autoRotate?.toLowerCase() != "false");
        orbit.autoRotateSpeed = 0.5;
        orbit.autoFit = true;
        if (orbit.autoRotate && autoRotate) {
            const autoRotateValue = parseFloat(autoRotate);
            if (!isNaN(autoRotateValue)) {
                orbit.autoRotateSpeed = autoRotateValue;
            }
        }
    }
    else {
        console.warn("Missing camera object, can not add orbit controls")
    }
}