import { Delegate, Helper, Template, UpdateController } from "@24tools/playable_template";
import { Object3D, Vector3 } from "three";
import { ThreeC } from "../../../ThreeC";
import { CameraC } from "../../../CameraC";

export class FollowCameraC {
    private static updateDelegate: Delegate<number>;
    static target: Object3D;
    static offset: Vector3;

    static mainContainer: Object3D = new Object3D();
    static cameraContainer: Object3D = new Object3D();
    static cameraRotation: Object3D = new Object3D();

    static inputDirection: Vector3 = new Vector3();
    static lookAheadAmount = 1;
    static lookAheadLerpSpeed = 0.7;

    private static lookAheadCurrent: Vector3 = new Vector3();
    private static lookAheadTarget: Vector3 = new Vector3();
    private static basePos: Vector3 = new Vector3();
    private static targetPos: Vector3 = new Vector3();
    private static oldPos: Vector3 = new Vector3();

    static init(target: Object3D) {
        this.target = target;
        this.offset = this.Offset;

        this.updateDelegate = new Delegate<number>(this.update.bind(this));
        UpdateController.Instance.onUpdate.addListener(this.updateDelegate);

        ThreeC.addToScene(this.mainContainer);
        this.mainContainer.add(this.cameraContainer);
        this.cameraContainer.add(this.cameraRotation);
        this.cameraRotation.add(CameraC.cameraContainer);

        this.cameraRotation.rotateY(Math.PI);

        this.mainContainer.position.copy(target.position);
        this.mainContainer.position.x += this.Offset.x;
        this.mainContainer.position.z += this.Offset.z;

        this.cameraContainer.position.y += this.Offset.y;
    }

    private static update(delta: number) {
        if (!this.target.position) return;

        this.lookAheadTarget.copy(this.inputDirection).multiplyScalar(this.lookAheadAmount);
        this.lookAheadCurrent.lerp(this.lookAheadTarget, delta * this.lookAheadLerpSpeed);

        const offset = this.Offset;

        this.basePos.copy(this.target.position);
        this.basePos.x += offset.x;
        this.basePos.z += offset.z;

        this.targetPos.copy(this.basePos);
        this.targetPos.x += this.lookAheadCurrent.x;
        this.targetPos.z += this.lookAheadCurrent.z;

        this.oldPos.copy(this.mainContainer.position);

        this.mainContainer.position.copy(this.basePos);
        this.mainContainer.lookAt(this.target.position);
        this.cameraContainer.lookAt(this.target.position);

        const lerpSpeed = 10;
        this.mainContainer.position.lerpVectors(this.oldPos, this.targetPos, delta * lerpSpeed);
    }

    static get RotationCorrection() {
        return this.mainContainer.rotation.clone();
    }

    static get Offset() {
        const portrait = window.screenSize.portrait;
        const values = portrait
            ? Template.getValue<number[]>("global", "camera_position_p")
            : Template.getValue<number[]>("global", "camera_position_l");
        return Helper.returnVectorCamera(values);
    }
}
