import WBVector3 from '../../util/wbVector3';
export var HeadPosition;
(function (HeadPosition) {
    HeadPosition["RIGHT"] = "right";
    HeadPosition["CENTER"] = "center";
    HeadPosition["LEFT"] = "left";
})(HeadPosition || (HeadPosition = {}));
// Factor between 1 unit in real world and 1 unit in animation world
const ROA_FACTOR = 1.0 / 1.5;
// Approximate head positions when the wither is in idle mode and facing North
const APPROX_IDLE_HEAD_POSITIONS = {
    [HeadPosition.LEFT]: { x: -4.2 * ROA_FACTOR, y: 10.6 * ROA_FACTOR, z: -4.6 * ROA_FACTOR },
    [HeadPosition.CENTER]: { x: 0 * ROA_FACTOR, y: 13.5 * ROA_FACTOR, z: -3.5 * ROA_FACTOR },
    [HeadPosition.RIGHT]: { x: 4.6 * ROA_FACTOR, y: 10.3 * ROA_FACTOR, z: -4.7 * ROA_FACTOR }
};
export default class HeadAttack {
    constructor(wither, headPos) {
        this.headPos = headPos;
        this.wither = wither;
        [
            this.headTargetYawPropertyName,
            this.headTargetPitchPropertyName
        ] = this.getHeadTargetPropertyNames(headPos);
    }
    // Get an estimate of the head location when the wither is in idle mode
    getApproxIdleHeadLocation(headPos) {
        const entity = this.wither.getEntity();
        if (entity) {
            const witherLocation = entity.location;
            const witherYawDegrees = entity.getRotation().y;
            const headOffset = APPROX_IDLE_HEAD_POSITIONS[headPos];
            const rotationFactor = ((witherYawDegrees + 180) + 360) % 360;
            const headOffsetRotated = WBVector3.fromMC(headOffset).rotateY(rotationFactor);
            return headOffsetRotated.add(witherLocation);
        }
        return undefined;
    }
    // Get the target property names for the specified head position
    getHeadTargetPropertyNames(headPos) {
        switch (headPos) {
            case HeadPosition.LEFT:
                return ['twb_wisu:left_head_target_yaw', 'twb_wisu:left_head_target_pitch'];
            case HeadPosition.CENTER:
                return ['twb_wisu:center_head_target_yaw', 'twb_wisu:center_head_target_pitch'];
            case HeadPosition.RIGHT:
                return ['twb_wisu:right_head_target_yaw', 'twb_wisu:right_head_target_pitch'];
            default:
                throw new Error('Invalid head position');
        }
    }
    // Get the nearest target within the specified range and angle
    getNearestTarget(headPos, maxDistanceSquared, maxAngle) {
        // Get required data
        const entity = this.wither.getEntity();
        if (!entity) {
            return undefined;
        }
        const targets = this.wither.getTargetController().getTargets();
        const headLocation = this.getApproxIdleHeadLocation(headPos);
        if (!headLocation) {
            return undefined;
        }
        // const headLocation = this.wither.getEntity().location;
        const witherViewDirection = entity.getViewDirection();
        const witherViewDirectionN = WBVector3.fromMC(witherViewDirection).normalize();
        // Determine nearest target
        let nearestValidTarget;
        let nearestValidDistanceSquared = Number.MAX_VALUE;
        for (const target of targets) {
            // Step 1: Check if the target is in range
            const targetLocation = WBVector3.fromMC(target.location);
            const distanceSquared = WBVector3.distanceSquared(headLocation, targetLocation);
            if (distanceSquared > maxDistanceSquared) {
                continue;
            }
            // Step 2: Check if the target is in the right direction
            const targetDirection = targetLocation.subtract(headLocation).normalize();
            const angleDegrees = targetDirection.angleDegrees(witherViewDirectionN);
            if (Math.abs(angleDegrees) > maxAngle) {
                continue;
            }
            // Step 3: Check if the target is nearer than the current nearest target
            if (distanceSquared < nearestValidDistanceSquared) {
                nearestValidTarget = target;
                nearestValidDistanceSquared = distanceSquared;
            }
        }
        return nearestValidTarget;
    }
}
