from BaseClasses import Region, Entrance, Location, CollectionState
import typing

from .LADXR.checkMetadata import checkMetadataTable
from .Common import *
from worlds.generic.Rules import add_item_rule
from .Items import ladxr_item_to_la_item_name


prefilled_events = ["ANGLER_KEYHOLE", "RAFT", "MEDICINE2", "CASTLE_BUTTON"]

links_awakening_dungeon_names = [
    "Tail Cave",
    "Bottle Grotto",
    "Key Cavern",
    "Angler's Tunnel",
    "Catfish's Maw",
    "Face Shrine",
    "Eagle's Tower",
    "Turtle Rock",
    "Color Dungeon"
]


def meta_to_name(meta):
    return f"{meta.name} ({meta.area})"

def get_location_name_groups() -> typing.Dict[str, typing.Set[str]]:
    groups = {
        "Instrument Pedestals": {
            "Full Moon Cello (Tail Cave)",
            "Conch Horn (Bottle Grotto)",
            "Sea Lily's Bell (Key Cavern)",
            "Surf Harp (Angler's Tunnel)",
            "Wind Marimba (Catfish's Maw)",
            "Coral Triangle (Face Shrine)",
            "Organ of Evening Calm (Eagle's Tower)",
            "Thunder Drum (Turtle Rock)",
        },
        "Boss Rewards": {
            "Moldorm Heart Container (Tail Cave)",
            "Genie Heart Container (Bottle Grotto)",
            "Slime Eye Heart Container (Key Cavern)",
            "Angler Fish Heart Container (Angler's Tunnel)",
            "Slime Eel Heart Container (Catfish's Maw)",
            "Facade Heart Container (Face Shrine)",
            "Evil Eagle Heart Container (Eagle's Tower)",
            "Hot Head Heart Container (Turtle Rock)",
            "Tunic Fairy Item 1 (Color Dungeon)",
            "Tunic Fairy Item 2 (Color Dungeon)",
        },
    }
    # Add region groups
    for s, v in checkMetadataTable.items():
        if s == "None":
            continue
        groups.setdefault(v.area, set()).add(meta_to_name(v))
    return groups

links_awakening_location_name_groups = get_location_name_groups()

def get_locations_to_id():
    ret = {

    }

    # Magic to generate unique ids
    for s, v in checkMetadataTable.items():
        if s == "None":
            continue
        splits = s.split("-")

        main_id = int(splits[0], 16)
        sub_id = 0
        if len(splits) > 1:
            sub_id = splits[1]
            if sub_id.isnumeric():
                sub_id = (int(sub_id) + 1) * 1000
            else:
                sub_id = 1000
        name = f"{v.name} ({v.area})"
        ret[name] = BASE_ID + main_id + sub_id

    return ret


locations_to_id = get_locations_to_id()


class LinksAwakeningLocation(Location):
    game = LINKS_AWAKENING
    dungeon = None

    def __init__(self, player: int, region, ladxr_item):
        name = meta_to_name(ladxr_item.metadata)
        address = None

        if ladxr_item.event is not None:
            name = ladxr_item.event
        else:
            address = locations_to_id[name]
        super().__init__(player, name, address)
        self.parent_region = region
        self.ladxr_item = ladxr_item

        def filter_item(item):
            if not ladxr_item.MULTIWORLD and item.player != player:
                return False
            return True
        add_item_rule(self, filter_item)


def has_free_weapon(state: CollectionState, player: int) -> bool:
    return state.has("Progressive Sword", player) or state.has("Magic Rod", player) or state.has("Boomerang", player) or state.has("Hookshot", player)


# If the player has access to farm enough rupees to afford a game, we assume that they can keep beating the game
def can_farm_rupees(state: CollectionState, player: int) -> bool:
    return has_free_weapon(state, player) and (state.has("Can Play Trendy Game", player=player) or state.has("RAFT", player=player))


