# Copyright (c) 2023 The Old Man and the C
#
# This file is part of Elden Ring Randomizer Hints.
#
# Elden Ring Randomizer Hints is free software: you can redistribute it and/or
# modify it under the terms of the GNU Affero General Public License as
# published by the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# Elden Ring Randomizer Hints is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
# or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License
# for more details.
#
# You should have received a copy of the GNU Affero General Public License along
# with Elden Ring Randomizer Hints. If not, see <https://www.gnu.org/licenses/>.

from source.utils.entryMatch import entryMatch
from source.model.randomizer.Areas import Areas
from source.model.randomizer.exportMissingEnemies import exportMissingEnemies
from source.model.randomizer.loadItemSlots import loadItemSlots
from source.model.randomizer.loadSpoilers import loadSpoilers
from source.model.randomizer.matchingSlot import matchingSlot
from source.model.randomizer.processItemEntry import processItemEntry
from source.data.model.randomizerData.killQuests import killQuests


# Parse the spoiler file generated by the randomizer to create a data structure
# indexed by item name that contains all the available information about the
# item's new location. Also return the random seed from the spoiler file and a
# list of missable items to display to the user.
def processRandomized(itemSlotsLines, mapNamesLines, annotationsLines,
                      spoilerLines, missableLots, missableShopLots):
    # Generate the dictionary of randomized items from the spoilers file
    seed, randomized, bossDict, enemyDict = loadSpoilers(spoilerLines)
    # get itemslots.txt and areas data structures to get item/location
    # information
    itemSlots = loadItemSlots(itemSlotsLines)
    areas = Areas(mapNamesLines, annotationsLines)
    questItems = dict()
    questTargets = dict()
    missable = dict()
    missingEnemies = []
    removeItems = []
    # process the item dictionary and add useful data
    for item in randomized:
        removeEntries = []
        for itemEntry in randomized[item]:
            # won't have an item slots entry if it's a random drop
            if "dropChance" in itemEntry:
                itemEntry["lotID"] = None
                continue
            # get the matching slot entry from itemslots.txt and merge
            itemSlot = matchingSlot(itemEntry, itemSlots, areas)
            if not itemSlot:
                continue
            itemEntry.update(itemSlot)
            # process the merged item entry
            if not processItemEntry(itemEntry, randomized, enemyDict, bossDict,
                                    areas):
                removeEntries.append(itemEntry)
                continue
            # check for missing enemy names
            if "newEnemy" in itemEntry and itemEntry["newEnemy"] == "an enemy":
                missingEnemies.append(itemEntry)
            # mark items that are dropped by an enemy that's part of a
            # shop quest
            for killQuestKey in killQuests:
                if entryMatch(itemEntry, killQuests[killQuestKey]):
                    if itemEntry["enemy"] not in questTargets:
                        questTargets[itemEntry["enemy"]] = [itemEntry]
                    else:
                        questTargets[itemEntry["enemy"]].append(itemEntry)
                    break
            # mark items that are only sold after defeating an enemy
            if "killQuest" in itemEntry:
                enemyName = itemEntry["killQuest"]["enemy"]
                if enemyName not in questItems:
                    questItems[enemyName] = [itemEntry]
                else:
                    questItems[enemyName].append(itemEntry)
            # if the item is easily missed, add it to the missable items list
            if "isShop" in itemEntry:
                selectedMissableLots = missableShopLots
            else:
                selectedMissableLots = missableLots
            if itemEntry["lotID"] in selectedMissableLots:
                if itemEntry["newItem"] in missable:
                    missable[itemEntry["newItem"]] += \
                        "\n\n" + selectedMissableLots[itemEntry["lotID"]]
                else:
                    missable[itemEntry["newItem"]] = \
                        selectedMissableLots[itemEntry["lotID"]]
        # remove any item entries we skipped from the item list
        for itemEntry in removeEntries:
            randomized[item].remove(itemEntry)
        # if the item list is now empty, mark it for deletion
        if not randomized[item]:
            removeItems.append(item)
    # remove any empty item lists
    for item in removeItems:
        randomized.pop(item)

    # connect shop items unlocked by killing bosses to their targets
    for key in questItems:
        for questItem in questItems[key]:
            questItem["parentEntries"] = questTargets[key]
    # if there were missing enemies, export them to a file, so they can be fixed
    if missingEnemies:
        missingEnemiesText = exportMissingEnemies(missingEnemies, bossDict,
                                                  enemyDict)
    else:
        missingEnemiesText = None
    return randomized, seed, missable, missingEnemiesText
