import Vector3 from "../../../../../core/geom/Vector3.js";
import { clamp } from "../../../../../core/math/clamp.js";
import ObservedValue from "../../../../../core/model/ObservedValue.js";
import { ResourceAccessKind } from "../../../../../core/model/ResourceAccessKind.js";
import { ResourceAccessSpecification } from "../../../../../core/model/ResourceAccessSpecification.js";
import { System } from '../../../../ecs/System.js';
import { Transform } from '../../../../ecs/transform/Transform.js';


import TopDownCameraController from './TopDownCameraController.js';


const v3_scratch = new Vector3();

class TopDownCameraControllerSystem extends System {
    constructor() {
        super();

        this.enabled = new ObservedValue(true);
        this.dependencies = [TopDownCameraController];

        this.components_used = [
            ResourceAccessSpecification.from(Transform,ResourceAccessKind.Write)
        ];
    }

    /**
     *
     * @param {TopDownCameraController} ctrl
     * @param {number} entity
     * @private
     */
    __visit_camera_entity(ctrl, entity){
        const transform = this.entityManager.dataset.getComponent(entity,Transform);

        if(transform === undefined){
            return;
        }

        computeTopDownTransform(ctrl,transform);
    }

    update(timeDelta) {
        const em = this.entityManager;

        /**
         *
         * @type {EntityComponentDataset}
         */
        const dataset = em.dataset;

        if (this.enabled.get() && dataset !== null) {
            dataset.traverseComponents(TopDownCameraController, this.__visit_camera_entity, this);
        }

    }
}

/**
 *
 * @param {TopDownCameraController} control
 * @param {Transform} result
 */
export function computeTopDownTransform(control, result) {

    const rotation = result.rotation;

    //clamp the distance
    const distance = clamp(control.distance, control.distanceMin, control.distanceMax);
    const target = control.target;

    const yaw = control.yaw;
    const pitch = control.pitch;
    const roll = control.roll;

    // compute rotation
    rotation.fromEulerAnglesZYX(pitch, yaw, roll);

    // compute camera position
    v3_scratch.copy(Vector3.forward);
    v3_scratch.applyQuaternion(rotation);

    v3_scratch.multiplyScalar(-distance);

    v3_scratch.add(target);

    result.position.copy(v3_scratch);
}

/**
 *
 * @param {Transform} transform
 * @param {TopDownCameraController} controller
 * @param {number} distance
 */
export function setCameraControllerFromTransform(transform, controller, distance = 20) {
    const v_f = Vector3.forward.clone();
    v_f.multiplyScalar(distance);
    v_f.applyQuaternion(transform.rotation);

    controller.setFromEyeAndTarget(transform.position,v_f);
}

export default TopDownCameraControllerSystem;
