import { Block, BlockPermutation, Container, DimensionType, DimensionTypes, EffectType, EffectTypes, Entity, EntityComponentTypes, EntityDamageCause, EntityTameableComponent, ItemStack, MinecraftDimensionTypes, world } from "@minecraft/server";
import { system } from "./eternal/constants";
import { Vector2, Vector3 } from "./eternal/classes";
import entityAbilitySystem from "./AbilityCooldown";
const dimensionTypes = [MinecraftDimensionTypes.overworld, MinecraftDimensionTypes.nether, MinecraftDimensionTypes.theEnd]
class AnimalUtils {
    /**
     * 
     * @param {Entity} entity 
     * @param {EffectTypes} effect 
     */
    static ToucanEffect() {
        dimensionTypes.forEach((dim) => {
            const toucans = world.getDimension(dim).getEntities({ type: "ecbl_bc:toucan", "tags": ['tamed'] })
            toucans.forEach((entity) => {
                entity.dimension.getPlayers({ location: entity.location, "maxDistance": 10 }).forEach((player) => {

                    /**
                     * 
                     * @type {EntityTameableComponent}
                     */
                    const tamebale = entity.getComponent(EntityComponentTypes.Tameable);
                    if (tamebale.tamedToPlayerId == player.id) {
                        player.addEffect('minecraft:regeneration', 60);

                    }
                })
            })
        })
    }
    /**
     * 
     * @param {Entity} entity 
     * @param {number} tickAmount 
     */
    static FreezeEntity(entity, tickAmount) {
        return new Promise((resolve) => {
            const originalPos = entity.location;
            const originalRot = entity.getRotation()
            const unfreezeOnTick = system.currentTick + tickAmount;
            const runId = system.runInterval(() => {
                if (system.currentTick >= unfreezeOnTick) {

                    system.clearRun(runId);


                }
                else {
                    entity.setRotation(originalRot);
                    entity.teleport(originalPos)
                }

            })
        })
    }
    /**
     * 
     * @param {Entity} entity 
     * @param {Entity} target
     */
    static elephantCharge(entity, target) {
        try {
            const direction = entity.getViewDirection();
            const speed = entity.getVelocity();
            target.applyKnockback(direction.x, direction.z, 5, 0.5);
        }
        catch {

        }
    }
    /**
     * 
     * @param {Entity} target 
     * @param {number} knockbackAmount 
     * @param {Vector3} direction 
     */
    static ApplyKnockOnHit(target, knockbackAmount, direction) {

        target.applyKnockback(direction.x, direction.z, 1 * knockbackAmount, 0.3 * knockbackAmount);
    }
    /**
     * 
     * @param {Entity} entity 
     */
    static chargeAbility(entity) {
        if (entityAbilitySystem.AbilityReady(entity.id, 'charge')) {
            entityAbilitySystem.SetCooldown(entity.id, 100, 'charge')
            entity.setProperty('ecbl_bc:charging', true)
            const chargeDuration = 20;
            const chargeEnd = system.currentTick + chargeDuration;
            const viewDir = entity.getViewDirection();
            const chargeMultiplier = 4;
            viewDir.x *= chargeMultiplier;
            viewDir.z *= chargeMultiplier
            viewDir.y = 0;
            entity.applyImpulse(viewDir)
            const runId = system.runInterval(() => {
                const rider = entity.getComponent(EntityComponentTypes.Rideable).getRiders()[0];

                if (chargeEnd > system.currentTick) {
                    const targets = entity.dimension.getEntities({ location: entity.location, maxDistance: 2 });

                    targets.forEach((target) => {
                        if (target != entity && target != rider) {
                            target.applyDamage(10, {
                                "cause": EntityDamageCause.entityAttack,
                                "damagingEntity": entity
                            })
                        }
                        this.elephantCharge(entity, target)
                    })

                }
                else {
                    entity.setProperty('ecbl_bc:charging', false)
                    system.clearRun(runId)
                }
            })
        }
    }
    /**
     * 
     * @param {Entity} entity 
     */
    static async elephantPump(entity) {
        const basePos = { x: 0, y: -1, z: 3 };
        const degreesRot = entity.getRotation().y;
        const newPos = rotatePointInDegrees(basePos, degreesRot);
        const entityPos = entity.location;
        const newWorldPos = { x: entityPos.x + newPos.x, y: entityPos.y + newPos.y, z: entityPos.z + newPos.z }

        start: for (let x = -1; x <= 1; x++) {
            for (let z = -1; z <= 1; z++) {
                const newBlockLoc = { x: newWorldPos.x + x, y: newWorldPos.y, z: newWorldPos.z + z };
                const newBlock = entity.dimension.getBlock(newBlockLoc);
                if (newBlock.typeId === 'minecraft:water' || newBlock.permutation.getState('sea_grass_type') !== undefined) {


                    

                    entity.playAnimation("animation.ecbl_bc.elephant.water_pump");
                    entity.runCommand(`tag @s add spray_charged`)
                    await this.FreezeEntity(entity, 107);

                    break start;
                }
            }
        }



    }
    /**
     * 
     * @param {Entity} entity 
     */
    static dropItems(entity) {
        /**
         * @type {Container}
         */
        const inv = entity.getComponent(EntityComponentTypes.Inventory).container;
        for (let slot = 0; slot < inv.size; slot++) {
            try {
                entity.dimension.spawnItem(inv.getItem(slot), entity.location);
            }
            catch { }
        }
        inv.clearAll();
    }
    /**
     * 
     * @param {Entity} entity 
     */
    static elephantSpray(entity) {
        entity.runCommand(`tag @s add no_spray_charged`)
        entity.runCommand(`tag @s remove spray_charged`)

    
        entity.playAnimation('animation.ecbl_bc.elephant.water_spray');
        const basePos = { x: 0, y: 1, z: 6 };
        const degreesRot = entity.getRotation().y;
        const newPos = rotatePointInDegrees(basePos, degreesRot);
        const entityPos = entity.location;
        const newWorldPos = { x: entityPos.x + newPos.x, y: entityPos.y + newPos.y, z: entityPos.z + newPos.z }
        const duration = 90;
        const timeDone = system.currentTick + duration
        const runId = system.runInterval(() => {
            if (timeDone > system.currentTick) {
                system.runJob(FertilizeCrops(entity, 2, newWorldPos))
            }
            else {

                system.clearRun(runId)
            }
        }, 3)
    }
    /**
     * 
     * @param {Entity} entity 
     */
    static VultureHit(entity) {
        entity.dimension.spawnParticle('plank_waao:vulture_feather_particles', entity.location)
    }
    /**
     * 
     * @param {Entity} entity 
     * @param {string} deathEntityId 
     * @param {[{id:string,count:number}]} items 
     * @param {string[]} transferProperties 
     */
    static DeathAnimation(entity, deathEntityId, items, transferProperties) {
        const entityRotation = entity.getRotation();
        const entityPos = entity.location;
        const dimension = entity.dimension;

        const deathEntity = dimension.spawnEntity(deathEntityId, entityPos);
        deathEntity.setRotation(entityRotation)
        transferProperties.forEach((propertyId) => {
            deathEntity.setProperty(propertyId, entity.getProperty(propertyId))
        })
        entity.remove();
        items.forEach((dropInfo) => {
            const item = new ItemStack(dropInfo.id, dropInfo.count);
            dimension.spawnItem(item, entityPos)
        })


    }
}

