import argparse
import os
import urllib.parse
import urllib.request
from pathlib import Path

import yaml

import RaceRandom as random


def get_weights(path):
    if os.path.exists(Path(path)):
        with open(path, "r", encoding="utf-8") as f:
            return yaml.load(f, Loader=yaml.SafeLoader)
    elif urllib.parse.urlparse(path).scheme in ['http', 'https']:
        return yaml.load(urllib.request.urlopen(path), Loader=yaml.FullLoader)

def roll_settings(weights):
    while True:
        subweights = weights.get('subweights', {})
        if len(subweights) == 0:
            break
        chances = ({k: int(v['chance']) for (k, v) in subweights.items()})
        subweight_name = random.choices(list(chances.keys()), weights=list(chances.values()))[0]
        subweights = weights.get('subweights', {}).get(subweight_name, {}).get('weights', {})
        subweights['subweights'] = subweights.get('subweights', {})
        weights = {**weights, **subweights}

    def get_choice(option, root=None):
        root = weights if root is None else root
        if option not in root:
            return None
        if type(root[option]) is not dict:
            return root[option]
        if not root[option]:
            return None
        return random.choices(list(root[option].keys()), weights=list(map(int, root[option].values())))[0]

    def get_choice_default(option, root=weights, default=None):
        choice = get_choice(option, root)
        if choice is None and default is not None:
            return default
        return choice

    def get_choice_bool(option, root=weights):
        choice = get_choice(option, root)
        if choice is True or choice == 'on':
            return True
        if choice is False or choice == 'off':
            return False
        if choice is None:
            return choice
        raise Exception("This fields needs to be true/false or off/on")

    def get_choice_non_bool(option, root=weights):
        choice = get_choice(option, root)
        if choice is True or choice == 'on':
            return 'on'
        if choice is False or choice == 'off':
            return 'off'
        return choice

    def get_choice_yn(option, root=weights):
        choice = get_choice(option, root)
        if choice is True or choice == 'yes':
            return 'yes'
        if choice is False or choice == 'no':
            return 'no'
        return choice


    def get_choice_bool_default(option, root=weights, default=None):
        choice = get_choice_bool(option, root)
        if choice is None and default is not None:
            return default
        return choice

    ret = argparse.Namespace()

    ret.algorithm = get_choice('algorithm')

    glitch_map = {'none': 'noglitches', 'minorglitches': 'minorglitches',
                  'hmg': 'hybridglitches', 'hybridglitches': 'hybridglitches',
                  'owg': 'owglitches', 'owglitches': 'owglitches',
                  'no_logic': 'nologic'}
    glitches_required = get_choice('glitches_required')
    if glitches_required is not None:
        if glitches_required not in glitch_map.keys():
            print(f'Logic did not match one of: {", ".join(glitch_map.keys())}')
            glitches_required = 'none'
        ret.logic = glitch_map[glitches_required]

    # item_placement = get_choice('item_placement')
    # not supported in ER

    dungeon_items = get_choice('dungeon_items')
    dungeon_items = '' if dungeon_items == 'standard' or dungeon_items is None else dungeon_items
    dungeon_items = 'mcsb' if dungeon_items == 'full' else dungeon_items
    if 'map_shuffle' in weights:
        ret.mapshuffle = get_choice('map_shuffle')
    elif 'm' in dungeon_items:
        ret.mapshuffle = 'wild'
    if 'compass_shuffle' in weights:
        ret.compassshuffle = get_choice('compass_shuffle')
    elif 'c' in dungeon_items:
        ret.compassshuffle = 'wild'
    if 'smallkey_shuffle' in weights:
        ret.keyshuffle = get_choice('smallkey_shuffle')
    else:
        if 's' in dungeon_items:
            ret.keyshuffle = 'wild'
        if 'u' in dungeon_items:
            ret.keyshuffle = 'universal'
    if 'bigkey_shuffle' in weights:
        ret.bigkeyshuffle = get_choice('bigkey_shuffle')
    elif 'b' in dungeon_items:
        ret.bigkeyshuffle = 'wild'
    ret.prizeshuffle = get_choice('prize_shuffle')

    ret.accessibility = get_choice('accessibility')
    ret.restrict_boss_items = get_choice('restrict_boss_items')

    overworld_layout = get_choice('overworld_layout')
    ret.ow_layout = overworld_layout if overworld_layout != 'none' else 'vanilla'
    ret.ow_parallel = get_choice_bool('overworld_parallel')
    overworld_shuffle = get_choice('overworld_shuffle')
    if overworld_shuffle == 'parallel':
        ret.ow_layout = 'wild'
        ret.ow_parallel = True
    elif overworld_shuffle == 'full':
        ret.ow_layout = 'wild'
        ret.ow_parallel = False
    ret.ow_terrain = get_choice_bool('overworld_terrain')
    valid_options = {'none': 'none', 'polar': 'polar', 'grouped': 'polar', 'chaos': 'unrestricted', 'unrestricted': 'unrestricted'}
    ret.ow_crossed = get_choice('overworld_crossed')
    ret.ow_crossed = valid_options[ret.ow_crossed] if ret.ow_crossed in valid_options else 'none'
    ret.ow_keepsimilar = get_choice_bool('overworld_keepsimilar')
    ret.ow_mixed = get_choice_bool('overworld_swap')
    ret.ow_whirlpool = get_choice_bool('whirlpool_shuffle')
    overworld_flute = get_choice('flute_shuffle')
    ret.ow_fluteshuffle = overworld_flute if overworld_flute != 'none' else 'vanilla'
    ret.ow_fog = get_choice_bool('overworld_fog')
    ret.shuffle_followers = get_choice_bool('shuffle_followers')
    ret.bonk_drops = get_choice_bool('bonk_drops')
    entrance_shuffle = get_choice('entrance_shuffle')
    ret.shuffle = entrance_shuffle if entrance_shuffle != 'none' else 'vanilla'
    overworld_map = get_choice('overworld_map')
    ret.overworld_map = overworld_map if overworld_map != 'default' else 'default'
    door_shuffle = get_choice('door_shuffle')
    ret.door_shuffle = door_shuffle if door_shuffle != 'none' else 'vanilla'
    ret.intensity = get_choice('intensity')
    ret.door_type_mode = get_choice('door_type_mode')
    ret.trap_door_mode = get_choice('trap_door_mode')
    ret.key_logic_algorithm = get_choice('key_logic_algorithm')
    ret.decoupledoors = get_choice_bool('decoupledoors')
    ret.door_self_loops = get_choice_bool('door_self_loops')
    ret.experimental = get_choice_bool('experimental')
    ret.collection_rate = get_choice_bool('collection_rate')
    ret.dungeon_counters = get_choice_non_bool('dungeon_counters') if 'dungeon_counters' in weights else 'default'
    ret.pseudoboots = get_choice_bool('pseudoboots')
    ret.mirrorscroll = get_choice_bool('mirrorscroll')
    ret.shopsanity = get_choice_bool('shopsanity')
    keydropshuffle = get_choice_bool('keydropshuffle')
    ret.dropshuffle = get_choice('dropshuffle') if 'dropshuffle' in weights else 'none'
    ret.dropshuffle = 'keys' if ret.dropshuffle == 'none' and keydropshuffle else ret.dropshuffle
    ret.pottery = get_choice('pottery') if 'pottery' in weights else 'none'
    ret.pottery = 'keys' if ret.pottery == 'none' and keydropshuffle else ret.pottery
    ret.colorizepots = get_choice_bool_default('colorizepots', default=True)
    ret.shufflepots = get_choice_bool('pot_shuffle')
    ret.aga_randomness = get_choice_bool('aga_randomness')
    ret.mixed_travel = get_choice('mixed_travel') if 'mixed_travel' in weights else 'prevent'
    ret.standardize_palettes = (get_choice('standardize_palettes') if 'standardize_palettes' in weights
                                else 'standardize')

    goal = get_choice_default('goals', default='ganon')
    if goal is not None:
        ret.goal = {'ganon': 'ganon',
                    'fast_ganon': 'crystals',
                    'dungeons': 'dungeons',
                    'pedestal': 'pedestal',
                    'triforce-hunt': 'triforcehunt',
                    'trinity': 'trinity',
                    'ganonhunt': 'ganonhunt',
                    'completionist': 'completionist'
                    }[goal]

    ret.openpyramid = get_choice('open_pyramid') if 'open_pyramid' in weights else 'auto'

    ret.shuffleganon = get_choice_bool('shuffleganon')
    ret.shufflelinks = get_choice_bool('shufflelinks')
    ret.shuffletavern = get_choice_bool('shuffletavern')
    ret.skullwoods = get_choice('skullwoods')
    ret.linked_drops = get_choice('linked_drops')
    
    ret.crystals_gt = get_choice('tower_open')
    ret.crystals_ganon = get_choice('ganon_open')

    ret.triforce_pool = get_choice_default('triforce_pool', default=0)
    ret.triforce_goal = get_choice_default('triforce_goal', default=0)
    ret.triforce_pool_min = get_choice_default('triforce_pool_min', default=0)
    ret.triforce_pool_max = get_choice_default('triforce_pool_max', default=0)
    ret.triforce_goal_min = get_choice_default('triforce_goal_min', default=0)
    ret.triforce_goal_max = get_choice_default('triforce_goal_max', default=0)
    ret.triforce_min_difference = get_choice_default('triforce_min_difference', default=0)
    ret.triforce_max_difference = get_choice_default('triforce_max_difference', default=10000)

    ret.mode = get_choice('world_state')
    if ret.mode == 'retro':
        ret.mode = 'open'
        ret.retro = True
    ret.retro = get_choice_bool('retro')  # this overrides world_state if used
    ret.take_any = get_choice_default('take_any', default='none')

    ret.bombbag = get_choice_bool('bombbag')

    ret.hints = get_choice_bool('hints')

    swords = get_choice('weapons')
    if swords is not None:
        ret.swords = {'randomized': 'random',
                      'assured': 'assured',
                      'vanilla': 'vanilla',
                      'swordless': 'swordless'
                      }[swords]

    ret.difficulty = get_choice('item_pool')
    ret.flute_mode = get_choice_default('flute_mode', default='normal')
    ret.bow_mode = get_choice_default('bow_mode', default='progressive')

    ret.item_functionality = get_choice('item_functionality')

    old_style_bosses = {'basic': 'simple',
                        'normal': 'full',
                        'chaos': 'random'}
    boss_choice = get_choice('boss_shuffle')
    if boss_choice in old_style_bosses.keys():
        boss_choice = old_style_bosses[boss_choice]
    ret.shufflebosses = boss_choice

    enemy_choice = get_choice('enemy_shuffle')
    if enemy_choice == 'chaos':
        enemy_choice = 'random'
    ret.shuffleenemies = enemy_choice

    old_style_damage = {'none': 'default',
                        'chaos': 'random'}
    damage_choice = get_choice('enemy_damage')
    if damage_choice in old_style_damage:
        damage_choice = old_style_damage[damage_choice]
    ret.enemy_damage = damage_choice

    ret.enemy_health = get_choice('enemy_health')
    ret.any_enemy_logic = get_choice('any_enemy_logic')

    ret.beemizer = get_choice('beemizer') if 'beemizer' in weights else '0'

    inventoryweights = weights.get('startinventory', {})
    startitems = []
    for item in inventoryweights.keys():
        if get_choice(item, inventoryweights) == 'on':
            startitems.append(item)
    ret.startinventory = ','.join(startitems)
    if len(startitems) > 0:
        ret.usestartinventory = True

    if 'rom' in weights:
        romweights = weights['rom']
        ret.sprite = get_choice('sprite', romweights)
        ret.triforce_gfx = get_choice('triforce_gfx', romweights)
        ret.disablemusic = get_choice_bool('disablemusic', romweights)
        ret.quickswap = get_choice_bool('quickswap', romweights)
        ret.reduce_flashing = get_choice_bool('reduce_flashing', romweights)
        ret.fastmenu = get_choice('menuspeed', romweights)
        ret.heartcolor = get_choice('heartcolor', romweights)
        ret.heartbeep = get_choice_non_bool('heartbeep', romweights)
        ret.ow_palettes = get_choice('ow_palettes', romweights)
        ret.uw_palettes = get_choice('uw_palettes', romweights)
        ret.shuffle_sfx = get_choice_bool('shuffle_sfx', romweights)
        ret.shuffle_sfxinstruments = get_choice_bool('shuffle_sfxinstruments', romweights)
        ret.shuffle_songinstruments = get_choice_bool('shuffle_songinstruments', romweights)
        ret.msu_resume = get_choice_bool('msu_resume', romweights)

    return ret
