import * as mc from '@minecraft/server';
import * as lib from '../lib/lib';
import * as config from '../pb_scraft_config';

import { deleteProjectile, registerProjectile } from '../lib/projectile_tracker';
import { SPELL_PLAYERS } from '../pb_scraft_player';


export default class SpellHoldRaycast {

    player;
    cooldown;
    name;
    manacost;
    castTime;
    soundCast;
    particlesCast;
    projectileData;

    timeIndex = 0;
    isCharged = false;
    isActive = true;
    itemUse;

    totalTime;;
    glyphPower = 0;

    constructor(player, spellData, itemUse) {
        this.player = player;
        this.itemUse = itemUse;

        this.name = spellData.identifier;
        this.manacost = spellData.manacost;;
        this.castTime = spellData.castTime;;
        this.cooldown = spellData.cooldown;
        this.soundCast = spellData.soundCast;
        this.particlesCast = spellData.particlesCast;
        this.projectileData = config.EFFECT_LIST.find((f) => f.identifier == spellData.projectile);

        this.totalTime = this.castTime + this.cooldown;

        this.processItem(itemUse, spellData);
    }

    processItem(itemUse, spelldata)
    {
        const glyph_1 = itemUse.getDynamicProperty("pb_scraft_glyph_1");
        const glyph_2 = itemUse.getDynamicProperty("pb_scraft_glyph_2");

        let glyphMana = 1.0;
        let glyphCastTime = 1.0;

        if(glyph_1 != undefined)
        {
            if(glyph_1 == "pb_scraft:glyph_accelerate") glyphCastTime = glyphCastTime - 0.3;
            if(glyph_1 == "pb_scraft:glyph_alleviate") glyphMana = glyphMana - 0.2;
            if(glyph_1 == "pb_scraft:glyph_intensify") this.glyphPower = this.glyphPower + 10;
        }

        if(glyph_2 != undefined)
        {
            if(glyph_2 == "pb_scraft:glyph_accelerate") glyphCastTime = glyphCastTime - 0.3;
            if(glyph_2 == "pb_scraft:glyph_alleviate") glyphMana = glyphMana - 0.2;
            if(glyph_2 == "pb_scraft:glyph_intensify") this.glyphPower = this.glyphPower + 10;
        }

        const itemdata = config.CAST_ITEM_LIST.find((f) => f.identifier == itemUse.typeId);
        if(itemdata.element == spelldata.element) this.glyphPower += itemdata.spellPower;

        this.manacost = Math.floor(this.manacost * glyphMana);
        this.castTime = this.castTime * glyphCastTime;
    }

    cast() {
        this.player.playSound(this.soundCast, this.player.location);
    }

    stop() {
        this.isActive = false;
        lib.setCooldown(this.player, 0);
    }

    release() {
        this.trigger();
        this.isActive = false;
    }

