from typing import Tuple
from BaseClasses import Location
from .Options import TotalLocations
from .Options import ChestsPerEnvironment
from .Options import ShrinesPerEnvironment
from .Options import ScavengersPerEnvironment
from .Options import ScannersPerEnvironment
from .Options import AltarsPerEnvironment
from .RoR2Environments import *


class RiskOfRainLocation(Location):
    game: str = "Risk of Rain 2"


ror2_locations_start_id = 38000


def get_classic_item_pickups(n: int) -> Dict[str, int]:
    """Get n ItemPickups, capped at the max value for TotalLocations"""
    n = max(n, 0)
    n = min(n, TotalLocations.range_end)
    return { f"ItemPickup{i+1}": ror2_locations_start_id+i for i in range(n) }


item_pickups = get_classic_item_pickups(TotalLocations.range_end)
location_table = item_pickups


def environment_abreviation(long_name:str) -> str:
    """convert long environment names to initials"""
    abrev = ""
    # go through every word finding a letter (or number) for an initial
    for word in long_name.split():
        initial = word[0]
        for letter in word:
            if letter.isalnum():
                initial = letter
                break
        abrev+= initial
    return abrev

# highest numbered orderedstages (this is so we can treat the easily caculate the check ids based on the environment and location "offset")
highest_orderedstage: int= max(compress_dict_list_horizontal(environment_orderedstages_table).values())

ror2_locations_start_orderedstage = ror2_locations_start_id + TotalLocations.range_end

class orderedstage_location:
    """A class to behave like a struct for storing the offsets of location types in the allocated space per orderedstage environments."""
    # TODO is there a better, more generic way to do this?
    offset_ChestsPerEnvironment     = 0
    offset_ShrinesPerEnvironment    = offset_ChestsPerEnvironment       + ChestsPerEnvironment.range_end
    offset_ScavengersPerEnvironment = offset_ShrinesPerEnvironment      + ShrinesPerEnvironment.range_end
    offset_ScannersPerEnvironment   = offset_ScavengersPerEnvironment   + ScavengersPerEnvironment.range_end
    offset_AltarsPerEnvironment     = offset_ScannersPerEnvironment     + ScannersPerEnvironment.range_end

    # total space allocated to the locations in a single orderedstage environment
    allocation = offset_AltarsPerEnvironment + AltarsPerEnvironment.range_end

    def get_environment_locations(chests:int, shrines:int, scavengers:int, scanners:int, altars:int, environment: Tuple[str, int]) -> Dict[str, int]:
        """Get the locations within a specific environment"""
        environment_name = environment[0]
        environment_index = environment[1]
        locations = {}

        # due to this mapping, since environment ids are not consecutive, there are lots of "wasted" id numbers
        # TODO perhaps a hashing algorithm could be used to compress this range and save "wasted" ids
        environment_start_id = environment_index * orderedstage_location.allocation + ror2_locations_start_orderedstage
        for n in range(chests):
            locations.update({f"{environment_name}: Chest {n+1}":            n + orderedstage_location.offset_ChestsPerEnvironment       + environment_start_id})
        for n in range(shrines):
            locations.update({f"{environment_name}: Shrine {n+1}":           n + orderedstage_location.offset_ShrinesPerEnvironment      + environment_start_id})
        for n in range(scavengers):
            locations.update({f"{environment_name}: Scavenger {n+1}":        n + orderedstage_location.offset_ScavengersPerEnvironment   + environment_start_id})
        for n in range(scanners):
            locations.update({f"{environment_name}: Radio Scanner {n+1}":    n + orderedstage_location.offset_ScannersPerEnvironment     + environment_start_id})
        for n in range(altars):
            locations.update({f"{environment_name}: Newt Altar {n+1}":       n + orderedstage_location.offset_AltarsPerEnvironment       + environment_start_id})
        return locations

    def get_locations(chests:int, shrines:int, scavengers:int, scanners:int, altars:int, dlc_sotv:bool) -> Dict[str, int]:
        """Get a dictionary of locations for the ordedstage environments with the locations from the parameters."""
        locations = {}
        orderedstages = compress_dict_list_horizontal(environment_vanilla_orderedstages_table)
        if(dlc_sotv): orderedstages.update(compress_dict_list_horizontal(environment_sotv_orderedstages_table))
        # for every environment, generate the respective locations
        for environment_name, environment_index in orderedstages.items():
            # locations = locations | orderedstage_location.get_environment_locations(
            locations.update(orderedstage_location.get_environment_locations(
                chests=chests,
                shrines=shrines,
                scavengers=scavengers,
                scanners=scanners,
                altars=altars,
                environment=(environment_name, environment_index)
            ))
        return locations

    def getall_locations(dlc_sotv:bool=True) -> Dict[str, int]:
        """
        Get all locations in ordered stages.
        Set dlc_sotv to true for the SOTV DLC to be included.
        """
        # to get all locations, attempt using as many locations as possible
        return orderedstage_location.get_locations(
            chests=ChestsPerEnvironment.range_end,
            shrines=ShrinesPerEnvironment.range_end,
            scavengers=ScavengersPerEnvironment.range_end,
            scanners=ScannersPerEnvironment.range_end,
            altars=AltarsPerEnvironment.range_end,
            dlc_sotv=dlc_sotv
        )


ror2_location_post_orderedstage = ror2_locations_start_orderedstage + highest_orderedstage*orderedstage_location.allocation
location_table.update(orderedstage_location.getall_locations())
# use the sotv dlc in the lookup table so that all ids can be looked up regardless of use

lookup_id_to_name: Dict[int, str] = {id: name for name, id in location_table.items()}