from dataclasses import dataclass

import os.path
import typing
import logging
from Options import Choice, Toggle, DefaultOnToggle, Range, FreeText, PerGameCommonOptions, OptionGroup, Removed
from collections import defaultdict
import Utils

DefaultOffToggle = Toggle

logger = logging.getLogger("Link's Awakening Logger")


class LADXROption:
    def to_ladxr_option(self, all_options):
        if not self.ladxr_name:
            return None, None

        return (self.ladxr_name, self.name_lookup[self.value].replace("_", ""))


class Logic(Choice, LADXROption):
    """
    Affects where items are allowed to be placed.

    **Normal:** Playable without using any tricks or glitches. Can require
    knowledge from a vanilla playthrough, such as how to open Color Dungeon.

    **Hard:** More advanced techniques may be required, but glitches are not.
    Examples include tricky jumps, killing enemies with only pots.

    **Glitched:** Advanced glitches and techniques may be required, but
    extremely difficult or tedious tricks are not required. Examples include
    Bomb Triggers, Super Jumps and Jesus Jumps.

    **Hell:** Obscure knowledge and hard techniques may be required. Examples
    include featherless jumping with boots and/or hookshot, sequential pit
    buffers and unclipped superjumps. Things in here can be extremely hard to do
    or very time consuming.
    """
    display_name = "Logic"
    rich_text_doc = True
    ladxr_name = "logic"
    # option_casual = 0
    option_normal = 1
    option_hard = 2
    option_glitched = 3
    option_hell = 4

    default = option_normal


class TradeQuest(DefaultOffToggle, LADXROption):
    """
    Trade quest items are randomized. Each NPC takes its normal trade quest
    item and gives a randomized item in return.
    """
    display_name = "Trade Quest"
    ladxr_name = "tradequest"


class TextShuffle(DefaultOffToggle):
    """
    Shuffles all text in the game.
    """
    display_name = "Text Shuffle"


class Rooster(DefaultOnToggle, LADXROption):
    """
    Adds the rooster to the item pool. If disabled, the overworld will be
    modified so that any location requiring the rooster is accessible by other
    means.
    """
    display_name = "Rooster"
    ladxr_name = "rooster"


class EntranceShuffle(Choice, LADXROption):
    """
    Randomizes where overworld entrances lead.

    **Simple:** Single-entrance caves/houses that have items are shuffled
    amongst each other.

    If *Dungeon Shuffle* is enabled, then dungeons will be shuffled with all the
    non-connector entrances in the pool. Note, some entrances can lead into water, use
    the warp-to-home from the save&quit menu to escape this.
    """

    # [Advanced] Simple, but two-way connector caves are shuffled in their own pool as well.
    # [Expert] Advanced, but caves/houses without items are also shuffled into the Simple entrance pool.
    # [Insanity] Expert, but the Raft Minigame hut and Mamu's cave are added to the non-connector pool.

    option_none = 0
    option_simple = 1
    # option_advanced = 2
    # option_expert = 3
    # option_insanity = 4
    default = option_none
    display_name = "Entrance Shuffle"
    ladxr_name = "entranceshuffle"
    rich_text_doc = True


class DungeonShuffle(DefaultOffToggle, LADXROption):
    """
    Randomizes dungeon entrances with each other.
    """
    display_name = "Dungeon Shuffle"
    ladxr_name = "dungeonshuffle"


class APTitleScreen(DefaultOnToggle):
    """
    Enables AP specific title screen and disables the intro cutscene.
    """
    display_name = "AP Title Screen"


class BossShuffle(Choice):
    display_name = "Boss Shuffle"
    none = 0
    shuffle = 1
    random = 2
    default = none


class DungeonItemShuffle(Choice):
    display_name = "Dungeon Item Shuffle"
    rich_text_doc = True
    option_original_dungeon = 0
    option_own_dungeons = 1
    option_own_world = 2
    option_any_world = 3
    option_different_world = 4
    # option_delete = 5
    # option_start_with = 6
    alias_true = 3
    alias_false = 0
    ladxr_item: str


class ShuffleNightmareKeys(DungeonItemShuffle):
    """
    **Original Dungeon:** The item will be within its original dungeon.

    **Own Dungeons:** The item will be within a dungeon in your world.

    **Own World:** The item will be somewhere in your world.

    **Any World:** The item could be anywhere.

    **Different World:** The item will be somewhere in another world.
    """
    display_name = "Shuffle Nightmare Keys"
    ladxr_item = "NIGHTMARE_KEY"


