import {PlayerCharacterDataModel} from "../data/character.mjs";
import {ActionManager} from "./actions/action-manager.mjs";
import {LiturgyData} from "../data/miracle/liturgydata.mjs";
import {ModifyLiturgy} from "../dialog/modify-liturgy.mjs";

export class CharacterSheet extends ActorSheet {
    /**@override */
    static get defaultOptions() {
        return foundry.utils.mergeObject(super.defaultOptions, {
            classes: ['dsa41', 'sheet', 'actor', 'character'],
            width: 1100,
            height: 480,
            tabs: [
                {
                    navSelector: '.sheet-tabs',
                    contentSelector: '.sheet-body',
                    initial: 'description',
                },
            ],
        });
    }

    /** @override */
    get template() {
        return `systems/DSA_4-1/templates/actor/actor-character-sheet.hbs`;
    }

    static onDroppedData(actor, characterSheet, data) {
        const uuid = foundry.utils.parseUuid(data.uuid);
        const collection = uuid.collection.index ?? uuid.collection;
        const document = CharacterSheet.getElementByName(collection, uuid.id);
        const {
            name,
            type
        } = document
        console.log(name, type)
        switch (type) {
            case "Skill":
                return characterSheet.#handleDroppedSkill(actor, document); // on false cancel this whole operation
            case "Advantage":
                return characterSheet.#handleDroppedAdvantage(actor, document);
            case "ActiveEffect":
                return characterSheet.#handleDroppedActiveEffect(actor, document);
            case "Equipment":
                return characterSheet.#handleDroppedEquipment(actor, document);
            case "Liturgy":
                return characterSheet.#handleDroppedLiturgy(actor, document);
            case "SpecialAbility":
                return characterSheet.#handleDroppedSpecialAbility(actor, document);
            default:
                return false;
        }

    }

    static getElementByName(collection, id) {
        const array = Array.from(collection);
        for (const element of array) {
            if (element._id === id) {
                return element;
            }
        }
    }

    #addSkillsToContext(context) {
        const actorData = context.data;
        context.skills = {};
        context.flatSkills = [];

