"""
Author: Louis M
Date: Fri, 15 Mar 2024 18:41:40 +0000
Description: Main module for Aquaria game multiworld randomizer
"""

from typing import List, Dict, ClassVar, Any
from worlds.AutoWorld import World, WebWorld
from BaseClasses import Tutorial, MultiWorld, ItemClassification
from .Items import item_table, AquariaItem, ItemType, ItemGroup
from .Locations import location_table
from .Options import AquariaOptions
from .Regions import AquariaRegions


class AquariaWeb(WebWorld):
    """
    Class used to generate the Aquaria Game Web pages (setup, tutorial, etc.)
    """
    theme = "ocean"

    bug_report_page = "https://github.com/tioui/Aquaria_Randomizer/issues"

    setup = Tutorial(
        "Multiworld Setup Guide",
        "A guide to setting up Aquaria for MultiWorld.",
        "English",
        "setup_en.md",
        "setup/en",
        ["Tioui"]
    )

    setup_fr = Tutorial(
        "Guide de configuration Multimonde",
        "Un guide pour configurer Aquaria MultiWorld",
        "Français",
        "setup_fr.md",
        "setup/fr",
        ["Tioui"]
    )

    tutorials = [setup, setup_fr]


class AquariaWorld(World):
    """
    Aquaria is a side-scrolling action-adventure game. It follows Naija, an
    aquatic humanoid woman, as she explores the underwater world of Aquaria.
    Along her journey, she learns about the history of the world she inhabits
    as well as her own past. The gameplay focuses on a combination of swimming,
    singing, and combat, through which Naija can interact with the world. Her
    songs can move items, affect plants and animals, and change her physical
    appearance into other forms that have different abilities, like firing
    projectiles at hostile creatures, or passing through barriers inaccessible
    to her in her natural form.
    From: https://en.wikipedia.org/wiki/Aquaria_(video_game)
    """

    game: str = "Aquaria"
    "The name of the game"

    topology_present = True
    "show path to required location checks in spoiler"

    web: WebWorld = AquariaWeb()
    "The web page generation informations"

    item_name_to_id: ClassVar[Dict[str, int]] =\
        {name: data.id for name, data in item_table.items()}
    "The name and associated ID of each item of the world"

    item_name_groups = {
        "Damage": {"Energy form", "Nature form", "Beast form",
                   "Li and Li song", "Baby Nautilus", "Baby Piranha",
                   "Baby Blaster"},
        "Light": {"Sun form", "Baby Dumbo"}
    }
    """Grouping item make it easier to find them"""

    location_name_to_id = location_table
    "The name and associated ID of each location of the world"

    base_id = 698000
    "The starting ID of the items and locations of the world"

    ingredients_substitution: List[int]
    "Used to randomize ingredient drop"

    options_dataclass = AquariaOptions
    "Used to manage world options"

    options: AquariaOptions
    "Every options of the world"

    regions: AquariaRegions
    "Used to manage Regions"

    exclude: List[str]

    def __init__(self, multiworld: MultiWorld, player: int):
        """Initialisation of the Aquaria World"""
        super(AquariaWorld, self).__init__(multiworld, player)
        self.regions = AquariaRegions(multiworld, player)
        self.ingredients_substitution = []
        self.exclude = []

    def create_regions(self) -> None:
        """
        Create every Region in `regions`
        """
        self.regions.add_regions_to_world()
        self.regions.connect_regions()
        self.regions.add_event_locations()

    def create_item(self, name: str) -> AquariaItem:
        """
        Create an AquariaItem using 'name' as item name.
        """
        result: AquariaItem
        try:
            data = item_table[name]
            classification: ItemClassification = ItemClassification.useful
            if data.type == ItemType.JUNK:
                classification = ItemClassification.filler
            elif data.type == ItemType.PROGRESSION:
                classification = ItemClassification.progression
            result = AquariaItem(name, classification, data.id, self.player)
        except BaseException:
            raise Exception('The item ' + name + ' is not valid.')

        return result

    def __pre_fill_item(self, item_name: str, location_name: str, precollected) -> None:
        """Pre-assign an item to a location"""
        if item_name not in precollected:
            self.exclude.append(item_name)
            data = item_table[item_name]
            item = AquariaItem(item_name, ItemClassification.useful, data.id, self.player)
            self.multiworld.get_location(location_name, self.player).place_locked_item(item)

    def get_filler_item_name(self):
        """Getting a random ingredient item as filler"""
        ingredients = []
        for name, data in item_table.items():
            if data.group == ItemGroup.INGREDIENT:
                ingredients.append(name)
        filler_item_name = self.random.choice(ingredients)
        return filler_item_name

    def create_items(self) -> None:
        """Create every item in the world"""
        precollected = [item.name for item in self.multiworld.precollected_items[self.player]]
        if self.options.turtle_randomizer.value > 0:
            if self.options.turtle_randomizer.value == 2:
                self.__pre_fill_item("Transturtle Final Boss", "Final Boss area, Transturtle", precollected)
        else:
            self.__pre_fill_item("Transturtle Veil top left", "The Veil top left area, Transturtle", precollected)
            self.__pre_fill_item("Transturtle Veil top right", "The Veil top right area, Transturtle", precollected)
            self.__pre_fill_item("Transturtle Open Water top right", "Open Water top right area, Transturtle",
                                 precollected)
            self.__pre_fill_item("Transturtle Forest bottom left", "Kelp Forest bottom left area, Transturtle",
                                 precollected)
            self.__pre_fill_item("Transturtle Home Water", "Home Water, Transturtle", precollected)
            self.__pre_fill_item("Transturtle Abyss right", "Abyss right area, Transturtle", precollected)
            self.__pre_fill_item("Transturtle Final Boss", "Final Boss area, Transturtle", precollected)
            # The last two are inverted because in the original game, they are special turtle that communicate directly
            self.__pre_fill_item("Transturtle Simon Says", "Arnassi Ruins, Transturtle", precollected)
            self.__pre_fill_item("Transturtle Arnassi Ruins", "Simon Says area, Transturtle", precollected)
        for name, data in item_table.items():
            if name not in self.exclude:
                for i in range(data.count):
                    item = self.create_item(name)
                    self.multiworld.itempool.append(item)

    def set_rules(self) -> None:
        """
        Launched when the Multiworld generator is ready to generate rules
        """

        self.regions.adjusting_rules(self.options)
        self.multiworld.completion_condition[self.player] = lambda \
            state: state.has("Victory", self.player)

    def generate_basic(self) -> None:
        """
        Player-specific randomization that does not affect logic.
        Used to fill then `ingredients_substitution` list
        """
        simple_ingredients_substitution = [i for i in range(27)]
        if self.options.ingredient_randomizer.value > 0:
            if self.options.ingredient_randomizer.value == 1:
                simple_ingredients_substitution.pop(-1)
                simple_ingredients_substitution.pop(-1)
                simple_ingredients_substitution.pop(-1)
            self.random.shuffle(simple_ingredients_substitution)
            if self.options.ingredient_randomizer.value == 1:
                simple_ingredients_substitution.extend([24, 25, 26])
        dishes_substitution = [i for i in range(27, 76)]
        if self.options.dish_randomizer:
            self.random.shuffle(dishes_substitution)
        self.ingredients_substitution.clear()
        self.ingredients_substitution.extend(simple_ingredients_substitution)
        self.ingredients_substitution.extend(dishes_substitution)

    def fill_slot_data(self) -> Dict[str, Any]:
        return {"ingredientReplacement": self.ingredients_substitution,
                "aquarian_translate": bool(self.options.aquarian_translation.value),
                "blind_goal": bool(self.options.blind_goal.value),
                "secret_needed": self.options.objective.value > 0,
                "minibosses_to_kill": self.options.mini_bosses_to_beat.value,
                "bigbosses_to_kill": self.options.big_bosses_to_beat.value,
                "skip_first_vision": bool(self.options.skip_first_vision.value),
                "unconfine_home_water_energy_door": self.options.unconfine_home_water.value in [1, 3],
                "unconfine_home_water_transturtle": self.options.unconfine_home_water.value in [2, 3],
                }
