import { assert } from "../../../../../core/assert.js";
import { clamp01 } from "../../../../../core/math/clamp01.js";
import { lerp } from "../../../../../core/math/lerp.js";
import { SerializationMetadata } from "../../../../ecs/components/SerializationMetadata.js";
import Entity from "../../../../ecs/Entity.js";
import { RepeatBehavior } from "../../../../intelligence/behavior/decorator/RepeatBehavior.js";
import { BehaviorComponent } from "../../../../intelligence/behavior/ecs/BehaviorComponent.js";
import { ActionBehavior } from "../../../../intelligence/behavior/primitive/ActionBehavior.js";
import TopDownCameraController from "../topdown/TopDownCameraController.js";

/**
 *
 * @param {TopDownCameraController} target
 * @param {EntityComponentDataset} ecd
 * @param {number} [responsiveness] higher value results in sharper movement when following
 * @param {number} [inertia]
 * @returns {{controller:TopDownCameraController, entity:Entity}}
 */
export function setup_filtered_camera_controller({
                                                     target,
                                                     ecd,
                                                     responsiveness = 10,
                                                     inertia = 0.1
                                                 }) {

    assert.defined(target, 'target');
    assert.defined(ecd, 'ecd');
    assert.isNumber(responsiveness, 'responsiveness');

    const result = new TopDownCameraController();
    result.copy(target);

    const remembered_value = new TopDownCameraController();
    remembered_value.copy(result);

    const entityBuilder = new Entity();

    entityBuilder
        .add(BehaviorComponent.from(
            RepeatBehavior.from(
                new ActionBehavior(time_delta => {
                    if (!remembered_value.equals(target)) {

                        // external change detected, override filtered result
                        result.copy(target);


                    } else {

                        const d = clamp01(1 - Math.exp(-responsiveness * time_delta));

                        target.distance = lerp(target.distance, result.distance, d);

                        target.pitch = result.pitch;
                        target.yaw = result.yaw;
                        target.roll = result.roll;

                        target.distanceMin = lerp(target.distanceMin, result.distanceMin, d);
                        target.distanceMax = lerp(target.distanceMax, result.distanceMax, d);

                        target.target.copy(result.target);
                    }


                    // remember current value
                    remembered_value.copy(target);
                })
            )
        ))
        .add(SerializationMetadata.Transient)
        .build(ecd);


    return {
        controller: result,
        entity: entityBuilder
    };
}
