import typing
import random

from .Locations import location_table, lookup_name_to_id as locations_lookup_name_to_id
from .Items import (createResourcePackName, item_table, progressive_table, progressive_item_list,
    lookup_name_to_item, resourcepack_items as resourcePackItems, lookup_name_to_id as items_lookup_name_to_id)

from .Regions import create_regions, getConnectionName
from .Rules import set_rules
from .Options import raft_options

from BaseClasses import Region, RegionType, Entrance, Location, MultiWorld, Item
from ..AutoWorld import World

class RaftWorld(World):
    """
    Raft is a flooded world exploration game. You're stranded on a small raft in the middle of the
    ocean, and you must survive on trash floating by you on the top of the water and around/on any
    islands that you come across.
    """
    game: str = "Raft"

    item_name_to_id = items_lookup_name_to_id.copy()
    lastItemId = max(filter(lambda val: val is not None, item_name_to_id.values()))

    location_name_to_id = locations_lookup_name_to_id
    options = raft_options

    data_version = 1

    def generate_basic(self):
        minRPSpecified = self.world.minimum_resource_pack_amount[self.player].value
        maxRPSpecified = self.world.maximum_resource_pack_amount[self.player].value
        minimumResourcePackAmount = min(minRPSpecified, maxRPSpecified)
        maximumResourcePackAmount = max(minRPSpecified, maxRPSpecified)
        # Generate item pool
        pool = []
        for item in item_table:
            raft_item = self.create_item_replaceAsNecessary(item["name"])
            pool.append(raft_item)

        extraItemNamePool = []
        extras = len(location_table) - len(item_table) - 1 # Victory takes up 1 unaccounted-for slot
        if extras > 0:
            if (self.world.use_resource_packs[self.player].value):
                for packItem in resourcePackItems:
                    for i in range(minimumResourcePackAmount, maximumResourcePackAmount + 1):
                        extraItemNamePool.append(createResourcePackName(i, packItem))

            if self.world.duplicate_items[self.player].value != 0:
                dupeItemPool = item_table.copy()
                # Remove frequencies if necessary
                if self.world.island_frequency_locations[self.player].value != 3: # Not completely random locations
                    dupeItemPool = (itm for itm in dupeItemPool if "Frequency" not in itm["name"])
                
                # Remove progression or non-progression items if necessary
                if (self.world.duplicate_items[self.player].value == 1): # Progression only
                    dupeItemPool = (itm for itm in dupeItemPool if itm["progression"] == True)
                elif (self.world.duplicate_items[self.player].value == 2): # Non-progression only
                    dupeItemPool = (itm for itm in dupeItemPool if itm["progression"] == False)
                
                dupeItemPool = list(dupeItemPool)
                # Finally, add items as necessary
                if len(dupeItemPool) > 0:
                    for item in dupeItemPool:
                        extraItemNamePool.append(item["name"])
            
            if (len(extraItemNamePool) > 0):
                for randomItem in random.choices(extraItemNamePool, k=extras):
                    raft_item = self.create_item_replaceAsNecessary(randomItem)
                    pool.append(raft_item)

        self.world.itempool += pool

    def set_rules(self):
        set_rules(self.world, self.player)

    def create_regions(self):
        create_regions(self.world, self.player)

    def fill_slot_data(self):
        slot_data = {}
        return slot_data
    
    def create_item_replaceAsNecessary(self, name: str) -> Item:
        isFrequency = "Frequency" in name
        shouldUseProgressive = ((isFrequency and self.world.island_frequency_locations[self.player].value == 2)
            or (not isFrequency and self.world.progressive_items[self.player].value))
        if shouldUseProgressive and name in progressive_table:
            name = progressive_table[name]
        return self.create_item(name)

    def create_item(self, name: str) -> Item:
        item = lookup_name_to_item[name]
        return RaftItem(name, item["progression"], self.item_name_to_id[name], player=self.player)
    
    def create_resourcePack(self, rpName: str) -> Item:
        return RaftItem(rpName, False, self.item_name_to_id[rpName], player=self.player)
    
    def collect_item(self, state, item, remove=False):
        if item.name in progressive_item_list:
            prog_table = progressive_item_list[item.name]
            if remove:
                for item_name in reversed(prog_table):
                    if state.has(item_name, item.player):
                        return item_name
            else:
                for item_name in prog_table:
                    if not state.has(item_name, item.player):
                        return item_name

        return super(RaftWorld, self).collect_item(state, item, remove)

    def get_required_client_version(self) -> typing.Tuple[int, int, int]:
        return max((0, 2, 0), super(RaftWorld, self).get_required_client_version())
    
    def pre_fill(self):
        if self.world.island_frequency_locations[self.player] == 0:
            self.setLocationItem("Radio Tower Frequency to Vasagatan", "Vasagatan Frequency")
            self.setLocationItem("Vasagatan Frequency to Balboa", "Balboa Island Frequency")
            self.setLocationItem("Relay Station quest", "Caravan Island Frequency")
            self.setLocationItem("Caravan Island Frequency to Tangaroa", "Tangaroa Frequency")
        elif self.world.island_frequency_locations[self.player] == 1:
            self.setLocationItemFromRegion("RadioTower", "Vasagatan Frequency")
            self.setLocationItemFromRegion("Vasagatan", "Balboa Island Frequency")
            self.setLocationItemFromRegion("BalboaIsland", "Caravan Island Frequency")
            self.setLocationItemFromRegion("CaravanIsland", "Tangaroa Frequency")
        # Victory item
        self.world.get_location("Tangaroa Next Frequency", self.player).place_locked_item(
            RaftItem("Victory", True, None, player=self.player))
    
    def setLocationItem(self, location: str, itemName: str):
        itemToUse = next(filter(lambda itm: itm.name == itemName, self.world.itempool))
        self.world.itempool.remove(itemToUse)
        self.world.get_location(location, self.player).place_locked_item(itemToUse)
    
    def setLocationItemFromRegion(self, region: str, itemName: str):
        itemToUse = next(filter(lambda itm: itm.name == itemName, self.world.itempool))
        self.world.itempool.remove(itemToUse)
        location = random.choice(list(loc for loc in location_table if loc["region"] == region))
        self.world.get_location(location["name"], self.player).place_locked_item(itemToUse)

def create_region(world: MultiWorld, player: int, name: str, locations=None, exits=None):
    ret = Region(name, RegionType.Generic, name, player)
    ret.world = world
    if locations:
        for location in locations:
            loc_id = locations_lookup_name_to_id.get(location, 0)
            locationObj = RaftLocation(player, location, loc_id, ret)
            ret.locations.append(locationObj)
    if exits:
        for exit in exits:
            ret.exits.append(Entrance(player, getConnectionName(name, exit), ret))

    return ret

class RaftLocation(Location):
    game = "Raft"


class RaftItem(Item):
    game = "Raft"