
import { Node, Vec3 } from "cc";
import { Timer } from "../../../../../../extensions/oops-plugin-framework/assets/core/common/timer/Timer";
import { Vec3Util } from "../../../../../../extensions/oops-plugin-framework/assets/core/utils/Vec3Util";
import { ecs } from "../../../../../../extensions/oops-plugin-framework/assets/libs/ecs/ECS";

/** 向目标移动，移动过程中目标位置变化会自动修正移动目标点，直到未修正前移动到目标点停止 */
@ecs.register('MoveToPath')
export class MoveToPathComp extends ecs.Comp {
    /** 移动节点 */
    node: Node = null!;
    /** 移动速度（每秒移动的像素距离） */
    speed: number = 0;
    /** 移动方向 */
    velocity: Vec3 = Vec3Util.zero;
    /** 路径数组 */
    paths: Vec3[] | null = null;
    /** 坐标标（默认本地坐标） */
    ns: number = Node.NodeSpace.LOCAL;
    /** 移动完成回调 */
    onComplete: Function | null = null;
    /** 距离变化时 */
    onChange: Function | null = null;

    reset() {
        this.ns = Node.NodeSpace.LOCAL;
        this.onComplete = null;
        this.onChange = null;
    }
}

@ecs.register('VariableMoveToPath')
class VariableMoveToPathComponent extends ecs.Comp {
    /** 延时触发器 */
    timer: Timer = new Timer();
    /** 距离 */
    distance: number = 0;
    /** 起点备份 */
    start: Vec3 | null = null;
    /** 终点备份 */
    end: Vec3 | null = null;
    /** 目标位置 */
    target!: Vec3;

    isComplete: boolean = false


    reset() {
        this.end = null;
        this.isComplete = false
        this.timer.reset();
    }
}

/** 按照路径移动 */
export class MoveToPathSystem extends ecs.ComblockSystem<ecs.Entity> implements ecs.IEntityEnterSystem, ecs.IEntityRemoveSystem, ecs.ISystemUpdate {
    filter(): ecs.IMatcher {
        return ecs.allOf(MoveToPathComp);
    }

    entityEnter(e: ecs.Entity): void {
        e.add(VariableMoveToPathComponent);
    }

    entityRemove(e: ecs.Entity): void {
        e.remove(VariableMoveToPathComponent);
    }

    update(e: ecs.Entity) {
        let move = e.get(MoveToPathComp);
        let mtv = e.get(VariableMoveToPathComponent);

        // console.assert(move.speed > 0, "移动速度必须要大于零");
        // 如果没有路径点或者已经完成当前路径点的移动，则获取下一个路径点
        if ((move.paths && move.paths.length > 0)) {
            if ((mtv.end == null || mtv.isComplete)) {
                mtv.reset()
                let end = move.paths.shift();
                this.prepareMovement(e, move, mtv, end);
            }

        } else if (mtv.end == null || mtv.isComplete) {
            this.exit(e)
        }


        // 执行移动
        if (move.speed > 0 && mtv.end != null && !mtv.isComplete) {
            let trans = Vec3Util.mul(move.velocity, move.speed * this.dt);
            let start = move.ns == Node.NodeSpace.WORLD ? move.node.worldPosition : move.node.position;
            let end = Vec3Util.add(start, trans)
            if (Vec3Util.sub(end, mtv.start).length() > mtv.distance||mtv.timer.update(this.dt)) {
                this.finishMovement(e, move, mtv);
                mtv.isComplete = true
            } else {
                move.node.translate(trans, move.ns);
                move.node.forward = trans
            }

        }



    }

    private prepareMovement(e: ecs.Entity, move: MoveToPathComp, mtv: VariableMoveToPathComponent, end: Vec3) {
        let target = end.clone();
        let start = move.ns == Node.NodeSpace.WORLD ? move.node.worldPosition : move.node.position;
        move.velocity = Vec3Util.sub(target, start).normalize();
        let distance = Vec3.distance(start, target);
        move.onChange?.call(this);

        if (distance <= 0) {
            // this.exit(e);
            mtv.isComplete = true
        } else {
            mtv.timer.step = distance / move.speed;
            mtv.end = end.clone();
            mtv.target = target;
            mtv.start = start.clone()
            mtv.distance = Vec3.distance(mtv.end, mtv.start);
        }
    }

    private finishMovement(e: ecs.Entity, move: MoveToPathComp, mtv: VariableMoveToPathComponent) {
        if (move.ns == Node.NodeSpace.WORLD)
            move.node.worldPosition = mtv.target;
        else
            move.node.position = mtv.target;

        // this.exit(e);
    }

    private exit(e: ecs.Entity) {
        let move = e.get(MoveToPathComp);
        move.onComplete?.call(this);
        e.remove(VariableMoveToPathComponent);
        e.remove(MoveToPathComp);
    }
}