const Player = Java.loadClass('net.minecraft.world.entity.player.Player')
const Vec3 = Java.loadClass('net.minecraft.world.phys.Vec3')

/*
 * ボスのエンティティID
 *
 * エンティティ登録時のIDに合わせてください。
 * 例:
 * event.create('avadon', 'entityjs:mob')
 * の場合は kubejs:avadon
 */
const BOSS_ID = 'kubejs:avadon'

/*
 * 調整用設定
 */

// 突進速度
const CHARGE_SPEED = 3

// プレイヤーの上空へ移動する速度
const HOVER_SPEED = 2

// プレイヤーから何ブロック上に浮かぶか
const HOVER_HEIGHT = 7.0

// 突進する時間
// 20tick = 約1秒
const CHARGE_TIME = 50

// 空中で待機する時間
const HOVER_TIME = 30

// この距離までプレイヤーに近づくと攻撃
const HIT_DISTANCE = 2.5


/*
 * distanceTo() を使わず、座標から距離を計算する
 */
function distanceBetween(entityA, entityB) {
    const dx = entityA.getX() - entityB.getX()
    const dy = entityA.getY() - entityB.getY()
    const dz = entityA.getZ() - entityB.getZ()

    return Math.sqrt(
        dx * dx +
        dy * dy +
        dz * dz
    )
}


/*
 * ターゲット設定
 *
 * EntityJSの内部フィールド名は addGoalTargets ですが、
 * KubeJSから使用するイベント名は addGoals です。
 */
EntityJSEvents.addGoals(BOSS_ID, event => {
    event.nearestAttackableTarget(
        1,          // 優先度
        Player,     // ターゲットにするエンティティ
        10,         // ターゲット更新間隔
        true,       // 視線が必要
        false,      // 到達可能である必要はない
        null        // 追加条件
    )
})


/*
 * AIゴール設定
 */
EntityJSEvents.addGoalSelectors(BOSS_ID, event => {
    // 既存のAIを削除
    event.removeAllGoals()

    event.customGoal(
        'charge_and_hover',
        1,

        /*
         * canUse
         *
         * このAIを開始できる条件
         */
        mob => {
            const target = mob.getTarget()

            return (
                target != null &&
                !target.isDeadOrDying()
            )
        },

        /*
         * canContinueToUse
         *
         * このAIを継続する条件
         */
        mob => {
            const target = mob.getTarget()

            return (
                target != null &&
                !target.isDeadOrDying()
            )
        },

        /*
         * 割り込み可能か
         */
        false,

        /*
         * start
         *
         * AI開始時に1回だけ実行
         */
        mob => {
            const data = mob.getPersistentData()

            data.putString('AvadonPhase', 'charge')
            data.putInt('AvadonTimer', 0)
            data.putBoolean('AvadonHit', false)

            // 空中に浮かぶため重力を無効化
            mob.setNoGravity(true)
        },

        /*
         * stop
         *
         * AI停止時に1回だけ実行
         */
        mob => {
            mob.setDeltaMovement(new Vec3(0, 0, 0))
            mob.setNoGravity(false)
        },

        /*
         * 毎tick実行するか
         */
        true,

        /*
         * tick
         *
         * AI実行中、毎tick実行
         */
        mob => {
            const target = mob.getTarget()

            /*
             * ターゲットがいなくなった場合
             */
            if (
                target == null ||
                target.isDeadOrDying()
            ) {
                mob.setDeltaMovement(new Vec3(0, 0, 0))
                return
            }

            const data = mob.getPersistentData()

            let phase = data.getString('AvadonPhase')
            let timer = data.getInt('AvadonTimer')

            /*
             * 初期値
             */
            if (phase === '') {
                phase = 'charge'
                timer = 0
            }


            /*
             * フェーズ1:
             * プレイヤーに向かって突進
             */
            if (phase === 'charge') {
                const dx = target.getX() - mob.getX()
                const dy = target.getY() + 1.0 - mob.getY()
                const dz = target.getZ() - mob.getZ()

                const distance = Math.sqrt(
                    dx * dx +
                    dy * dy +
                    dz * dz
                )

                /*
                 * プレイヤーの方向へ移動
                 */
                if (distance > 0.001) {
                    const vx = dx / distance * CHARGE_SPEED
                    const vy = dy / distance * CHARGE_SPEED
                    const vz = dz / distance * CHARGE_SPEED

                    mob.setDeltaMovement(
                        new Vec3(vx, vy, vz)
                    )
                }

                /*
                 * プレイヤーに近づいたら攻撃
                 *
                 * distanceTo() は使わず、
                 * distanceBetween() で距離を計算する
                 */
                if (
                    !data.getBoolean('AvadonHit') &&
                    distanceBetween(mob, target) <= HIT_DISTANCE
                ) {
                    mob.doHurtTarget(target)

                    // 同じ突進中に何度も攻撃しない
                    data.putBoolean('AvadonHit', true)

                    // 攻撃後は浮遊フェーズへ移行
                    phase = 'hover'
                    timer = 0

                    mob.setDeltaMovement(
                        new Vec3(0, 0, 0)
                    )
                }

                /*
                 * 一定時間突進しても当たらなかった場合も、
                 * 浮遊フェーズへ移行する
                 */
                if (timer >= CHARGE_TIME) {
                    phase = 'hover'
                    timer = 0

                    data.putBoolean('AvadonHit', false)

                    mob.setDeltaMovement(
                        new Vec3(0, 0, 0)
                    )
                }
            }


            /*
             * フェーズ2:
             * プレイヤーの上空に移動して浮遊
             */
            else if (phase === 'hover') {
                const hoverX = target.getX()
                const hoverY = target.getY() + HOVER_HEIGHT
                const hoverZ = target.getZ()

                const dx = hoverX - mob.getX()
                const dy = hoverY - mob.getY()
                const dz = hoverZ - mob.getZ()

                const distance = Math.sqrt(
                    dx * dx +
                    dy * dy +
                    dz * dz
                )

                /*
                 * プレイヤーの上空から離れている場合は、
                 * ゆっくり移動する
                 */
                if (distance > 1.0) {
                    const vx = dx / distance * HOVER_SPEED
                    const vy = dy / distance * HOVER_SPEED
                    const vz = dz / distance * HOVER_SPEED

                    mob.setDeltaMovement(
                        new Vec3(vx, vy, vz)
                    )
                } else {
                    /*
                     * プレイヤーの上空に到着したら停止
                     */
                    mob.setDeltaMovement(
                        new Vec3(0, 0, 0)
                    )
                }

                /*
                 * 一定時間浮遊したら、
                 * 再び突進フェーズへ移行
                 */
                if (timer >= HOVER_TIME) {
                    phase = 'charge'
                    timer = 0

                    data.putBoolean('AvadonHit', false)
                }
            }


            /*
             * 現在のフェーズとタイマーを保存
             */
            data.putString('AvadonPhase', phase)
            data.putInt('AvadonTimer', timer + 1)
        }
    )
})