function isLookingAtEntity(baseEntity, targetEntity) {
    var fovDegrees = 15;
    var basePos = baseEntity.eyePos();
    var lookDir = baseEntity.getLookVector().normalized();

    var targetFoot = targetEntity.pos();
    var targetEye = targetEntity.eyePos();

    var segDir = targetEye.subtract(targetFoot);
    var toBase = basePos.subtract(targetFoot);

    var t = toBase.dot(segDir) / segDir.dot(segDir);
    t = Math.max(0, Math.min(1, t));

    var closestPoint = targetFoot.add(segDir.multiply(t));
    var toClosest = closestPoint.subtract(basePos).normalized();

    var dot = lookDir.dot(toClosest);
    var clamped = Math.max(-1, Math.min(1, dot));
    var angle = Math.acos(clamped) * (180 / Math.PI);

    return angle < fovDegrees || isLookingAtTargetPos(baseEntity.eyePos(), baseEntity.getLookVector(), targetEntity.eyePos(), fovDegrees) || isLookingAtTargetPos(baseEntity.eyePos(), baseEntity.getLookVector(), targetEntity.pos(), fovDegrees);
}

function isLookingAtTargetPos(basePos, baseRot, targetPos, fov) {
    var directionToEntity = targetPos.subtract(basePos).normalized();
    var dotProduct = Math.max(-1, Math.min(1, baseRot.dot(directionToEntity)));

    var angle = Math.acos(dotProduct) * (180 / Math.PI);

    return Math.abs(angle) < fov;
}

function nearestEntityToPos(pos, others) {
    if (others.length === 0)
        return null;
    var nearest = others[0]
        var lowest = pos.distanceTo(nearest.pos());
    for (var i = 1; i < others.length; i++) {
        var distance = pos.distanceTo(others[i].pos());
        if (distance < lowest) {
            lowest = distance;
            nearest = others[i]
        }
    }
    return nearest;
}

var space_travel = implement("fan:external/space_travel");

