let $ResourceLocation = Java.loadClass('net.minecraft.resources.ResourceLocation');
let $ResourceKey = Java.loadClass("net.minecraft.resources.ResourceKey")
let DAMAGE_TYPE = $ResourceKey.createRegistryKey("damage_type")

let $ClipContext = Java.loadClass('net.minecraft.world.level.ClipContext')
let $ProjectileUtil = Java.loadClass('net.minecraft.world.entity.projectile.ProjectileUtil')

function getDamageSource(
    /** @type {Internal.Level}*/ level,
    /** @type {Internal.DamageType_}*/ damageType
    ) {
    let resourceKey = $ResourceKey.create(DAMAGE_TYPE, Utils.id(damageType))
    let holder = level.registryAccess().registryOrThrow(DAMAGE_TYPE).getHolderOrThrow(resourceKey)
    var damagesource = holder.get()
    return new DamageSource(damagesource)
}

function getDamageSourceBothSides(
    /** @type {Internal.Level}*/ level,
    /** @type {Internal.DamageType_}*/ damageType,
    /** @type {Internal.LivingEntity}*/ destEntity,
    /** @type {Internal.LivingEntity}*/ sourceEntity
    ) {
    var resourceKey = $ResourceKey.create(DAMAGE_TYPE, Utils.id(damageType))
    var holder = level.registryAccess().registryOrThrow(DAMAGE_TYPE).getHolderOrThrow(resourceKey)
    var damagesourceholder = holder
    return new DamageSource(damagesourceholder, sourceEntity, destEntity)
}

/**
 * 
 * @param {Internal.LivingEntity} entity 
 * @param {Internal.ServerLevel} level 
 * @param {number} distance
 * @returns {Object}
 * Returns the block and/or entity that the entity is looking at.
 * Ignores non-solid blocks and spectators.
 */
let advancedRayTrace = (entity, level, distance) => {
    let eyePos = entity.eyePosition;
    let viewVec = entity.getViewVector(1)
    let endPos = eyePos.add(viewVec.x() * distance, viewVec.y() * distance, viewVec.z() * distance)
    let aabb = AABB.of(eyePos.x(), eyePos.y(), eyePos.z(), endPos.x(), endPos.y(), endPos.z())

    let ray = $ProjectileUtil.getEntityHitResult(level, entity, eyePos, endPos, aabb, (e) => {
        return !e.isSpectator()
    }, 0)

    let clip = new $ClipContext(
        entity.getEyePosition(1), 
        entity.getEyePosition(1).add(entity.getLookAngle().scale(distance)), 
        'collider', 'none', 
        entity
    )
    let hit = level.clip(clip)
    if (ray == null) {
        return {
            block: hit.getBlockPos() ? level.getBlock(hit.getBlockPos()) : null,
            entity: null
        }
    }
    return {
        block: level.getBlock(hit.getBlockPos()),
        entity: ray.entity
    }
}

function resolveAllegedBooleanFromObject(thing) {
    if (thing.toString() == 'true') { return true; }
    if (thing.toString() == 'false') { return false; }
    return null
}

StartupEvents.registry('palladium:abilities', (event) => {
    
    event.create('arrzenhanced:raycast_damage')
    .icon(palladium.createItemIcon('minecraft:note_block'))
    .addProperty('distance', 'integer', 10, 'Max distance to raycast')
    .addProperty('damage', 'integer', 2, 'Damage to deal')
    .addProperty('runHexiosFunction', 'boolean', false, 'run a specific function for hexios')
    .tick((entity, entry, holder, enabled) => {
        if (enabled) {
            var dist = entry.getPropertyByName('distance');
            var damage = entry.getPropertyByName('damage');
            var runHexiosFunction = resolveAllegedBooleanFromObject(entry.getPropertyByName('runHexiosFunction'));
            var level = entity.getLevel()
            var victim = advancedRayTrace(entity, level, dist).entity
            if (!victim) return
            if (runHexiosFunction) {
                global.hexiosFunction(entity, victim)
            }

            let damageSource = getDamageSourceBothSides(level, "minecraft:player_attack", entity, victim)
            // victim.attack("minecraft:player_attack", damage)
            victim.attack(damageSource, damage)
        }
    });
});

global.hexiosFunction = (entity, victim) => {
    let EnergyBarReference = Java.loadClass('net.threetag.palladium.power.energybar.EnergyBarReference');
    let DAMAGE_BOOST_REF = new EnergyBarReference('arrzenhanced:soul_fire_magic', 'damage_boost');
    
    if (entity.persistentData['soulFireDamageCooldown'] > 0) {return}
    entity.persistentData['soulFireCooldown'] = 120;
    entity.persistentData['soulFireDamageCooldown'] = 10;

    var playerPowerBar = DAMAGE_BOOST_REF.getEntry(entity)
    if (playerPowerBar.get() == playerPowerBar.getMax()) {return}
    playerPowerBar.add(300)
}