world.afterEvents.entityHitEntity.subscribe(e => {

    const { hitEntity, damagingEntity } = e;
    switch (damagingEntity.typeId) {
        case "ecbl_bc:elephant":
            AnimalUtils.elephantCharge(damagingEntity, hitEntity);
            break;
        case "ecbl_bc:jaguar":
            AnimalUtils.ApplyKnockOnHit(hitEntity, 2, damagingEntity.getViewDirection());
            break

        case "ecbl_bc:moose":
            AnimalUtils.ApplyKnockOnHit(hitEntity, 1.2, damagingEntity.getViewDirection());
            break;
        case "ecbl_bc:bear":
            AnimalUtils.ApplyKnockOnHit(hitEntity, 1.1, damagingEntity.getViewDirection());
            break;


        default:
            break;
    }


});

system.afterEvents.scriptEventReceive.subscribe((event) => {
    if (event.id === 'ecbl_bc:spray' && event.sourceEntity.hasComponent(EntityComponentTypes.IsSaddled) && event.sourceEntity.getComponent(EntityComponentTypes.Rideable).getRiders().length >= 1) {
        AnimalUtils.elephantSpray(event.sourceEntity)
    }

    if (event.id === 'ecbl_bc:pump' && event.sourceEntity.hasComponent(EntityComponentTypes.IsSaddled) && event.sourceEntity.getComponent(EntityComponentTypes.Rideable).getRiders().length >= 1) {
        AnimalUtils.elephantPump(event.sourceEntity)
    }

    if (event.id == 'ecbl_bc:charge') {
        if (!event.sourceEntity.hasTag('ecbl_bc_pumnping') && event.sourceEntity.hasComponent(EntityComponentTypes.IsSaddled) && event.sourceEntity.getComponent(EntityComponentTypes.Rideable).getRiders().length >= 1) AnimalUtils.chargeAbility(event.sourceEntity)
    }

    if (event.id == 'ecbl_bc:drop_items') {
        AnimalUtils.dropItems(event.sourceEntity)
    }

    if (event.id == 'ecbl_bc:at_nest') {


        system.runJob(SquirrelCheck(event.sourceEntity))
    }

    if (event.id == "ecbl_bc:owl_to_nest") {

        system.runJob(OwlCheck(event))

    }
}
)


