"""
Defines Region for The Witness, assigns locations to them,
and connects them with the proper requirements
"""

from BaseClasses import MultiWorld, Entrance
from .static_logic import StaticWitnessLogic
from .Options import get_option_value
from .locations import WitnessPlayerLocations, StaticWitnessLocations
from .player_logic import WitnessPlayerLogic


class WitnessRegions:
    """Class that defines Witness Regions"""

    locat = None
    logic = None

    def make_lambda(self, panel_hex_to_solve_set, world, player, player_logic):
        """
        Lambdas are made in a for loop, so the values have to be captured
        This function is for that purpose
        """

        return lambda state: state._witness_can_solve_panels(
            panel_hex_to_solve_set, world, player, player_logic, self.locat
        )

    def connect(self, world: MultiWorld, player: int, source: str, target: str, player_logic: WitnessPlayerLogic,
                panel_hex_to_solve_set=frozenset({frozenset()}), backwards: bool = False):
        """
        connect two regions and set the corresponding requirement
        """
        source_region = world.get_region(source, player)
        target_region = world.get_region(target, player)

        backwards = " Backwards" if backwards else ""

        connection = Entrance(
            player,
            source + " to " + target + backwards,
            source_region
        )

        connection.access_rule = self.make_lambda(panel_hex_to_solve_set, world, player, player_logic)

        source_region.exits.append(connection)
        connection.connect(target_region)

    def create_regions(self, world, player: int, player_logic: WitnessPlayerLogic):
        """
        Creates all the regions for The Witness
        """
        from . import create_region

        world.regions += [
            create_region(world, player, 'Menu', self.locat, None, ["The Splashscreen?"]),
        ]

        difficulty = get_option_value(world, player, "puzzle_randomization")

        if difficulty == 1:
            reference_logic = StaticWitnessLogic.sigma_expert
        elif difficulty == 0:
            reference_logic = StaticWitnessLogic.sigma_normal
        else:
            reference_logic = StaticWitnessLogic.vanilla

        all_locations = set()

        for region_name, region in reference_logic.ALL_REGIONS_BY_NAME.items():
            locations_for_this_region = [
                reference_logic.CHECKS_BY_HEX[panel]["checkName"] for panel in region["panels"]
                if reference_logic.CHECKS_BY_HEX[panel]["checkName"] in self.locat.CHECK_LOCATION_TABLE
            ]
            locations_for_this_region += [
                StaticWitnessLocations.get_event_name(panel) for panel in region["panels"]
                if StaticWitnessLocations.get_event_name(panel) in self.locat.EVENT_LOCATION_TABLE
            ]

            all_locations = all_locations | set(locations_for_this_region)

            world.regions += [
                create_region(world, player, region_name, self.locat, locations_for_this_region)
            ]

        for region_name, region in reference_logic.ALL_REGIONS_BY_NAME.items():
            for connection in player_logic.CONNECTIONS_BY_REGION_NAME[region_name]:
                if connection[1] == frozenset({frozenset(["TrueOneWay"])}):
                    self.connect(world, player, region_name, connection[0], player_logic, frozenset({frozenset()}))
                    continue

                backwards_connections = set()

                for subset in connection[1]:
                    if all({panel in player_logic.DOOR_ITEMS_BY_ID for panel in subset}):
                        if all({reference_logic.CHECKS_BY_HEX[panel]["id"] is None for panel in subset}):
                            backwards_connections.add(subset)

                if backwards_connections:
                    self.connect(
                        world, player, connection[0], region_name, player_logic,
                        frozenset(backwards_connections), True
                    )

                self.connect(world, player, region_name, connection[0], player_logic, connection[1])

        world.get_entrance("The Splashscreen?", player).connect(
            world.get_region('First Hallway', player)
        )

    def __init__(self, locat: WitnessPlayerLocations):
        self.locat = locat