    trigger() {
        lib.runCooldown(this.player);   //casting staff cooldown here
        const dimension = this.player.dimension;
        const view_vector = this.player.getViewDirection();
        const location = this.player.getHeadLocation();
        const impulse = this.projectileData.impulse;

        const range = this.projectileData.range;
        const direction_multiplier = this.projectileData.direction_multiplier;
        
        let distance = range;
        let entity_hit;

        SPELL_PLAYERS.get(this.player.id).consumeMana(this.manacost);

        //check for first impact on block
        let block_raycast_hit = dimension.getBlockFromRay(location, view_vector, { includeLiquidBlocks: false, includePassableBlocks: true, maxDistance: range });
        if (block_raycast_hit != undefined) distance = Math.round(lib.getDistance(location, block_raycast_hit.block.location)) - 1;

        //check for first impact on entity
        let entity_raycast_hit = dimension.getEntitiesFromRay(location, view_vector, { families: ["mob"], excludeFamilies: ["projectile", "prop", "npc", "spell", "inanimate"], maxDistance: range });
        entity_raycast_hit = entity_raycast_hit.concat(dimension.getEntitiesFromRay(location, view_vector, { families: ["monster"], excludeFamilies: ["projectile", "prop", "npc", "spell", "inanimate"], maxDistance: range }));
        if (entity_raycast_hit.length > 0) {
            const distance_entity = entity_raycast_hit[0].distance;
            entity_hit = entity_raycast_hit[0].entity;
            if (distance > distance_entity) distance = distance_entity;
        }
        //check for first impact on player
        if (config.PVP.enabled) {
            let entity_raycast_hit_pvp = dimension.getEntitiesFromRay(location, view_vector, { families: ["player"], maxDistance: range });
            if (entity_raycast_hit_pvp.length > 0) {
                if (entity_raycast_hit_pvp[0].entity != this.player) {
                    const distance_entity = entity_raycast_hit_pvp[0].distance;
                    entity_hit = entity_raycast_hit_pvp[0].entity;
                    if (distance > distance_entity) distance = distance_entity;
                }
            }
        }
        const target_location = { x: (location.x + view_vector.x * distance), y: (location.y + view_vector.y * distance), z: (location.z + view_vector.z * distance) };
        if(lib.locationIsValid(dimension, target_location) == false) return;

        if (this.particlesCast != undefined)
        {
            let mvm = new mc.MolangVariableMap();
            mvm.setFloat(`variable.direction`, -Math.round(this.player.getRotation().y));
            mvm.setFloat(`variable.direction_y`, -this.player.getRotation().x * Math.PI / 180);

            this.particlesCast.forEach(element => { mc.world.getDimension(dimension.id).spawnParticle(element, location, mvm); });
        }

        const projectile = mc.world.getDimension(dimension.id).spawnEntity(this.projectileData.identifier, { x: target_location.x + view_vector.x * 2, y: target_location.y + 0.75, z: target_location.z + view_vector.z * 2 });
        projectile.setDynamicProperty("pb_scraft:glyph_power", this.glyphPower);
        const component = projectile.getComponent("minecraft:tameable");
        component.tame(this.player);

        if (config.PVP.enabled) projectile.triggerEvent("evt:target_pvp");
        else projectile.triggerEvent("evt:target_pve");

        projectile.addEffect('slow_falling', 10 * mc.TicksPerSecond, { amplifier: 1, showParticles: false });
        projectile.applyImpulse({ x: view_vector.x * direction_multiplier.x * impulse, y: view_vector.y * direction_multiplier.y * impulse, z: view_vector.z * direction_multiplier.z * impulse });
        dimension.playSound(this.projectileData.soundLaunch, location);
    }

    tick() {
        if (this.isActive) {
            lib.runCooldown(this.player);
            if (this.timeIndex % this.castTime == 0) this.cast();
            if (this.timeIndex >= this.castTime) this.isCharged = true;
            this.timeIndex++;
        }
    }
}

export function damagetick(data, projectileData) {

    const dimension = data.dimension.id;
    const location = data.location;
    const projectile = data.source;

    const tame_component = projectile.getComponent("minecraft:tameable");
    const caster = tame_component.tamedToPlayer;

    let damage = projectileData.damage;
    const glyphPower = projectile.getDynamicProperty("pb_scraft:glyph_power");
    if (caster != undefined) damage = damage + projectileData.spellpower * (SPELL_PLAYERS.get(caster.id).spellPower) + projectileData.spellpower * glyphPower;
    damage = damage * config.SCALING;

    try {
        for (let entity of lib.targetMobMonster(location, dimension, projectileData.radius)) {
            entity.applyDamage(damage, { cause: mc.EntityDamageCause.contact, damagingEntity: caster });
            entity.applyKnockback(entity.location.x - location.x, entity.location.z - location.z, projectileData.knockback_horizontal, projectileData.knockback_vertical);
            if(projectileData.effect != undefined) entity.addEffect(projectileData.effect.type, projectileData.effect.duration, projectileData.effect.options);
        }

        if (config.PVP.enabled) {
            const pvp_damage = damage * config.PVP.scaling;
            for (let entity of mc.world.getDimension(dimension).getEntities({ location: location, families: ["player"], maxDistance: projectileData.radius })) {
                if (entity != caster) {
                    entity.applyDamage(pvp_damage, { cause: mc.EntityDamageCause.contact, damagingEntity: caster });
                    entity.applyKnockback(entity.location.x - location.x, entity.location.z - location.z, projectileData.knockback_horizontal, projectileData.knockback_vertical);
                    if(projectileData.effect != undefined) entity.addEffect(projectileData.effect.type, projectileData.effect.duration, projectileData.effect.options);
                }
            }
        }
    }
    catch (error) { }
}