function init(hero) {
    hero.setName("Lucifer Morningstar/King Of Hell");
    hero.setTier(10);

    hero.setHelmet("item.superhero_armor.piece.helmet");
    hero.setChestplate("item.superhero_armor.piece.chestpiece");
    hero.setLeggings("item.superhero_armor.piece.leggings");
    hero.setBoots("item.superhero_armor.piece.boots");
    hero.addPrimaryEquipment("fisktag:weapon{WeaponType:fan:apple}", true, item => item.nbt().getString("WeaponType") == 'fan:apple');
    hero.addPrimaryEquipment("fisktag:weapon{WeaponType:fan:duck}", true, item => item.nbt().getString("WeaponType") == 'fan:duck');

    hero.addAttribute("PUNCH_DAMAGE", 15, 0);
    hero.addAttribute("FALL_RESISTANCE", 1.0, 1);
    hero.addAttribute("IMPACT_DAMAGE", 10.0, 0);
    hero.addAttribute("SPRINT_SPEED", 0.5, 1);
    hero.addAttribute("MAX_HEALTH", 20, 0);
    hero.addAttribute("JUMP_HEIGHT", 3, 0);

    hero.setDefaultScale(entity => 1 + 0.3 * entity.getData("fan:dyn/devil_timer"));

    hero.addPowers("fan:lucifer_king_of_hell", "fan:lucifer_archangel_phys","fan:luci1","fan:extremis");

    hero.addKeyBind("WINGS", "Toggle Wings", 1);
    hero.addKeyBind("WINGS_VISUAL", "\u00A7mToggle Wings", 1);
    hero.addKeyBind("BLADE", "Fire Arms", 4);
    hero.addKeyBind("CHARGED_BEAM", "The fiery spit", 5);


    hero.addKeyBind("TELEPORT", "Teleport", 2);
    hero.addKeyBind("TELEPORT_VISUAL", "\u00A7mTeleport", 2);

    hero.addKeyBind("TELEKINESIS_AIM", "Telekinesis", 3);
    hero.addKeyBind("TELEKINESIS", "Telekinesis", 3);
    hero.addKeyBind("TELEKINESIS_VISUAL", "\u00A7mTelekinesis", 3);



    hero.setTickHandler((entity, manager) => {
        space_travel.tick(entity, manager);

        manager.incrementData(entity, "fan:dyn/holoanim", 20, 20, entity.is("DISPLAY") && !entity.as("DISPLAY").isStatic() && entity.as("DISPLAY").getDisplayType() === "HOLOGRAM");

        if (entity.isSneaking()) {
            entity.world().getEntitiesInRangeOf(entity.pos(), 30).forEach(entityGrabbed => {
                var grabbed = entity.world().getEntityById(entity.getData("fiskheroes:grab_id"));
                if (entityGrabbed.isLivingEntity() && !entityGrabbed.equals(entity) && entityGrabbed.equals(grabbed)) {
                    entityGrabbed.hurtByAttacker(hero, "FLAME", "%1$s was burned to death by %2$s.", 5, entity)
                }
            });
        }

        var isSurvival = entity.getWornChestplate().nbt().getBoolean("NeedsUnlock");
        if (entity.getData("fan:dyn/transform2")) {
            if (entity.getData("fan:dyn/transform2_timer") > 0.7 && !entity.getData("fan:dyn/ignite")) {
                var get = [];
                entity.world().getEntitiesInRangeOf(entity.eyePos(), 16).forEach(other => {
                    if (isLookingAtEntity(entity, other, 30) && entity.canSee(other) && other.isLivingEntity() && !entity.equals(other)) {
                        get.push(other);
                    }
                });
                var target = nearestEntityToPos(entity.pos(), get);
                if (target) {
                    target.hurtByAttacker(hero, "INCINERATION", "%s was Incinerated by %s", isSurvival ? 50 : 10000, entity);
                }
                entity.playSound("fan:main.snap", 1, 1);
                manager.setData(entity, "fan:dyn/ignite", true);
            }
            if (entity.getData("fan:dyn/transform2_timer") == 1) {
                manager.setData(entity, "fan:dyn/transform2", false);
                manager.setData(entity, "fan:dyn/ignite", false);
            }
        }
        if (entity.getData("fan:dyn/fire") && entity.getData("fan:dyn/fire_timer") == 1) {
            var hurt = (targett, damage) => {
                targett.hurtByAttacker(hero, damage, "%s Fell into the pits of desire by %s", 0.001, entity);

            };
            var get = [];
            entity.world().getEntitiesInRangeOf(entity.eyePos(), 10).forEach(other => {
                if (entity.canSee(other) && other.isLivingEntity() && !entity.equals(other)) {
                    if (entity.getData("fan:dyn/devil_timer") == 1) {
                        hurt(other, "DESIRE_DEVIL");
                    } else if (isLookingAtTargetPos(entity.eyePos(), entity.getLookVector(), other.eyePos(), 30) && isLookingAtTargetPos(other.eyePos(), other.getLookVector(), entity.eyePos(), 30)) {
                        get.push(other);
                    }

                }
            });
            var target = nearestEntityToPos(entity.pos(), get);
            if (target) {
                hurt(target, "DESIRE");
            }
            manager.setData(entity, "fan:dyn/desire_cooldown", 1);
            manager.setData(entity, "fan:dyn/fire", false);
        }

        manager.incrementData(entity, "fan:dyn/desire_cooldown", entity.getData("fan:dyn/devil_timer") == 1 ? 60 : 120, false);

        var t = entity.getData("fiskheroes:dyn/superhero_landing_ticks");

        if (t == 0 && !entity.isSprinting() && !entity.isOnGround() && entity.motionY() < -1.25 && entity.getData("fiskheroes:flight_boost_timer") > 0 && entity.world().blockAt(entity.pos().add(0, -2, 0)).isSolid()) {
            manager.setDataWithNotify(entity, "fiskheroes:dyn/superhero_landing_ticks", t = 12);
            entity.playSound("fiskheroes:suit.antimonitor.forcefield.deflect", 1, 1.15 - Math.random() * 0.3);
        } else if (t > 0) {
            manager.setData(entity, "fiskheroes:dyn/superhero_landing_ticks", --t);
        }

        manager.incrementData(entity, "fiskheroes:dyn/superhero_landing_timer", 2, 8, t > 0);
        manager.incrementData(entity, "fan:dyn/wings_flight_timer", 5, entity.getData("fiskheroes:flight_timer") > 0);

        if (entity.getData("fan:dyn/wings_flight_timer") > 0.5) {
            var baseSpeed = 0.025;
            var speed = baseSpeed + (0.05 - baseSpeed) * entity.getData("fiskheroes:flight_boost_timer");
            manager.setData(entity, "fan:dyn/wings_flight", entity.getData("fan:dyn/wings_flight") + speed);
        } else if (entity.getData("fan:dyn/wings_flight_timer") == 0 && entity.getData("fan:dyn/wings_flight") > 0) {
            manager.setData(entity, "fan:dyn/wings_flight", 0);
        }

        if (entity.getData("fiskheroes:dyn/superhero_landing_timer") > 0.8) {
            entity.world().getEntitiesInRangeOf(entity.pos(), 3).forEach(other => {
                if (entity.canSee(other) && other.isLivingEntity() && !entity.equals(other)) {
                    other.hurtByAttacker(hero, "LAND", "%s Got landed on by %s", 2.5, entity);
                }
            });
        }
    });
    hero.setKeyBindEnabled((entity, keyBind) => {
        var wings = entity.getData("fan:dyn/wings_timer") == 1;
        var devil = entity.getData("fan:dyn/devil_timer") == 1;
        if (entity.getData("fiskheroes:flying") && entity.isSprinting() && keyBind.indexOf("VISUAL") == -1) {
            return false;
        }
        var flying = entity.getData("fiskheroes:flying") && entity.isSprinting();
        switch (keyBind) {
        case "TELEPORT":
            return !entity.getData("fiskheroes:shield");
        case "TELEPORT_VISUAL":
            return entity.getData("fiskheroes:shield") || flying;
        case "CHARGED_BEAM":
        return entity.getData("fiskheroes:blade");

        case "WINGS":
            return (!wings || entity.isSneaking() || entity.getData("fan:dyn/devil_timer") > 0) && (entity.getData("fan:dyn/devil_timer") == 0 || devil);
        case "WINGS_VISUAL":
            return (!wings || entity.isSneaking() || entity.getData("fan:dyn/devil_timer") > 0) && (entity.getData("fan:dyn/devil_timer") == 0 || devil) && flying;

        case "SHIELD":
            return wings && !entity.isSneaking() && entity.getData("fan:dyn/devil_timer") == 0;
        case "SHIELD_VISUAL":
            return wings && !entity.isSneaking() && entity.getData("fan:dyn/devil_timer") == 0 && flying;

        case "DEVIL":
            return (entity.getData("fan:dyn/devil_timer") == 0 || devil) && entity.getData("fan:dyn/wings_timer") == 0;
        case "DEVIL_VISUAL":
            return flying || entity.getData("fan:dyn/devil_timer") > 0 && entity.getData("fan:dyn/devil_timer") < 1 || entity.getData("fan:dyn/wings_timer") > 0;

        case "TELEKINESIS_AIM":
            return entity.getData("fiskheroes:grab_id") > -1 && !devil;
        case "TELEKINESIS":
            return entity.getHeldItem().isEmpty() && !devil && !entity.getData("fiskheroes:shield");
        case "TELEKINESIS_VISUAL":
            return (!entity.getHeldItem().isEmpty() || entity.getData("fiskheroes:shield") || flying) && !devil;

        case "INCINERATION":
            return devil && !entity.getData("fan:dyn/fire") && entity.getData("fan:dyn/transform2_timer") == 0 && entity.getHeldItem().isEmpty();
        case "INCINERATION_VISUAL":
            return devil && (flying || entity.getData("fan:dyn/transform2_timer") > 0 || !entity.getHeldItem().isEmpty() || entity.getData("fan:dyn/fire"));

        case "DESIRE":
            return !entity.getData("fan:dyn/transform2") && entity.getData("fiskheroes:grab_id") == -1 && entity.getData("fan:dyn/fire_timer") == 0 && entity.getData("fan:dyn/desire_cooldown") == 0;
        case "DESIRE_VISUAL":
            return flying || entity.getData("fiskheroes:grab_id") > -1 || entity.getData("fan:dyn/transform2") || entity.getData("fan:dyn/fire_timer") > 0 || entity.getData("fan:dyn/desire_cooldown") > 0;
        default:
            return true;
        }
    });

    hero.setModifierEnabled((entity, modifier) => {
        switch (modifier.name()) {
        case "fiskheroes:controlled_flight":
            return entity.getData("fan:dyn/wings_timer") == 1 && entity.getData("fiskheroes:shield_timer") == 0;
        case "fiskheroes:shield":
            return entity.getData("fan:dyn/wings_timer") == 1;
        default:
            return true;
        }
    });

    hero.addDamageProfile("INCINERATION", {
        "types": {
            "COSMIC": 1.0,
            "MAGIC": 0.5,
            "EXPLOSION": 0.5
        },
        "properties": {
            "COOK_ENTITY": true,
            "IGNITE": 100,
            "HEAT_TRANSFER": 200
        }
    });

    hero.addDamageProfile("LAND", {
        "types": {
            "BLUNT": 0.5,
            "EXPLOSION": 0.5
        }
    });

    hero.addDamageProfile("FLAME_PUNCH", {
        "types": {
            "BLUNT": 1.0,
            "FIRE": 0.4
        },
        "properties": {
            "HEAT_TRANSFER": 40,
            "IGNITE": 2
        }
    });

    hero.addDamageProfile("FLAME", {
        "types": {
            "FIRE": 2.0
        },
        "properties": {
            "COOK_ENTITY": true,
            "IGNITE": 50,
            "HEAT_TRANSFER": 100
        }
    });

    hero.addDamageProfile("DESIRE", {
        "types": {
            "COSMIC": 1.0,
            "MAGIC": 0.5,
        },
        "properties": {
            "REDUCE_KNOCKBACK": 1,
            "EFFECTS": [{
                    "id": "minecraft:slowness",
                    "duration": 1200,
                    "amplifier": 5,
                }, {
                    "id": "minecraft:nausea",
                    "duration": 800,
                    "amplifier": 1,
                }, {
                    "id": "fiskheroes:tutridium",
                    "duration": 400,
                    "amplifier": 1,
                }
            ]
        }
    });

    hero.addDamageProfile("DESIRE_DEVIL", {
        "types": {
            "COSMIC": 1.0,
            "MAGIC": 0.5,
        },
        "properties": {
            "REDUCE_KNOCKBACK": 1,
            "EFFECTS": [{
                    "id": "minecraft:slowness",
                    "duration": 600,
                    "amplifier": 5,
                }, {
                    "id": "minecraft:nausea",
                    "duration": 400,
                    "amplifier": 1,
                }, {
                    "id": "fiskheroes:tutridium",
                    "duration": 150,
                    "amplifier": 1,
                }
            ]
        }
    });

    hero.addAttributeProfile("DEVIL", devilProfile);
    hero.setAttributeProfile(getAttributeProfile)
    hero.setDamageProfile(entity => null);

    hero.setHasProperty((entity, property) => property == "BREATHE_SPACE" || property == "MASK_TOGGLE" && entity.getData("fan:dyn/devil_timer") < 1);

}

function devilProfile(profile) {
    profile.inheritDefaults();
    profile.addAttribute("PUNCH_DAMAGE", 18.5, 0);
    profile.addAttribute("IMPACT_DAMAGE", 15.0, 0);
    profile.addAttribute("SPRINT_SPEED", 1, 1);
    profile.addAttribute("JUMP_HEIGHT", 5, 0);
}

function getAttributeProfile(entity) {
    return entity.getData("fan:dyn/devil_timer") == 1 ? "DEVIL" : null;
}