function* SquirrelCheck(entity) {

    start: for (let x = -6; x <= 6; x++) {
        for (let y = -6; y <= 6; y++) {
            for (let z = -6; z <= 6; z++) {
                yield;
                const entityLoc = entity.location;
                const newBlockLoc = { x: entityLoc.x + x, y: entityLoc.y + y, z: entityLoc.z + z };
                const newBlock = entity.dimension.getBlock(newBlockLoc);
                if (newBlock.typeId === "ecbl_bc:squirrel_nest" && !newBlock.permutation.getState('ecbl_bc:has_squirrel')) {
                    newBlock.setPermutation(newBlock.permutation.withState('ecbl_bc:has_squirrel', true))
                    entity.remove();
                    break start;

                }
            }
        }

    }


}
function* OwlCheck(event) {
    try {
        const entityLoc = event.sourceEntity.location;
        start: for (let x = -1; x <= 1; x++) {
            for (let y = -1; y <= 1; y++) {
                for (let z = -1; z <= 1; z++) {
                    yield;
                    const newBlockLoc = { x: entityLoc.x + x, y: entityLoc.y + y, z: entityLoc.z + z };
                    const newBlock = event.sourceEntity.dimension.getBlock(newBlockLoc);
                    if (newBlock.typeId === "ecbl_bc:owl_nest" && !newBlock.permutation.getState('ecbl_bc:has_owl')) {
                        newBlock.setPermutation(newBlock.permutation.withState('ecbl_bc:has_owl', true))
                        event.sourceEntity.remove();
                        break start;
                    }
                }
            }
        }

    }
    catch { }
}
/** 
 * 
 * @param {import("@minecraft/server").Vector3} pos 
 * @param {number} degrees 
 * @returns {import("@minecraft/server").Vector3}
 */
function rotatePointInDegrees(pos, degrees) {
    const x = pos.x;
    const y = pos.z;
    const degreesInRad = degrees * (Math.PI / 180);

    const newX = x * Math.cos(degreesInRad) - y * Math.sin(degreesInRad);
    const newY = x * Math.sin(degreesInRad) + y * Math.cos(degreesInRad);
    return { x: newX, y: pos.y, z: newY }
}

/**
 * 
 * @param {Entity} entity 
 * @param {number} radius 
 */
function* FertilizeCrops(entity, radius, baseLoc) {

    for (let x = -radius; x <= radius; x++) {
        for (let z = -radius; z <= radius; z++) {


            const newPos = { x: baseLoc.x + x, y: baseLoc.y, z: baseLoc.z + z };
            //    entity.dimension.setBlockPermutation(newPos,BlockPermutation.resolve('dirt'))


            try {
                let block = entity.dimension.getTopmostBlock({ x: newPos.x, z: newPos.z }, newPos.y);

                let nextLoc = block.location;
                nextLoc.y += 1;
                block = entity.dimension.getBlock(nextLoc)
                if (block.typeId == 'minecraft:fire') {

                    block.setPermutation(BlockPermutation.resolve('minecraft:air'));
                }

                else if (block.permutation.getState('growth') !== undefined && block.permutation.getState('growth') < 7) {
                    const chanceNum = RandInt(0, 15);

                    if (chanceNum == 3) block.setPermutation(block.permutation.withState('growth', block.permutation.getState('growth') + 1));

                }
            }
            catch { }
            yield
        }
    }
}

function RandInt(min, max) {
    const randAdd = Math.floor((max - min) * Math.random());
    return min + randAdd;
}

world.afterEvents.entityHurt.subscribe((event) => {
    try {
        if (event.hurtEntity.typeId == 'ecbl_bc:vulture') {
            AnimalUtils.VultureHit(event.hurtEntity)
        }
        else if (event.hurtEntity.typeId == 'ecbl_bc:owl') {
            event.hurtEntity.dimension.spawnParticle('plank_waao:owl_feather_particles', event.hurtEntity.location)
        }
        else if (event.damageSource.damagingEntity !== undefined && event.damageSource.damagingEntity.typeId == "ecbl_bc:snake") {
            event.hurtEntity.addEffect('minecraft:poison', 100)
        }
        else if (event.hurtEntity.typeId == 'ecbl_bc:squirrel' && event.hurtEntity.hasTag('nest_spawn')) {
            AnimalUtils.dropItems(event.hurtEntity)
            event.hurtEntity.triggerEvent("ecbl_bc:go_home");
        }
    } catch { }



})

system.runInterval(() => {
    AnimalUtils.ToucanEffect();
    const dimensions = [MinecraftDimensionTypes.overworld, MinecraftDimensionTypes.nether, MinecraftDimensionTypes.theEnd];
    dimensions.forEach((dimension) => {
        const elephants = world.getDimension(dimension).getEntities({ type: 'ecbl_bc:elephant' });
        elephants.forEach((elephant) => {
            if (elephant.hasComponent(EntityComponentTypes.IsSaddled) && elephant.getComponent(EntityComponentTypes.Rideable).getRiders().length >= 1) {
                elephant.addEffect('slowness', 1, { amplifier: 4, "showParticles": false })
            }
        })
    })
})

world.afterEvents.entityDie.subscribe((event) => {
    try {
        if (event.deadEntity.typeId == 'ecbl_bc:jaguar') {
            AnimalUtils.DeathAnimation(event.deadEntity, 'ecbl_bc:jaguar_death', [], ['ecbl_bc:variant'])
        }
        else if (event.deadEntity.typeId == "ecbl_bc:eagle") event.deadEntity.dimension.spawnParticle('plank_waao:eagle_died', event.deadEntity.location)
    }
    catch { }


})