class ShuffleSmallKeys(DungeonItemShuffle):
    """
    **Original Dungeon:** The item will be within its original dungeon.

    **Own Dungeons:** The item will be within a dungeon in your world.

    **Own World:** The item will be somewhere in your world.

    **Any World:** The item could be anywhere.

    **Different World:** The item will be somewhere in another world.
    """
    display_name = "Shuffle Small Keys"
    ladxr_item = "KEY"


class ShuffleMaps(DungeonItemShuffle):
    """
    **Original Dungeon:** The item will be within its original dungeon.

    **Own Dungeons:** The item will be within a dungeon in your world.

    **Own World:** The item will be somewhere in your world.

    **Any World:** The item could be anywhere.

    **Different World:** The item will be somewhere in another world.
    """
    display_name = "Shuffle Maps"
    ladxr_item = "MAP"


class ShuffleCompasses(DungeonItemShuffle):
    """
    **Original Dungeon:** The item will be within its original dungeon.

    **Own Dungeons:** The item will be within a dungeon in your world.

    **Own World:** The item will be somewhere in your world.

    **Any World:** The item could be anywhere.

    **Different World:** The item will be somewhere in another world.
    """
    display_name = "Shuffle Compasses"
    ladxr_item = "COMPASS"


class ShuffleStoneBeaks(DungeonItemShuffle):
    """
    **Original Dungeon:** The item will be within its original dungeon.

    **Own Dungeons:** The item will be within a dungeon in your world.

    **Own World:** The item will be somewhere in your world.

    **Any World:** The item could be anywhere.

    **Different World:** The item will be somewhere in another world.
    """
    display_name = "Shuffle Stone Beaks"
    ladxr_item = "STONE_BEAK"


class ShuffleInstruments(DungeonItemShuffle):
    """
    **Original Dungeon:** The item will be within its original dungeon.

    **Own Dungeons:** The item will be within a dungeon in your world.

    **Own World:** The item will be somewhere in your world.

    **Any World:** The item could be anywhere.

    **Different World:** The item will be somewhere in another world.

    **Vanilla:** The item will be in its vanilla location in your world.
    """
    display_name = "Shuffle Instruments"
    ladxr_item = "INSTRUMENT"
    default = 100
    option_vanilla = 100
    alias_false = 100


class Goal(Choice, LADXROption):
    """
    The Goal of the game.

    **Instruments:** The Wind Fish's Egg will only open if you have the required
    number of Instruments of the Sirens, and play the Ballad of the Wind Fish.

    **Seashells:** The Egg will open when you bring 20 seashells. The Ballad and
    Ocarina are not needed.

    **Open:** The Egg will start pre-opened.
    """
    display_name = "Goal"
    rich_text_doc = True
    ladxr_name = "goal"
    option_instruments = 1
    option_seashells = 2
    option_open = 3

    default = option_instruments

    def to_ladxr_option(self, all_options):
        if self.value == self.option_instruments:
            return ("goal", all_options["instrument_count"])
        else:
            return LADXROption.to_ladxr_option(self, all_options)


class InstrumentCount(Range, LADXROption):
    """
    Sets the number of instruments required to open the Egg.
    """
    display_name = "Instrument Count"
    ladxr_name = None
    range_start = 0
    range_end = 8
    default = 8


class NagMessages(DefaultOffToggle, LADXROption):
    """
    Controls if nag messages are shown when rocks and crystals are touched.
    Useful for glitches, annoying for everything else.
    """
    display_name = "Nag Messages"
    ladxr_name = "nagmessages"


class MusicChangeCondition(Choice):
    """
    Controls how the music changes.

    **Sword:** When you pick up a sword, the music changes.

    **Always:** You always have the post-sword music.
    """
    display_name = "Music Change Condition"
    rich_text_doc = True
    option_sword = 0
    option_always = 1
    default = option_always


class HardMode(Choice, LADXROption):
    """
    **Oracle:** Less iframes and health from drops. Bombs damage yourself. Water
    damages you without flippers. No pieces of power or acorns.

    **Hero:** Switch version hero mode, double damage, no heart/fairy drops.

    **OHKO:** You die on a single hit, always.
    """
    display_name = "Hard Mode"
    ladxr_name = "hardmode"
    rich_text_doc = True
    option_none = 0
    option_oracle = 1
    option_hero = 2
    option_ohko = 3
    default = option_none