        Object.values(actorData.items).forEach((item, index) => {
                if (item.type === "Skill") {

                    const talentGruppe = item.system.gruppe;
                    const eigenschaften = Object.values(item.system.probe);
                    const werte = [
                        {name: eigenschaften[0], value: this.prepareEigenschaftRoll(actorData, eigenschaften[0])},
                        {name: eigenschaften[1], value: this.prepareEigenschaftRoll(actorData, eigenschaften[1])},
                        {name: eigenschaften[2], value: this.prepareEigenschaftRoll(actorData, eigenschaften[2])}
                    ]
                    if (context.skills[talentGruppe] == null) {
                        context.skills[talentGruppe] = [];
                    }
                    const obj = {
                        type: "talent",
                        gruppe: talentGruppe,
                        name: item.name,
                        taw: "" + item.system.taw,
                        tawPath: `system.items.${index}.taw`,
                        werte,
                        rollEigenschaft1: werte[0].value,
                        rollEigenschaft2: werte[1].value,
                        rollEigenschaft3: werte[2].value,
                        eigenschaft1: werte[0].name,
                        eigenschaft2: werte[1].name,
                        eigenschaft3: werte[2].name,
                        probe: `(${eigenschaften.join("/")})`,
                        at: item.system.at,
                        pa: item.system.pa,
                        id: item._id,
                    };
                    context.skills[talentGruppe].push(obj);
                    context.flatSkills.push(obj);
                }
            }
        );
    }

    #cleanUpMerkmal(merkmale) {
        return merkmale.split(",").map((merkmal) => merkmal.trim())
    }

    /** @override */
    async getData() {
        const context = super.getData();


        // Use a safe clone of the actor data for further operations.
        const actorData = context.data;

        // Add the actor's data to context.data for easier access, as well as flags.
        context.system = actorData.system;
        context.flags = actorData.flags;
        context.derived = context.document.system;
        context.originalName = actorData.name;
        context.name = context.derived.name ?? actorData.name;
        context.effects = actorData.effects ?? [];

        this.#addSkillsToContext(context)
        this.#addAdvantagesToContext(context)
        this.#addSpecialAbilitiesToContext(context)
        await this.#addAttributesToContext(context)
        this.#addEquipmentsToContext(context)
        await this.#addCombatStatistics(context)
        this.#addActionsToContext(context)
        this.#addSpellsToContext(context)
        this.#addLiturgiesToContext(context)
        return context;
    }

    #addSpellsToContext(context) {
        const actorData = context.data;
        context.spells = [];
        Object.values(actorData.items).forEach((item, index) => {
            if (item.type === "Spell") {
                const eigenschaften = item.system.probe;
                const werte = [
                    {name: eigenschaften[0], value: this.prepareEigenschaftRoll(actorData, eigenschaften[0])},
                    {name: eigenschaften[1], value: this.prepareEigenschaftRoll(actorData, eigenschaften[1])},
                    {name: eigenschaften[2], value: this.prepareEigenschaftRoll(actorData, eigenschaften[2])}
                ]
                context.spells.push({
                    id: item._id,
                    name: item.name,
                    zfw: item.system.zfw,
                    hauszauber: item.system.hauszauber,
                    merkmal: this.#cleanUpMerkmal(item.system.merkmal),
                    rollEigenschaft1: werte[0].value,
                    rollEigenschaft2: werte[1].value,
                    rollEigenschaft3: werte[2].value,
                    eigenschaft1: werte[0].name,
                    eigenschaft2: werte[1].name,
                    eigenschaft3: werte[2].name,
                })
            }
        })
        context.hasSpells = context.spells.length > 0;
    }

    async #getModsOfAttribute(keyPath) {
        let returnValue = [];
        Array.from(this.object.appliedEffects).forEach(
            (e) =>
                e.changes.filter(c => c.key === keyPath).forEach(change => {
                    returnValue.push({
                        name: e.name,
                        value: change.value > 0 ? "+" + change.value : "" + change.value,
                        icon: e.icon,
                    })
                }))
        return returnValue;
    }

    async #addAttributesToContext(context) {
        context.mods = {
            "mu": await this.#getModsOfAttribute('system.attribute.mu.mod'),
            "kl": await this.#getModsOfAttribute('system.attribute.kl.mod'),
            "in": await this.#getModsOfAttribute('system.attribute.in.mod'),
            "ch": await this.#getModsOfAttribute('system.attribute.ch.mod'),
            "ff": await this.#getModsOfAttribute('system.attribute.ff.mod'),
            "ge": await this.#getModsOfAttribute('system.attribute.ge.mod'),
            "ko": await this.#getModsOfAttribute('system.attribute.ko.mod'),
            "kk": await this.#getModsOfAttribute('system.attribute.kk.mod')
        }
        context.attributes = [
            {
                eigenschaft: "mu",
                name: "MU",
                tooltip: "Mut",
                wert: (context.derived.attribute.mu.aktuell + context.derived.attribute.mu.mod) ?? 0,
            },
            {
                eigenschaft: "kl",
                name: "KL",
                tooltip: "Klugheit",
                wert: (context.derived.attribute.kl.aktuell + context.derived.attribute.kl.mod) ?? 0,
            },
            {
                eigenschaft: "in",
                name: "IN",
                tooltip: "Intuition",
                wert: (context.derived.attribute.in.aktuell + context.derived.attribute.in.mod) ?? 0,
            },
            {
                eigenschaft: "ch",
                name: "CH",
                tooltip: "Charisma",
                wert: (context.derived.attribute.ch.aktuell + context.derived.attribute.ch.mod) ?? 0,
            },
            {
                eigenschaft: "ff",
                name: "FF",
                tooltip: "Fingerfertigkeit",
                wert: (context.derived.attribute.ff.aktuell + context.derived.attribute.ff.mod) ?? 0,
            },
            {
                eigenschaft: "ge",
                name: "GE",
                tooltip: "Geschicklichkeit",
                wert: (context.derived.attribute.ge.aktuell + context.derived.attribute.ge.mod) ?? 0,
            },
            {
                eigenschaft: "ko",
                name: "KO",
                tooltip: "Konstitution",
                wert: (context.derived.attribute.ko.aktuell + context.derived.attribute.ko.mod) ?? 0,
            },
            {
                eigenschaft: "kk",
                name: "KK",
                tooltip: "Körperkraft",
                wert: (context.derived.attribute.kk.aktuell + context.derived.attribute.kk.mod) ?? 0,
            },

        ];

    }

    #addAdvantagesToContext(context) {
        context.advantages = [];
        const actorData = context.data;
        Object.values(actorData.items).forEach((item) => {
                if (item.type === "Advantage") {
                    context.advantages.push({
                        id: item._id,
                        name: item.name,
                        value: item.system.value,
                        options: item.system.auswahl,
                        description: item.system.description,
                    });
                }
            }
        );
    }

    #addSpecialAbilitiesToContext(context) {
        context.specialAbilities = [];
        const actorData = context.data;
        Object.values(actorData.items).forEach((item) => {
                if (item.type === "SpecialAbility") {
                    context.specialAbilities.push({
                        id: item._id,
                        name: item.name,
                    });
                }
            }
        );
    }

    #findEquipmentOnSlot(slot, setNumber) {
        return this.object.items.get(this.object.system.heldenausruestung[setNumber]?.[slot])
    }

    #addActionsToContext(context) {
        const am = new ActionManager(this.object)
        context.actions = am.evaluate()
    }

    #isWorn(itemId) {

        const slots = PlayerCharacterDataModel.getSlots()
        const set = this.object.system.heldenausruestung[this.object.system.setEquipped]
        if (set) {
            for (const slot of slots) {
                const equipmentSlotId = set[slot]
                if (equipmentSlotId === itemId) {
                    return slot
                }
            }
        }

        return false
    }

    async #addCombatStatistics(context) {
        const actorData = context.data;

        context.inidice = actorData.system.ini.wuerfel;
        context.inivalue = actorData.system.ini.aktuell;
        context.inimod = actorData.system.ini.mod;

        context.aupper = Math.min((context.actor.system.aup.aktuell / context.actor.system.aup.max) * 100, 100);
        context.lepper = Math.min((context.actor.system.lep.aktuell / context.actor.system.lep.max) * 100, 100);
        context.keper = Math.min((context.actor.system.kap.aktuell / context.actor.system.kap.max) * 100, 100);
        context.aspper = Math.min((context.actor.system.asp.aktuell / context.actor.system.asp.max) * 100, 100);
        context.lepcurrent = context.actor.system.lep.aktuell ?? 0

        context.aupcurrent = context.actor.system.aup.aktuell ?? 0

        const fernkampf = this.#findEquipmentOnSlot("fernkampf", context.actor.system.setEquipped)
        const links = this.#findEquipmentOnSlot("links", context.actor.system.setEquipped)
        const rechts = this.#findEquipmentOnSlot("rechts", context.actor.system.setEquipped)
        context.attacks = [];

        if (fernkampf) {
            const fkitems = fernkampf.system.rangedSkills.map(async (skillInQuestion) => await this.object.items.getName(skillInQuestion))
            fkitems.forEach(async skill => {
                const obj = await skill
                console.log(this.object.system.fk, obj.system.at);
                context.attacks.push({
                    name: obj.name,
                    using: fernkampf.name,
                    atroll: `1d20 + ${this.object.system.fk + obj.system.at}`,
                    at: `1w20 + ${this.object.system.fk + obj.system.at}`,
                    iniroll: `(${context.inidice})d6 + ${context.inivalue + fernkampf.system.iniModifier ?? 0}`,
                    ini: `${context.inidice}w6 + ${context.inivalue + fernkampf.system.iniModifier ?? 0}`,
                })
            })
        }
        if (links) {
            const meitems = links.system.meleeSkills.map(async (skillInQuestion) => await this.object.items.getName(skillInQuestion))
            meitems.forEach(async skill => {
                const obj = await skill
                context.attacks.push({
                    name: obj.name,
                    using: links.name,
                    atroll: `1d20 + ${this.object.system.at + obj.system.at + links.system.attackModifier}`,
                    at: `1w20 + ${this.object.system.at + obj.system.at + links.system.attackModifier}`,
                    paroll: `1d20 + ${this.object.system.pa + obj.system.pa + links.system.parryModifier}`,
                    pa: `1w20 + ${this.object.system.pa + obj.system.pa + links.system.parryModifier}`,
                    iniroll: `(${context.inidice})d6 + ${context.inivalue + links.system.iniModifier ?? 0}`,
                    ini: `${context.inidice}w6 + ${context.inivalue + links.system.iniModifier ?? 0}`,
                })
            })
        }
        if (rechts) {
            const meitems = rechts.system.meleeSkills.map(async (skillInQuestion) => await this.object.items.getName(skillInQuestion))
            meitems.forEach(async skill => {
                const obj = await skill
                console.log(this.object.system.at)
                context.attacks.push({
                    name: obj.name,
                    using: rechts.name,
                    atroll: `1d20 + ${this.object.system.at + obj.system.at + rechts.system.attackModifier}`,
                    at: `1w20 + ${this.object.system.at + obj.system.at + rechts.system.attackModifier}`,
                    paroll: `1d20 + ${this.object.system.pa + obj.system.pa + rechts.system.parryModifier}`,
                    pa: `1w20 + ${this.object.system.pa + obj.system.pa + rechts.system.parryModifier}`,
                    iniroll: `(${context.inidice})d6 + ${context.inivalue + rechts.system.iniModifier ?? 0}`,
                    ini: `${context.inidice}w6 + ${context.inivalue + rechts.system.iniModifier ?? 0}`,
                })
            })
        }

        // add weapons to sidebar
    }

    prepareEigenschaftRoll(actorData, name) {
        if (name && name !== "*") {
            return actorData.system.attribute[name.toLowerCase()].aktuell
        } else {
            return 0
        }
    }

    #addEquipmentsToContext(context) {
        context.equipments = [];
        const actorData = context.data;
        context.carryingweight = 0;
        Object.values(actorData.items).forEach((item, index) => {
            if (item.type === "Equipment") {

                // worn items are halved weight

                let effectiveWeight = item.system.weight ?? 0
                if (this.#isWorn(item._id)) {
                    effectiveWeight = item.system.weight ? item.system.weight / 2 : 0
                }

                context.equipments.push({
                    index: index,
                    id: item._id,
                    quantity: item.system.quantity,
                    name: item.name,
                    icon: item.img ?? "",
                    weight: item.system.weight,
                    worn: this.#isWorn(item._id)
                })
                context.carryingweight += item.system.quantity * effectiveWeight;
            }
        })
        context.maxcarryingcapacity = actorData.system.attribute.kk.aktuell
        context.carryingpercentage = Math.min((context.carryingweight / context.maxcarryingcapacity) * 100, 100);

        const maxSets = 3
        const romanNumerals = ["I", "II", "III", "IV", "V", "VI", "VII", "VIII", "IX", "X"]
        context.sets = []
        for (let setIndex = 0; setIndex < maxSets; setIndex++) {

            context.sets.push({
                tab: "set" + (setIndex + 1),
                name: romanNumerals[setIndex],
                index: setIndex,
                slots: [
                    {
                        target: "links",
                        id: this.object.system.heldenausruestung[setIndex]?.links,
                        name: this.object.items.get(actorData.system.heldenausruestung[setIndex]?.links)?.name,
                        icon: this.object.items.get(actorData.system.heldenausruestung[setIndex]?.links)?.img
                    },
                    {
                        target: "rechts",
                        id: this.object.system.heldenausruestung[setIndex]?.rechts,
                        name: this.object.items.get(actorData.system.heldenausruestung[setIndex]?.rechts)?.name,
                        icon: this.object.items.get(actorData.system.heldenausruestung[setIndex]?.rechts)?.img
                    },
                    {
                        target: "brust",
                        id: this.object.system.heldenausruestung[setIndex]?.brust,
                        name: this.object.items.get(actorData.system.heldenausruestung[setIndex]?.brust)?.name,
                        icon: this.object.items.get(actorData.system.heldenausruestung[setIndex]?.brust)?.img
                    },
                    {
                        target: "ruecken",
                        id: this.object.system.heldenausruestung[setIndex]?.ruecken,
                        name: this.object.items.get(actorData.system.heldenausruestung[setIndex]?.ruecken)?.name,
                        icon: this.object.items.get(actorData.system.heldenausruestung[setIndex]?.ruecken)?.img
                    },
                    {
                        target: "kopf",
                        id: this.object.system.heldenausruestung[setIndex]?.kopf,
                        name: this.object.items.get(actorData.system.heldenausruestung[setIndex]?.kopf)?.name,
                        icon: this.object.items.get(actorData.system.heldenausruestung[setIndex]?.kopf)?.img
                    },
                    {
                        target: "fernkampf",
                        id: this.object.system.heldenausruestung[setIndex]?.fernkampf,
                        name: this.object.items.get(actorData.system.heldenausruestung[setIndex]?.fernkampf)?.name,
                        icon: this.object.items.get(actorData.system.heldenausruestung[setIndex]?.fernkampf)?.img
                    },
                    {
                        target: "munition",
                        id: this.object.system.heldenausruestung[setIndex]?.munition,
                        name: this.object.items.get(actorData.system.heldenausruestung[setIndex]?.munition)?.name,
                        icon: this.object.items.get(actorData.system.heldenausruestung[setIndex]?.munition)?.img
                    },
                    {
                        target: "armlinks",
                        id: this.object.system.heldenausruestung[setIndex]?.armlinks,
                        name: this.object.items.get(actorData.system.heldenausruestung[setIndex]?.armlinks)?.name,
                        icon: this.object.items.get(actorData.system.heldenausruestung[setIndex]?.armlinks)?.img
                    },
                    {
                        target: "armrechts",
                        id: this.object.system.heldenausruestung[setIndex]?.armrechts,
                        name: this.object.items.get(actorData.system.heldenausruestung[setIndex]?.armrechts)?.name,
                        icon: this.object.items.get(actorData.system.heldenausruestung[setIndex]?.armrechts)?.img
                    },
                    {
                        target: "bauch",
                        id: this.object.system.heldenausruestung[setIndex]?.bauch,
                        name: this.object.items.get(actorData.system.heldenausruestung[setIndex]?.bauch)?.name,
                        icon: this.object.items.get(actorData.system.heldenausruestung[setIndex]?.bauch)?.img
                    },
                    {
                        target: "beinlinks",
                        id: this.object.system.heldenausruestung[setIndex]?.beinlinks,
                        name: this.object.items.get(actorData.system.heldenausruestung[setIndex]?.beinlinks)?.name,
                        icon: this.object.items.get(actorData.system.heldenausruestung[setIndex]?.beinlinks)?.img
                    },
                    {
                        target: "beinrechts",
                        id: this.object.system.heldenausruestung[setIndex]?.beinrechts,
                        name: this.object.items.get(actorData.system.heldenausruestung[setIndex]?.beinrechts)?.name,
                        icon: this.object.items.get(actorData.system.heldenausruestung[setIndex]?.beinrechts)?.img
                    }
                ]
            })
        }
    }

    async _onTalentRoll(event) {
        event.preventDefault();
        const dataset = event.currentTarget.dataset;
        console.log(dataset)
        if (dataset.rolleigenschaft1) {
            let roll1 = new Roll("3d20", this.actor.getRollData());

            let evaluated1 = (await roll1.evaluate())

            const dsaDieRollEvaluated = this._evaluateRoll(evaluated1.terms[0].results, {
                taw: dataset.taw,
                werte: [dataset.rolleigenschaft1, dataset.rolleigenschaft2, dataset.rolleigenschaft3],
            })

            if (dsaDieRollEvaluated.tap >= 0) { // erfolg
                evaluated1.toMessage({
                    speaker: ChatMessage.getSpeaker({actor: this.actor}),
                    flavor: ` ${dsaDieRollEvaluated.meisterlich ? 'Meisterlich geschafft' : 'Geschafft'} mit ${dsaDieRollEvaluated.tap} Punkten übrig`,
                    rollMode: game.settings.get('core', 'rollMode'),
                })
            } else { // misserfolg
                evaluated1.toMessage({
                    speaker: ChatMessage.getSpeaker({actor: this.actor}),
                    flavor: ` ${dsaDieRollEvaluated.meisterlich ? 'Gepatzt' : ''} mit ${Math.abs(dsaDieRollEvaluated.tap)} Punkten daneben`,
                    rollMode: game.settings.get('core', 'rollMode'),
                })
            }


        }
    }

    _evaluateRoll(rolledDice, {
        taw,
        lowerThreshold = 1,
        upperThreshold = 20,
        countToMeisterlich = 3,
        countToPatzer = 3,
        werte = []
    }) {
        let tap = taw;
        let meisterlichCounter = 0;
        let patzerCounter = 0;
        let failCounter = 0;

        rolledDice.forEach((rolledDie, index) => {
            if (tap < 0 && rolledDie.result > werte[index]) {
                tap -= rolledDie.result - werte[index];
                if (tap < 0) { // konnte nicht vollständig ausgeglichen werden
                    failCounter++;
                }
            } else if (rolledDie.result > werte[index]) { // taw ist bereits aufgebraucht und wert kann nicht ausgeglichen werden
                tap -= rolledDie.result - werte[index];
                failCounter++;
            }
            if (rolledDie.result <= lowerThreshold) meisterlichCounter++;
            if (rolledDie.result > upperThreshold) patzerCounter++;
        })

        return {
            tap,
            meisterlich: meisterlichCounter === countToMeisterlich,
            patzer: patzerCounter === countToPatzer,
        }
    }

    _onAttributeRoll(event) {
        event.preventDefault();
        const dataset = event.currentTarget.dataset;
        if (dataset.roll) {
            let label = dataset.label ? `[Attribut] ${dataset.label}` : '';
            let roll = new Roll(dataset.roll, this.actor.getRollData());
            roll.toMessage({
                speaker: ChatMessage.getSpeaker({actor: this.actor}),
                flavor: label,
                rollMode: game.settings.get('core', 'rollMode'),
            });
            return roll;
        }
    }

    openEmbeddedDocument(documentId) {
        this.object.items.get(documentId).sheet.render(true)
    }

    _onRoll(event) {
        event.preventDefault();
        const dataset = event.currentTarget.dataset;
        if (dataset.roll) {
            let label = dataset.label ? `${dataset.label}` : '';
            let roll = new Roll(dataset.roll, this.actor.getRollData());
            roll.toMessage({
                speaker: ChatMessage.getSpeaker({actor: this.actor}),
                flavor: label,
                rollMode: game.settings.get('core', 'rollMode'),
            });
            return roll;
        }
    }

    #mapAllSets() {
        const updateObject = {}
        Array.from(this.object.system.heldenausruestung).forEach((equipmentSet, index) => {
            updateObject[`system.heldenausruestung.${index}.links`] = equipmentSet.links;
            updateObject[`system.heldenausruestung.${index}.rechts`] = equipmentSet.rechts;
            updateObject[`system.heldenausruestung.${index}.brust`] = equipmentSet.brust;
            updateObject[`system.heldenausruestung.${index}.bauch`] = equipmentSet.bauch;
            updateObject[`system.heldenausruestung.${index}.ruecken`] = equipmentSet.ruecken;
            updateObject[`system.heldenausruestung.${index}.kopf`] = equipmentSet.kopf;
            updateObject[`system.heldenausruestung.${index}.fernkampf`] = equipmentSet.fernkampf;
            updateObject[`system.heldenausruestung.${index}.munition`] = equipmentSet.munition;
            updateObject[`system.heldenausruestung.${index}.armlinks`] = equipmentSet.armlinks;
            updateObject[`system.heldenausruestung.${index}.armrechts`] = equipmentSet.armrechts;
            updateObject[`system.heldenausruestung.${index}.beinlinks`] = equipmentSet.beinlinks;
            updateObject[`system.heldenausruestung.${index}.beinrechts`] = equipmentSet.beinrechts;

        })
        return updateObject;
    }

    showAdjustAttributeDialog(attributeName, attributeField, previousValue) {
        const thisActor = this;
        const myContent = `
            Value:
          <input id="attributeValue" type="number" value="${previousValue}" />
        `;

        function updateAttribute(html) {
            const value = html.find("input#attributeValue").val();
            const attribute = {}
            attribute[attributeField.toLowerCase()] = {
                aktuell: value
            }
            thisActor.object.update({system: {attribute}})
        }

        new Dialog({
            title: `${attributeName} ändern auf`,
            content: myContent,
            buttons: {
                button1: {
                    label: "Ändern",
                    callback: (html) => {
                        updateAttribute(html)
                    },
                    icon: `<i class="fas fa-check"></i>`
                }
            }
        }).render(true);

    }

    #addLiturgiesToContext(context) {
        const actorData = context.data;
        context.liturgies = [];
        context.blessings = [];

        Object.values(actorData.items).forEach((item, index) => {
            if (item.type === "Blessing") {
                context.blessings.push({
                    deity: item.system.gottheit,
                    value: item.system.wert
                })
            }
        })
        Object.values(actorData.items).forEach((item, index) => {
            if (item.type === "Liturgy") {

                context.blessings.forEach(({deity, value}) => {
                    let insertObject = context.liturgies.find(p => p.deity === deity);
                    if (!insertObject) {
                        insertObject = {
                            deity: deity,
                            lkp: value,
                            O: [],
                            I: [],
                            II: [],
                            III: [],
                            IV: [],
                            V: [],
                            VI: [],
                            VII: [],
                            VIII: [],
                            "NA": [],
                            countO: 1,
                            countI: 1,
                            countII: 1,
                            countIII: 1,
                            countIV: 1,
                            countV: 1,
                            countVI: 1,
                            countVII: 1,
                            countVIII: 1,
                            countNA: 0,
                            total: 3,

                        }
                        context.liturgies.push(insertObject);
                    }

                    // sort by rank
                    const rankData = LiturgyData.getRankOfLiturgy(item.system, deity)
                    if (rankData) {
                        let {index, name, lkp, mod, costKaP} = rankData;

                        insertObject["count" + name] = insertObject["count" + name] + 1;

                        insertObject[name].push({
                            id: item._id,
                            name: item.name,
                            lkpReq: lkp,
                            lkpMod: mod,
                            costKaP,
                            rank: index,  // get effective liturgy rank based on deity
                            liturgiekenntnis: deity,
                        })
                        insertObject.total = insertObject.total + 1;

                    }
                })
            }
        })


        // clean up counter
        Object.values(context.liturgies).forEach((litObject) => {

            if (litObject.O.length === 0) litObject.countO = false;
            if (litObject.I.length === 0) litObject.countI = false;
            if (litObject.II.length === 0) litObject.countII = false;
            if (litObject.III.length === 0) litObject.countIII = false;
            if (litObject.IV.length === 0) litObject.countIV = false;
            if (litObject.V.length === 0) litObject.countV = false;
            if (litObject.VI.length === 0) litObject.countVI = false;
            if (litObject.VII.length === 0) litObject.countVII = false;
            if (litObject.VIII.length === 0) litObject.countVIII = false;
            if (litObject.NA.length === 0) litObject.countNA = false;


        })

        context.hasLiturgies = context.blessings.length > 0;
    }

    #handleDroppedSkill(actor, skill) {
        const array = Array.from(actor.items);
        for (let i = 0; i < array.length; i++) {
            if (array[i].name === skill.name) {
                return false;
            }
        }
    }

    async #handleDroppedActiveEffect(actor, activeEffect) {
        const array = Array.from(actor.items);
        for (let i = 0; i < array.length; i++) {
            if (array[i].name === activeEffect.name) {
                // replace active effect
                actor.deleteEmbeddedDocuments('Item', [array[i].id]).then(() => {
                    console.log("await")
                })

                return true;
            }
        }
    }

    #handleDroppedAdvantage(actor, advantage) {
        const array = Array.from(actor.items);
        for (let i = 0; i < array.length; i++) {
            if (array[i].name === advantage.name) { // TODO: adjust for uniqueness
                return false;
            }
        }
    }

    activateListeners(html) {
        super.activateListeners(html);

        const tabs = new Tabs({
            navSelector: ".paperdoll-tabs.tabs",
            contentSelector: ".sheet-body.paperdoll-sets",
            initial: "set" + (this.object.system.setEquipped + 1)
        });
        tabs.bind(html[0]);

        html.on('click', '.attribute.rollable', (evt) => {
            this._onAttributeRoll(evt);
        });

        html.on('click', '[data-operation="switchSet"]', (evt) => {
            const {id} = evt.currentTarget.dataset;
            console.log(id);
            this.object.update({"system.setEquipped": id})
        })

        html.on('click', '.talent.rollable', (evt) => {
            this._onTalentRoll(evt);
        });

        html.on('click', '.sidebar-element.rollable', (evt) => {
            this._onRoll(evt);
        });

        // TODO: merge into click.clickable handler
        html.on('click', '.talent .name', (evt) => {
            this.openEmbeddedDocument(evt.target.dataset.id);
            evt.stopPropagation();
        })

        // TODO: merge into click.clickable handler
        html.on('click', '.advantage .name', (evt) => {
            this.openEmbeddedDocument(evt.target.dataset.id);
            evt.stopPropagation();
        })

        // TODO: merge into click.clickable handler
        html.on('click', '.equipment', (evt) => {
            this.openEmbeddedDocument(evt.target.parentElement.dataset.id);
            evt.stopPropagation();
        })

        html.on('click', '.clickable', async (evt) => {
            const {id, operation} = evt.currentTarget.dataset;
            if (operation === "openActorSheet") {
                this.openEmbeddedDocument(id);
                evt.stopPropagation();
            }
        })

        html.on('dragstart', '.equipment', (evt) => {
            evt.originalEvent.dataTransfer.setData("application/json", JSON.stringify({
                documentId: evt.currentTarget.dataset.id
            }));
        })

        html.on('drop', '.equipped', async (evt) => {
            const {actor, target, setId} = evt.currentTarget.dataset;
            try {
                const {documentId} = JSON.parse(evt.originalEvent.dataTransfer.getData("application/json"));


                if (actor === this.object._id && documentId) { // managing equipped items
                    //const slot = this.#isWorn(documentId, setId)
                    //const updateObject = await this.#getEquipmentset(Number(setId))
                    const updateObject = this.#mapAllSets()
                    updateObject[`system.heldenausruestung.${setId}.${target}`] = documentId;
                    console.log(updateObject);

                    await this.object.update(updateObject);
                }

                evt.stopPropagation();
            } catch (e) {
            }
        })

        new ContextMenu(html, '.talent.rollable', [
            {
                name: "Entfernen",
                icon: '<i class="fa-solid fa-trash"></i>',
                callback: (event) => {
                    this.object.deleteEmbeddedDocuments('Item', [event[0].dataset.id])
                },
                condition: () => true
            }
        ]);


        new ContextMenu(html, '.attribute.rollable', [
            {
                name: "Anpassen",
                icon: '<i class="fa-solid fa-pen"></i>',
                callback: (event) => {
                    this.showAdjustAttributeDialog(event[0].dataset.name, event[0].dataset.label, event[0].dataset.value)
                },
                condition: () => true
            }
        ]);

        let handler = ev => this._onDragStart(ev);
        // Find all items on the character sheet.
        html.find('.talent.rollable').each((i, li) => {
            // Add draggable attribute and dragstart listener.
            li.setAttribute("draggable", true);
            li.addEventListener("dragstart", handler, false);
        });

        new ContextMenu(html, '.equipment', [
            {
                name: "Aus dem Inventar entfernen",
                icon: '<i class="fa-solid fa-trash"></i>',
                callback: (event) => {
                    // TODO find id on heldenausruestung to remove the worn items as well
                    this.object.deleteEmbeddedDocuments('Item', [event[0].dataset.id])
                },
                condition: () => true
            }
        ]);

        new ContextMenu(html, '.equipped', [
            {
                name: "Gegenstand vom Set entfernen",
                callback: (event) => {
                    const {setId, target, actor} = event[0].dataset

                    const updateObject = this.#mapAllSets()
                    updateObject[`system.heldenausruestung.${setId}.${target}`] = null;

                    this.object.update(updateObject);
                },
                condition: () => true
            }
        ]);

        html.on('click', '.liturgy.rollable', async (evt) => {

            evt.stopPropagation();

            const {id, rank, lkp, deity} = evt.currentTarget.dataset;
            const document = await this.object.items.get(id)

            const data = {};

            data.rank = rank;
            data.lkp = lkp;
            data.deity = deity;
            data.variations = [];
            const ranks = LiturgyData.ranks
            ranks.forEach(rank => {
                if (document.system.auswirkung[rank]) {
                    data.variations.push({
                        rank,
                        effect: document.system.auswirkung[rank]
                    })
                }
            })
            data.mods = [];

            const htmlContent = await renderTemplate('systems/DSA_4-1/templates/dialog/modify-liturgy.hbs', data);

            const dialogData = {
                title: document.name,
                content: htmlContent,
                data: {},
                buttons: {
                    submit: {
                        label: "Wirken",
                        icon: '<i class="fas fa-die"></i>',
                        callback: (html) => {
                        },
                    },
                },
            }
            dialogData.render = new ModifyLiturgy(data).handleRender

            const dialog = new Dialog(dialogData, {
                classes: ['dsa41', 'dialog', 'liturgy'],
                height: 480
            })

            dialog.render(true);

            return false;
        })



    }

    #handleDroppedEquipment(actor, equipment) {
        const array = Array.from(actor.items);
        for (let i = 0; i < array.length; i++) {
            if (array[i].name === equipment.name) { // TODO: adjust item quantity if item is the same
                return false;
            }
        }
    }

    #handleDroppedLiturgy(actor, liturgy) {
        const array = Array.from(actor.items);
        for (let i = 0; i < array.length; i++) {
            if (array[i].name === liturgy.name) { // TODO: allow multiple miracles with the same name
                return false;
            }
        }
    }

    #handleDroppedSpecialAbility(actor, specialAbility) {
        const array = Array.from(actor.items);
        for (let i = 0; i < array.length; i++) {
            if (array[i].name === specialAbility.name) { // TODO: allow multiple miracles with the same name
                return false;
            }
        }
    }

}
