import { GdProjectileEffect, GdRoot, GdTower } from "../../GameData/index.js";
import { Hex, Vector2 } from "../../Util/index.js";
import { SimLevel, SimProjectile } from "../index.js";

export class SimTower {
    private _lastProjectileStep: number = -1;
    private _lastAoeStep: number = -1;
    private _gdProjectileEffect: GdProjectileEffect | null;
    private _projectileRange: number = -1;
    private _projectileRate: number = -1;
    private _projectileSize: number = -1;
    private _gdAoeEffect: GdProjectileEffect | null;
    private _aoeRange: number = -1;
    private _aoeRate: number = -1;
    private _gdTower: GdTower;
    private _position: Vector2;
    private _currentProjectileTarget: number = -1;
    private _simLevel: SimLevel;
    private _index: number;
    private _gdRoot: GdRoot;

    constructor(gdRoot: GdRoot, level: SimLevel, index: number, hex: Hex) {
        this._gdRoot = gdRoot;
        this._simLevel = level;
        this._position = Hex.toWorld(hex);
        this._index = index;

        const data = this._gdRoot.towers[index];
        this._gdProjectileEffect = data.projectileEffect;
        this._projectileRange = data.projectileRange;
        this._projectileRate = data.projectileRate;
        this._projectileSize = data.projectileSize;
        this._gdAoeEffect = data.aoeEffect;
        this._aoeRange = data.aoeRange;
        this._aoeRate = data.aoeRate;
        this._gdTower = data;
    }

    public get index(): number {
        return this._index;
    }

    public fireIfAble() {
        const level = this._simLevel;
        const currentStep = this._simLevel.currentStep;
        if (this.canFireProjectile() && this._gdProjectileEffect != null) {
            level.simProjectiles.push(new SimProjectile(level, this._gdProjectileEffect, this._position, this.pickProjectileTarget(), this._projectileSize));
            this._lastProjectileStep = currentStep;
        }
        if (this.canFireAoe()) {
            // TODO
        }
    }

    private canFireProjectile() {
        if (this._gdProjectileEffect == null)
            return false;

        const currentStep = this._simLevel.currentStep;
        if (this._lastProjectileStep == -1 || this._lastProjectileStep + this._projectileRate < currentStep) {
            return this.isEnemyInRange(this._projectileRange);
        }
        return false;
    }

    private canFireAoe() {
        const level = this._simLevel;
        const currentStep = level.currentStep;
        if (this._gdAoeEffect != null) {
            if (this._lastAoeStep == -1 || this._lastAoeStep + this._aoeRate < currentStep) {
                return this.isEnemyInRange(this._aoeRange);
            }
        }
        return false;
    }

    private pickProjectileTarget() {
        if (this._currentProjectileTarget != -1) {
            return this._currentProjectileTarget;
        }

        let minLength = -1;
        let candidate = -1;
        const level = this._simLevel;
        for (let idx = 0; idx < level.simEnemies.length; idx++) {
            const enemy = level.simEnemies[idx];
            const delta = enemy.currentPathPosition.subtract(this._position);
            const length = delta.magnitude();
            if (enemy.path != null && length < this._projectileRange) {
                const pathLength = enemy.path.length - enemy.currentPathIndex;
                if (minLength == -1 || pathLength < minLength) {
                    candidate = idx;
                    minLength = pathLength;
                }
            }
        }

        return candidate;
    }

    private isEnemyInRange(range: number) {
        const level = this._simLevel;
        for (const idx in level.simEnemies) {
            const enemy = level.simEnemies[idx];
            const delta = enemy.currentPathPosition.subtract(this._position);
            const length = delta.magnitude();
            if (length < range) {
                return true;
            }
        }
    }
}