class Overworld(Choice, LADXROption):
    """
    **Open Mabe:** Replaces rock on the east side of Mabe Village with bushes,
    allowing access to Ukuku Prairie without Power Bracelet.
    """
    display_name = "Overworld"
    ladxr_name = "overworld"
    rich_text_doc = True
    option_normal = 0
    option_open_mabe = 1
    default = option_normal


class Quickswap(Choice, LADXROption):
    """
    Instead of opening the map, the *SELECT* button swaps the top item of your inventory on to your *A* or *B* button.
    """
    display_name = "Quickswap"
    ladxr_name = "quickswap"
    rich_text_doc = True
    option_none = 0
    option_a = 1
    option_b = 2
    default = option_none


class TextMode(Choice, LADXROption):
    """
    **Fast:** Makes text appear twice as fast.
    """
    display_name = "Text Mode"
    ladxr_name = "textmode"
    rich_text_doc = True
    option_normal = 0
    option_fast = 1
    default = option_fast


class LowHpBeep(Choice, LADXROption):
    """
    Slows or disables the low health beeping sound.
    """
    display_name = "Low HP Beep"
    ladxr_name = "lowhpbeep"
    option_default = 0
    option_slow = 1
    option_none = 2
    default = option_default


class NoFlash(DefaultOnToggle, LADXROption):
    """
    Remove the flashing light effects from Mamu, shopkeeper and MadBatter.
    Useful for capture cards and people that are sensitive to these things.
    """
    display_name = "No Flash"
    ladxr_name = "noflash"


class BootsControls(Choice):
    """
    Adds an additional button to activate Pegasus Boots (does nothing if you
    haven't picked up your boots!)

    **Vanilla:** Nothing changes, you have to equip the boots to use them.

    **Bracelet:** Holding down the button for the bracelet also activates boots
    (somewhat like Link to the Past).

    **Press A:** Holding down A activates boots.

    **Press B:** Holding down B activates boots.
    """
    display_name = "Boots Controls"
    rich_text_doc = True
    option_vanilla = 0
    option_bracelet = 1
    option_press_a = 2
    alias_a = 2
    option_press_b = 3
    alias_b = 3


class LinkPalette(Choice, LADXROption):
    """
    Sets Link's palette.

    A-D are color palettes usually used during the damage animation and can
    change based on where you are.
    """
    display_name = "Link's Palette"
    ladxr_name = "linkspalette"
    option_normal = -1
    option_green = 0
    option_yellow = 1
    option_red = 2
    option_blue = 3
    option_invert_a = 4
    option_invert_b = 5
    option_invert_c = 6
    option_invert_d = 7
    default = option_normal

    def to_ladxr_option(self, all_options):
        return self.ladxr_name, str(self.value)


class TrendyGame(Choice):
    """
    **Easy:** All of the items hold still for you.

    **Normal:** The vanilla behavior.

    **Hard:** The trade item also moves.

    **Harder:** The items move faster.

    **Hardest:** The items move diagonally.

    **Impossible:** The items move impossibly fast, may scroll on and off the
    screen.
    """
    display_name = "Trendy Game"
    rich_text_doc = True
    option_easy = 0
    option_normal = 1
    option_hard = 2
    option_harder = 3
    option_hardest = 4
    option_impossible = 5
    default = option_normal


class GfxMod(DefaultOffToggle):
    """
    If enabled, the patcher will prompt the user for a modification file to change sprites in the game and optionally some text.
    """
    display_name = "GFX Modification"


class Palette(Choice):
    """
    Sets the palette for the game.

    Note: A few places aren't patched, such as the menu and a few color dungeon
    tiles.

    **Normal:** The vanilla palette.

    **1-Bit:** One bit of color per channel.

    **2-Bit:** Two bits of color per channel.

    **Greyscale:** Shades of grey.

    **Pink:** Aesthetic.

    **Inverted:** Inverted.
    """
    display_name = "Palette"
    rich_text_doc = True
    option_normal = 0
    option_1bit = 1
    option_2bit = 2
    option_greyscale = 3
    option_pink = 4
    option_inverted = 5


class Music(Choice, LADXROption):
    """
    **Vanilla:** Regular Music

    **Shuffled:** Shuffled Music

    **Off:** No music
    """
    display_name = "Music"
    ladxr_name = "music"
    rich_text_doc = True
    option_vanilla = 0
    option_shuffled = 1
    option_off = 2

    def to_ladxr_option(self, all_options):
        s = ""
        if self.value == self.option_shuffled:
            s = "random"
        elif self.value == self.option_off:
            s = "off"
        return self.ladxr_name, s