class LinksAwakeningRegion(Region):
    dungeon_index = None
    ladxr_region = None

    def __init__(self, name, ladxr_region, hint, player, world):
        super().__init__(name, player, world, hint)
        if ladxr_region:
            self.ladxr_region = ladxr_region
            if ladxr_region.dungeon:
                self.dungeon_index = ladxr_region.dungeon


def translate_item_name(item):
    if item in ladxr_item_to_la_item_name:
        return ladxr_item_to_la_item_name[item]

    return item


class GameStateAdapater:
    def __init__(self, state, player):
        self.state = state
        self.player = player

    def __contains__(self, item):
        if item.endswith("_USED"):
            return False
        if item in ladxr_item_to_la_item_name:
            item = ladxr_item_to_la_item_name[item]

        return self.state.has(item, self.player)

    def get(self, item, default):
        # Don't allow any money usage if you can't get back wasted rupees
        if item == "RUPEES":
            if can_farm_rupees(self.state, self.player):
                return self.state.prog_items[self.player]["RUPEES"]
            return 0
        elif item.endswith("_USED"):
            return 0
        else:
            item = ladxr_item_to_la_item_name[item]
        return self.state.prog_items[self.player].get(item, default)


class LinksAwakeningEntrance(Entrance):
    def __init__(self, player: int, name, region, condition):
        super().__init__(player, name, region)
        if isinstance(condition, str):
            if condition in ladxr_item_to_la_item_name:
                # Test if in inventory
                self.condition = ladxr_item_to_la_item_name[condition]
            else:
                # Event
                self.condition = condition
        elif condition:
            # rewrite condition
            # .copyWithModifiedItemNames(translate_item_name)
            self.condition = condition
        else:
            self.condition = None

    def access_rule(self, state):
        if isinstance(self.condition, str):
            return state.has(self.condition, self.player)
        if self.condition is None:
            return True

        return self.condition.test(GameStateAdapater(state, self.player))


# Helper to apply function to every ladxr region
def walk_ladxdr(f, n, walked=set()):
    if n in walked:
        return
    f(n)
    walked.add(n)

    for o, req in n.simple_connections:
        walk_ladxdr(f, o, walked)
    for o, req in n.gated_connections:
        walk_ladxdr(f, o, walked)


def ladxr_region_to_name(n):
    name = n.name
    if not name:
        if len(n.items) == 1:
            meta = n.items[0].metadata
            name = f"{meta.name} ({meta.area})"
        elif n.dungeon:
            name = f"D{n.dungeon} Room"
        else:
            name = "No Name"

    return name


def create_regions_from_ladxr(player, multiworld, logic):
    tmp = set()

    def print_items(n):
        print(f"Creating Region {ladxr_region_to_name(n)}")
        print("Has simple connections:")
        for region, info in n.simple_connections:
            print("  " + ladxr_region_to_name(region) + " | " + str(info))
        print("Has gated connections:")

        for region, info in n.gated_connections:
            print("  " + ladxr_region_to_name(region) + " | " + str(info))

        print("Has Locations:")
        for item in n.items:
            print("  " + str(item.metadata))
        print()

    used_names = {}

    regions = {}

    # Create regions
    for l in logic.location_list:
        # Temporarily uniqueify the name, until all regions are named
        name = ladxr_region_to_name(l)
        index = used_names.get(name, 0) + 1
        used_names[name] = index
        if index != 1:
            name += f" {index}"

        r = LinksAwakeningRegion(
            name=name, ladxr_region=l, hint="", player=player, world=multiworld)
        r.locations += [LinksAwakeningLocation(player, r, i) for i in l.items]
        regions[l] = r

    for ladxr_location in logic.location_list:
        for connection_location, connection_condition in ladxr_location.simple_connections + ladxr_location.gated_connections:
            region_a = regions[ladxr_location]
            region_b = regions[connection_location]
            # TODO: This name ain't gonna work for entrance rando, we need to cross reference with logic.world.overworld_entrance
            entrance = LinksAwakeningEntrance(
                player, f"{region_a.name} -> {region_b.name}", region_a, connection_condition)
            region_a.exits.append(entrance)
            entrance.connect(region_b)

    return list(regions.values())