class Warps(Choice):
    """
    **Improved:** Adds remake style warp screen to the game. Choose your warp
    destination on the map after jumping in a portal and press *B* to select.

    **Improved Additional:** Improved warps, and adds a warp point at Crazy
    Tracy's house (the Mambo teleport spot) and Eagle's Tower.
    """
    display_name = "Warps"
    rich_text_doc = True
    option_vanilla = 0
    option_improved = 1
    option_improved_additional = 2
    default = option_vanilla


class InGameHints(DefaultOnToggle):
    """
    When enabled, owl statues and library books may indicate the location of
    your items in the multiworld.
    """
    display_name = "In-game Hints"


class TarinsGift(Choice):
    """
    **Local Progression:** Forces Tarin's gift to be an item that immediately
    opens up local checks. Has little effect in single player games, and isn't
    always necessary with randomized entrances.

    **Bush Breaker:** Forces Tarin's gift to be an item that can destroy bushes.

    **Any Item:** Tarin's gift can be any item for any world
    """
    display_name = "Tarin's Gift"
    rich_text_doc = True
    option_local_progression = 0
    option_bush_breaker = 1
    option_any_item = 2
    default = option_local_progression


class StabilizeItemPool(DefaultOffToggle):
    """
    By default, some rupees in the item pool are randomly swapped with bombs,
    arrows, powders, or capacity upgrades. This set of items is also used as
    filler. This option disables that swapping and makes *Nothing* the filler
    item.
    """
    display_name = "Stabilize Item Pool"
    rich_text_doc = True


class ForeignItemIcons(Choice):
    """
    Choose how to display foreign items.

    **Guess By Name:** Foreign items can look like any Link's Awakening item.

    **Indicate Progression:** Foreign items are either a Piece of Power
    (progression) or Guardian Acorn (non-progression).
    """
    display_name = "Foreign Item Icons"
    rich_text_doc = True
    option_guess_by_name = 0
    option_indicate_progression = 1
    default = option_guess_by_name


ladx_option_groups = [
    OptionGroup("Gameplay Adjustments", [
        InGameHints,
        TarinsGift,
        HardMode,
        TrendyGame,
    ]),
    OptionGroup("World Layout", [
        Overworld,
        Warps,
        DungeonShuffle,
        EntranceShuffle,
    ]),
    OptionGroup("Item Pool", [
        ShuffleInstruments,
        ShuffleNightmareKeys,
        ShuffleSmallKeys,
        ShuffleMaps,
        ShuffleCompasses,
        ShuffleStoneBeaks,
        TradeQuest,
        Rooster,
        StabilizeItemPool,
    ]),
    OptionGroup("Quality of Life & Aesthetic", [
        NagMessages,
        Quickswap,
        BootsControls,
        ForeignItemIcons,
        GfxMod,
        LinkPalette,
        Palette,
        APTitleScreen,
        TextShuffle,
        TextMode,
        Music,
        MusicChangeCondition,
        LowHpBeep,
        NoFlash,
    ])
]

@dataclass
class LinksAwakeningOptions(PerGameCommonOptions):
    logic: Logic
    tradequest: TradeQuest
    rooster: Rooster
    experimental_dungeon_shuffle: DungeonShuffle
    experimental_entrance_shuffle: EntranceShuffle
    goal: Goal
    instrument_count: InstrumentCount
    link_palette: LinkPalette
    warps: Warps
    trendy_game: TrendyGame
    gfxmod: GfxMod
    palette: Palette
    text_shuffle: TextShuffle
    foreign_item_icons: ForeignItemIcons
    shuffle_nightmare_keys: ShuffleNightmareKeys
    shuffle_small_keys: ShuffleSmallKeys
    shuffle_maps: ShuffleMaps
    shuffle_compasses: ShuffleCompasses
    shuffle_stone_beaks: ShuffleStoneBeaks
    music: Music
    shuffle_instruments: ShuffleInstruments
    music_change_condition: MusicChangeCondition
    nag_messages: NagMessages
    ap_title_screen: APTitleScreen
    boots_controls: BootsControls
    quickswap: Quickswap
    hard_mode: HardMode
    low_hp_beep: LowHpBeep
    text_mode: TextMode
    no_flash: NoFlash
    in_game_hints: InGameHints
    tarins_gift: TarinsGift
    overworld: Overworld
    stabilize_item_pool: StabilizeItemPool

    warp_improvements: Removed
    additional_warp_points: Removed
