"""Vogon ship locations -- Hold, Captain's Quarters, Airlock."""

import random

from h2g2.engine.game_object import (
    GameObject, Room, Flag, Direction,
    DirectExit, ConditionalExit, BlockedExit,
)
from h2g2.engine.world import World
from h2g2.engine.state import GameState


# ---- Gibberish generator ----

_VOGON_SYLLABLES = [
    "blurp", "grun", "flib", "morgh", "splut", "vrunt", "glub", "snarg",
    "plod", "quork", "bleg", "frot", "nargh", "sklurb", "thrip", "zunt",
    "drob", "munt", "flob", "grak", "prunt", "vlib", "skrog", "blort",
]

_VOGON_SUFFIXES = [
    "ulous", "ting", "ment", "ish", "wards", "oid", "esque", "ble",
    "ness", "ling", "osity", "ated", "ingly", "ation",
]


def _gibberish_word() -> str:
    """Generate a random Vogon-sounding gibberish word."""
    base = random.choice(_VOGON_SYLLABLES)
    if random.random() > 0.5:
        base += random.choice(_VOGON_SYLLABLES)[:3]
    base += random.choice(_VOGON_SUFFIXES)
    return base


def _gibberish_line() -> str:
    """Generate a full line of Vogon gibberish."""
    words = [_gibberish_word() for _ in range(random.randint(4, 8))]
    line = " ".join(words)
    return f'"{line.capitalize()}!"'


# ---- Poetry text ----

POETRY_LINES = [
    '"Oh freddled gruntbuggly, thy nacturations are to me!"',
    '"As plurdled gabbleblotchits on a lurgid bee."',
    '"Groop I implore thee, my foonting turlingdromes."',
    '"And hooptiously drangle me with crinkly bindlewurdles,"',
    '"or I will rend thee in the gobberwarts with my blurglecruncheon, see if I don\'t!"',
]

POETRY_VERSE_2 = [
    '"Fripping lyshus wimbgunts, awhilst moongrovenly kormzibs."',
    '"Gashee morphousite, thou expungiest quoopisk!"',
    '"Bleem miserable venchit! Bleem forever mestinglish asunder frapt."',
]


# ---- Object action handlers ----

def _inner_door_action(state: GameState) -> bool:
    out = state.output
    door = state.world.get("INNER-DOOR")

    if state.prsa == "open":
        if door.fset_q(Flag.OPENBIT):
            out.tell("The inner airlock door is already open.\n")
        else:
            door.fset(Flag.OPENBIT)
            out.tell("You open the inner airlock door.\n")
        return True

    if state.prsa == "close":
        if not door.fset_q(Flag.OPENBIT):
            out.tell("It's already closed.\n")
        else:
            door.fclear(Flag.OPENBIT)
            out.tell("You close the inner airlock door.\n")
        return True

    if state.prsa == "examine":
        status = "open" if door.fset_q(Flag.OPENBIT) else "closed"
        out.tell(f"A heavy inner airlock door. It is {status}.\n")
        return True

    return False


def _airlock_obj_action(state: GameState) -> bool:
    out = state.output
    if state.prsa == "examine":
        out.tell(
            "The airlock is a standard Vogon design: built to eject "
            "unwanted hitchhikers into the vacuum of space.\n"
        )
        return True
    return False


def _dispenser_action(state: GameState) -> bool:
    out = state.output
    if state.prsa == "examine":
        out.tell(
            "The dispenser has a button, and a small sign that reads "
            '"Babel Fish." A small hole at the bottom dispenses the fish.\n'
        )
        return True
    return False


def _dispenser_button_action(state: GameState) -> bool:
    out = state.output

    if state.prsa not in ("push", "press"):
        return False

    fish_counter = state.flags.get("fish_counter", 5)

    if state.flags.get("babel_fish_in_ear"):
        out.tell("You already have a babel fish in your ear.\n")
        return True

    if fish_counter <= 0:
        out.tell(
            "The dispenser makes a grinding noise, but nothing comes "
            "out. It appears to be empty.\n"
        )
        return True

    state.flags["fish_counter"] = fish_counter - 1

    # Check puzzle conditions
    gown_hung = state.flags.get("gown_hung", False)
    towel_on_drain = state.flags.get("towel_on_drain", False)
    panel_blocker = state.flags.get("panel_blocker")
    item_on_satchel = state.flags.get("item_on_satchel")

    if not gown_hung:
        out.tell(
            "A small babel fish shoots out of the dispenser, arcs through "
            "the air, and vanishes down a drain in the floor.\n"
        )
        return True

    if not towel_on_drain:
        out.tell(
            "A small babel fish shoots out of the dispenser, bounces off "
            "the dressing gown hanging on the hook, and vanishes down a "
            "drain in the floor.\n"
        )
        return True

    if panel_blocker is None:
        out.tell(
            "A small babel fish shoots out of the dispenser, bounces off "
            "the dressing gown, slides across the towel covering the "
            "drain, and is scooped up by a small cleaning robot that "
            "emerges from a panel in the wall. The robot disappears back "
            "into the wall.\n"
        )
        return True

    if item_on_satchel is None:
        out.tell(
            "A small babel fish shoots out of the dispenser, bounces off "
            "the dressing gown, slides across the towel, hits the satchel "
            "blocking the robot panel, and flies upward -- straight into "
            "the upper reaches of the room and out of sight.\n"
        )
        return True

    # Success!
    out.tell(
        "A small babel fish shoots out of the dispenser, bounces off "
        "the dressing gown, slides across the towel, hits the satchel "
        "blocking the robot panel, flies upward, bounces off the junk "
        "mail balanced on the satchel, and lands neatly in your ear.\n\n"
        "You can suddenly understand everything around you. The babel "
        "fish is now happily nestled in your ear.\n"
    )
    state.flags["babel_fish_in_ear"] = True
    state.score += 12
    return True


def _hook_action(state: GameState) -> bool:
    out = state.output

    if state.prsa == "examine":
        if state.flags.get("gown_hung"):
            out.tell("Your gown is hanging on the hook.\n")
        else:
            out.tell("A small hook on the wall opposite the dispenser.\n")
        return True

    if state.prsa in ("hang", "put"):
        gown = state.world.objects.get("GOWN")
        if state.prso is gown:
            if not gown.is_held_by(state.protagonist):
                out.tell("You're not carrying the gown.\n")
                return True
            state.flags["gown_hung"] = True
            gown.move_to(state.world.local_globals)
            out.tell("You hang the gown on the hook.\n")
            return True

    return False


def _drain_action(state: GameState) -> bool:
    out = state.output

    if state.prsa == "examine":
        if state.flags.get("towel_on_drain"):
            out.tell("The drain is covered by a towel.\n")
        else:
            out.tell("A small drain grate in the floor.\n")
        return True

    if state.prsa in ("cover", "put", "block"):
        towel = state.world.objects.get("TOWEL")
        if state.prso is towel:
            if not towel.is_held_by(state.protagonist):
                out.tell("You're not carrying the towel.\n")
                return True
            state.flags["towel_on_drain"] = True
            towel.move_to(state.world.local_globals)
            out.tell("You cover the drain with the towel.\n")
            return True

    return False


def _robot_panel_action(state: GameState) -> bool:
    out = state.output

    if state.prsa == "examine":
        blocker = state.flags.get("panel_blocker")
        if blocker:
            out.tell(f"The robot panel is blocked by {blocker.the_desc()}.\n")
        else:
            out.tell("A small panel in the wall from which a cleaning robot emerges.\n")
        return True

    if state.prsa in ("put", "block", "cover"):
        satchel = state.world.objects.get("SATCHEL")
        if state.prso is satchel:
            if not satchel.is_held_by(state.protagonist):
                out.tell("You're not carrying the satchel.\n")
                return True
            state.flags["panel_blocker"] = satchel
            satchel.move_to(state.world.local_globals)
            out.tell("You put the satchel in front of the robot panel.\n")
            return True

    return False


def _babel_fish_action(state: GameState) -> bool:
    out = state.output
    if state.prsa == "examine":
        if state.flags.get("babel_fish_in_ear"):
            out.tell(
                "The babel fish is snugly lodged in your ear, translating "
                "all alien languages into English for you.\n"
            )
        else:
            out.tell("A small, yellow, leech-like fish.\n")
        return True
    if state.prsa == "take":
        if state.flags.get("babel_fish_in_ear"):
            out.tell("It's far too comfortable in your ear to remove.\n")
            return True
    return False


def _vogon_captain_action(state: GameState) -> bool:
    out = state.output
    if state.prsa == "examine":
        out.tell(
            "The Vogon Captain is a large, slug-like creature with a "
            "foul temper and a passion for bureaucratic paperwork. And "
            "poetry. Especially poetry.\n"
        )
        return True
    if state.prsa in ("tell", "ask", "talk"):
        out.tell(
            'The Captain grunts. "Resistance is useless!" he bellows.\n'
        )
        return True
    return False


def _poetry_action(state: GameState) -> bool:
    out = state.output
    if state.prsa == "examine":
        if state.flags.get("babel_fish_in_ear"):
            out.tell(
                "It's Vogon poetry. The third worst in the known galaxy.\n"
            )
        else:
            out.tell(
                "It sounds like someone gargling with a throat full of "
                "live weasels.\n"
            )
        return True

    if state.prsa in ("enjoy", "appreciate", "like", "listen"):
        if not state.flags.get("babel_fish_in_ear"):
            out.tell(
                "You can't even understand it. How could you enjoy it?\n"
            )
            return True
        if state.flags.get("poem_enjoyed"):
            out.tell("You've already expressed your enjoyment.\n")
            return True
        state.flags["poem_enjoyed"] = True
        state.score += 15
        out.tell(
            'You nod your head approvingly. "Oh, very good," you say.\n'
            "The Captain beams with delight and continues reading.\n"
        )
        return True

    return False


def _chair_action(state: GameState) -> bool:
    out = state.output
    if state.prsa == "examine":
        out.tell(
            "The Poetry Appreciation Chair is a fiendish Vogon device "
            "that clamps you in place and forces you to listen. Steel "
            "bands hold your arms, legs, and head firmly in position.\n"
        )
        return True
    if state.prsa in ("get out", "leave", "escape", "stand"):
        out.tell(
            "The steel bands hold you firmly in the chair. You're not "
            "going anywhere until the Captain decides otherwise.\n"
        )
        return True
    return False


def _glass_case_action(state: GameState) -> bool:
    out = state.output
    case = state.world.get("GLASS-CASE")

    if state.prsa == "examine":
        if case.fset_q(Flag.OPENBIT):
            out.tell("The glass case is open.\n")
        else:
            out.tell(
                "A sealed glass case on the wall. Inside you can see a "
                "device of some sort. There is a keyboard and a small "
                "switch on the front.\n"
            )
        return True

    if state.prsa == "open":
        if case.fset_q(Flag.OPENBIT):
            out.tell("It's already open.\n")
        else:
            out.tell("It won't open by force. Try the keyboard.\n")
        return True

    return False


def _keyboard_action(state: GameState) -> bool:
    out = state.output

    if state.prsa in ("type", "use", "push"):
        case = state.world.get("GLASS-CASE")
        if case.fset_q(Flag.OPENBIT):
            out.tell("The case is already open.\n")
            return True
        # Accept any typing action -- the correct answer is typing a
        # word from the poem while you understand it
        if state.flags.get("babel_fish_in_ear") and state.flags.get("poem_enjoyed"):
            case.fset(Flag.OPENBIT)
            state.score += 25
            out.tell(
                "You type a word from the poem on the keyboard. The case "
                "makes a satisfying click and swings open, revealing an "
                "atomic vector plotter.\n"
            )
            return True
        out.tell(
            "You type something on the keyboard. Nothing happens. "
            "Perhaps you need to type the right thing.\n"
        )
        return True

    if state.prsa == "examine":
        out.tell("A small keyboard attached to the glass case.\n")
        return True

    return False


def _switch_action(state: GameState) -> bool:
    out = state.output
    if state.prsa in ("push", "press", "flip", "turn on", "turn off"):
        out.tell("Click. Nothing obvious happens.\n")
        return True
    if state.prsa == "examine":
        out.tell("A small switch on the front of the glass case.\n")
        return True
    return False


def _plotter_action(state: GameState) -> bool:
    out = state.output
    case = state.world.get("GLASS-CASE")

    if state.prsa == "take":
        if not case.fset_q(Flag.OPENBIT):
            out.tell("The plotter is sealed inside the glass case.\n")
            return True
        plotter = state.world.get("PLOTTER")
        plotter.move_to(state.protagonist)
        out.tell("You take the atomic vector plotter.\n")
        return True

    if state.prsa == "examine":
        out.tell(
            "It's an atomic vector plotter -- a small, sleek device "
            "of obviously advanced technology.\n"
        )
        return True

    return False


def _satchel_action(state: GameState) -> bool:
    out = state.output
    satchel = state.world.get("SATCHEL")

    if state.prsa == "examine":
        out.tell("It's Ford's battered leather satchel.\n")
        return True

    if state.prsa in ("open", "look in"):
        visible = satchel.contents_string()
        if visible:
            descs = [obj.a_desc() for obj in visible]
            out.tell("The satchel contains " + ", ".join(descs) + ".\n")
        else:
            out.tell("The satchel is empty.\n")
        return True

    return False


def _towel_action(state: GameState) -> bool:
    out = state.output
    if state.prsa == "examine":
        out.tell(
            "It's a large, fluffy towel. About the most massively "
            "useful thing an interstellar hitchhiker can have.\n"
        )
        return True
    return False


def _guide_action(state: GameState) -> bool:
    out = state.output
    if state.prsa in ("read", "examine", "consult"):
        out.tell(
            "The cover of the Guide reads, in large friendly letters: "
            '"DON\'T PANIC"\n\n'
            "The Guide has a lot to say on many subjects. It is the "
            "standard repository of all knowledge and wisdom.\n"
        )
        return True
    return False


def _peanuts_action(state: GameState) -> bool:
    out = state.output
    if state.prsa in ("eat", "open"):
        out.tell(
            "You eat the peanuts. They were rather good, as peanuts go.\n"
        )
        state.world.get("PEANUTS").move_to(state.world.local_globals)
        return True
    if state.prsa == "examine":
        out.tell("A small packet of salted peanuts.\n")
        return True
    return False


def _ford_vogon_action(state: GameState) -> bool:
    out = state.output
    if state.prsa == "examine":
        if state.flags.get("ford_sleeping"):
            out.tell("Ford is slumped against the wall, fast asleep.\n")
        else:
            out.tell(
                "Ford looks somewhat the worse for wear, but his eyes "
                "are sharp and alert.\n"
            )
        return True
    if state.prsa in ("tell", "ask", "talk"):
        if state.flags.get("ford_sleeping"):
            out.tell("Ford is asleep and doesn't respond.\n")
        else:
            out.tell('"Don\'t panic," says Ford.\n')
        return True
    if state.prsa == "wake":
        if state.flags.get("ford_sleeping"):
            out.tell("Ford grunts and rolls over but doesn't wake.\n")
        else:
            out.tell("Ford is already awake.\n")
        return True
    return False


# ---- Timed event handlers ----

def _i_groggy(state: GameState) -> bool:
    """Groggy countdown -- player must act before passing out."""
    out = state.output
    counter = state.flags.get("groggy_counter", 0)

    if not state.flags.get("groggy"):
        return False

    if state.here and state.here.id != "HOLD":
        return False

    counter += 1
    state.flags["groggy_counter"] = counter

    if counter == 1:
        out.tell(
            "\nYour head is spinning. You feel very groggy and confused.\n"
        )
        return True
    if counter == 2:
        out.tell(
            "\nThe room lurches sickeningly. You really don't feel well.\n"
        )
        return True
    if counter >= 3:
        state.jigs_up(
            "The room spins wildly and you pass out on the filthy floor. "
            "You are woken some time later by a Vogon guard who drags "
            "you off and throws you out of an airlock."
        )
        return True

    return False


def _i_ford(state: GameState) -> bool:
    """Ford gives the player the Guide and goes to sleep."""
    out = state.output

    if state.here and state.here.id != "HOLD":
        return False

    ford = state.world.get("FORD-VOGON")
    guide = state.world.get("GUIDE")
    satchel = state.world.get("SATCHEL")
    towel = state.world.get("TOWEL")

    out.tell(
        '\nFord rummages through his satchel. "Here," he says, handing '
        'you a battered book. "The Hitchhiker\'s Guide to the Galaxy. '
        "It's the most remarkable book ever to come out of the great "
        'publishing corporations of Ursa Minor."\n\n'
        "He also hands you a towel.\n\n"
        '"A towel," says Ford, "is about the most massively useful thing '
        'an interstellar hitchhiker can have."\n\n'
        "With that, Ford slumps against the wall and falls asleep.\n"
    )
    guide.move_to(state.protagonist)
    towel.move_to(state.protagonist)
    satchel.move_to(state.here)
    state.flags["ford_sleeping"] = True
    return True


def _i_announcement(state: GameState) -> bool:
    """Intercom announcement."""
    out = state.output

    if state.here and state.here.id != "HOLD":
        return False

    out.tell(
        "\nThere is a crackle from the intercom. A Vogon voice "
        'announces: "Attention. This is your Captain speaking. We will '
        "shortly be arriving at our destination. Passengers are reminded "
        'that the airlock is NOT an emergency exit."\n'
    )
    return True


def _i_guards(state: GameState) -> bool:
    """Guards arrive to take you to the Captain."""
    out = state.output

    if state.here and state.here.id != "HOLD":
        return False

    out.tell(
        "\nThe door bursts open. Two Vogon guards stomp in, grab you "
        "and Ford by your collars, and drag you down the corridor to "
        "the Captain's quarters.\n"
    )
    # Move player and Ford to the Captain's quarters
    captains_quarters = state.world.get_room("CAPTAINS-QUARTERS")
    state.here = captains_quarters
    state.protagonist.move_to(captains_quarters)
    ford = state.world.get("FORD-VOGON")
    ford.move_to(captains_quarters)
    state.flags["ford_sleeping"] = False

    # Start the captain sequence
    state.flags["captain_counter"] = 0
    state.clock.queue(_i_captain, -1, name="I-CAPTAIN")

    return True


def _i_captain(state: GameState) -> bool:
    """Poetry reading sequence in the Captain's quarters."""
    out = state.output

    if state.here and state.here.id != "CAPTAINS-QUARTERS":
        return False

    counter = state.flags.get("captain_counter", 0)
    state.flags["captain_counter"] = counter + 1
    has_fish = state.flags.get("babel_fish_in_ear", False)

    if counter == 0:
        out.tell(
            '\nThe Vogon Captain clears his throat. "I\'m now going to '
            "read you some of my poetry,\" he announces.\n"
        )
        return True

    # Read poetry lines (counter 1-5)
    if 1 <= counter <= 5:
        line_idx = counter - 1
        if has_fish:
            line = POETRY_LINES[line_idx]
        else:
            line = _gibberish_line()
        out.tell(f"\nThe Captain reads: {line}\n")
        if counter == 5:
            if has_fish:
                out.tell(
                    "\nThe Captain pauses and looks at you expectantly.\n"
                )
            else:
                out.tell(
                    "\nThe Captain finishes. It sounded like someone "
                    "gargling with quicksand.\n"
                )
        return True

    # After the first verse
    if counter == 6:
        enjoyed = state.flags.get("poem_enjoyed", False)
        if enjoyed and has_fish:
            out.tell(
                '\nThe Captain is delighted. "You liked it! Oh how '
                "wonderful! Let me read you another!\"\n"
            )
            state.flags["captain_counter"] = 100  # jump to second verse
            return True
        else:
            _eject_to_airlock(state)
            return True

    # Second verse (counters 101-103)
    if 101 <= counter <= 103:
        line_idx = counter - 101
        if line_idx < len(POETRY_VERSE_2):
            line = POETRY_VERSE_2[line_idx]
            out.tell(f"\nThe Captain reads: {line}\n")
            if line_idx == len(POETRY_VERSE_2) - 1:
                # After second verse, eject
                out.tell(
                    '\n"Now," says the Captain, "did you enjoy that?"\n'
                    "Before you can answer, the guards grab you.\n"
                )
            return True

    if counter == 104:
        _eject_to_airlock(state)
        return True

    return False


def _eject_to_airlock(state: GameState) -> None:
    """Guards drag you from the Captain's quarters to the hold, then airlock."""
    out = state.output

    out.tell(
        "\nThe Vogon guards grab you and Ford, drag you struggling "
        "down the corridor, through the hold, and hurl you into the "
        "airlock.\n"
    )

    # Disable the captain timer
    state.clock.queue(_i_captain, 0, name="I-CAPTAIN")
    for entry in state.clock.entries:
        if entry.routine is _i_captain:
            entry.enabled = False

    # Move to airlock
    airlock = state.world.get_room("AIRLOCK")
    state.here = airlock
    state.protagonist.move_to(airlock)
    ford = state.world.get("FORD-VOGON")
    ford.move_to(airlock)

    state.flags["airlock_counter"] = 0
    state.clock.queue(_i_airlock, -1, name="I-AIRLOCK")


def _i_airlock(state: GameState) -> bool:
    """Airlock countdown -- Ford's dialogue then ejection into space."""
    out = state.output

    if state.here and state.here.id != "AIRLOCK":
        return False

    counter = state.flags.get("airlock_counter", 0)
    counter += 1
    state.flags["airlock_counter"] = counter

    if counter == 1:
        out.tell(
            '\nFord says: "I wonder if we\'ll be picked up by another '
            "ship. The chances are pretty remote, of course, but it's "
            'our only hope."\n'
        )
        return True

    if counter == 2:
        out.tell(
            "\nFord is rummaging in his pockets for something. "
            '"I had a Sub-Etha Sens-O-Matic somewhere..." he mutters.\n'
        )
        return True

    if counter == 3:
        out.tell(
            '\nFord says: "If we hold our breath, we might survive for '
            "about thirty seconds in the vacuum of space. It's not "
            'long, but it might just be enough."\n'
        )
        return True

    if counter >= 4:
        out.tell(
            "\nWith a great hiss, the outer airlock door opens. The "
            "air rushes out. You and Ford are sucked into the cold "
            "vacuum of space.\n\n"
            "You gasp. Stars wheel around you. The cold is incredible.\n"
        )

        # Disable airlock timer
        for entry in state.clock.entries:
            if entry.routine is _i_airlock:
                entry.enabled = False

        # Set heart probability for Heart of Gold sequence
        state.flags["heart_prob"] = 100

        # Transition to DARK room
        dark = state.world.rooms.get("DARK")
        if dark:
            state.here = dark
            state.protagonist.move_to(dark)
        else:
            # If DARK room doesn't exist yet, end the sequence
            out.tell(
                "\nYou drift in the void of space...\n"
                "Everything goes dark.\n"
            )
        return True

    return False


# ---- Room action handlers ----

def _hold_action(state: GameState, rarg: str) -> bool:
    out = state.output

    if rarg == "M-LOOK":
        out.tell(
            "This is a squalid room filled with grubby mattresses, "
            "unwashed cups, and unidentifiable bits of smelly alien "
            "underwear. A door lies to port, and an airlock lies to "
            "starboard.\n"
        )
        return True

    if rarg == "M-END":
        hold = state.world.get_room("HOLD")

        if hold.fset_q(Flag.REVISITBIT):
            # Revisit -- guards stun you
            state.jigs_up(
                "A Vogon guard spots you wandering around and stuns you "
                "with a blast from his kill-o-zap gun."
            )
            return True

        if not hold.fset_q(Flag.TOUCHBIT):
            # First visit
            hold.fset(Flag.TOUCHBIT)
            hold.fset(Flag.REVISITBIT)

            # Give player peanuts
            peanuts = state.world.get("PEANUTS")
            peanuts.move_to(state.protagonist)

            # Move Ford here
            ford = state.world.get("FORD-VOGON")
            ford.move_to(hold)

            # Set groggy state
            state.flags["groggy"] = True
            state.flags["groggy_counter"] = 0

            # Add score
            state.score += 8

            out.tell(
                "\nYou wake up. The room is spinning very gently round "
                "your head. Or at least it would be if you could see it, "
                "which you can't.\n\n"
                "You are lying on what feels like a pile of old mattresses "
                "in a room that smells like the inside of a pair of "
                "Vogon-ripper trainers.\n\n"
                'Ford Prefect is here. "It\'s OK," he says, "we\'re safe '
                'now. We\'re on a Vogon ship."\n\n'
                "He hands you a small packet of peanuts.\n"
            )

            # Queue timed events
            state.clock.queue(_i_groggy, 3, name="I-GROGGY")
            state.clock.queue(_i_ford, 6, name="I-FORD")
            state.clock.queue(_i_announcement, 18, name="I-ANNOUNCEMENT")
            state.clock.queue(_i_guards, 36, name="I-GUARDS")

            return True

    return False


def _hold_east_exit(state: GameState) -> "Room | None":
    """Exit from hold east to airlock -- requires inner door open."""
    out = state.output
    door = state.world.get("INNER-DOOR")
    if not door.fset_q(Flag.OPENBIT):
        out.tell("The inner airlock door is closed.\n")
        return None
    return state.world.get_room("AIRLOCK")


def _captains_quarters_action(state: GameState, rarg: str) -> bool:
    out = state.output

    if rarg == "M-LOOK":
        out.tell(
            "This is the cabin of the Vogon Captain. You and Ford are "
            "strapped into poetry appreciation chairs.\n"
        )
        return True

    return False


def _airlock_action(state: GameState, rarg: str) -> bool:
    out = state.output

    if rarg == "M-LOOK":
        out.tell(
            "This airlock has massive doors to port and starboard.\n"
        )
        return True

    return False


# ---- Registration ----

def register(world: World, state: GameState) -> None:
    """Create all Vogon ship rooms and objects."""

    # ---- Initialize state flags ----

    state.flags.setdefault("groggy", False)
    state.flags.setdefault("groggy_counter", 0)
    state.flags.setdefault("gown_hung", False)
    state.flags.setdefault("panel_blocker", None)
    state.flags.setdefault("item_on_satchel", None)
    state.flags.setdefault("fish_counter", 5)
    state.flags.setdefault("babel_fish_in_ear", False)
    state.flags.setdefault("captain_counter", 0)
    state.flags.setdefault("poem_enjoyed", False)
    state.flags.setdefault("airlock_counter", 0)
    state.flags.setdefault("guards_counter", 0)
    state.flags.setdefault("ford_sleeping", False)

    # ---- Global objects for the ship ----

    inner_door = world.register(GameObject(
        "INNER-DOOR", desc="inner door",
        synonyms=["door", "inner"],
        adjectives=["inner", "airlock"],
        flags={Flag.DOORBIT, Flag.NDESCBIT},
        action=_inner_door_action,
    ))
    inner_door.move_to(world.local_globals)

    airlock_obj = world.register(GameObject(
        "AIRLOCK-OBJ", desc="airlock",
        synonyms=["airlock"],
        flags={Flag.NDESCBIT},
        action=_airlock_obj_action,
    ))
    airlock_obj.move_to(world.local_globals)

    # ---- HOLD ----

    hold = world.register(Room(
        "HOLD", desc="Vogon Hold",
        flags={Flag.RLANDBIT, Flag.ONBIT},
        action=_hold_action,
        global_objects=[inner_door, airlock_obj],
    ))

    # Babel fish dispenser and puzzle objects
    dispenser = world.register(GameObject(
        "DISPENSER", desc="babel fish dispenser",
        synonyms=["dispenser", "machine"],
        adjectives=["babel", "fish"],
        flags={Flag.NDESCBIT},
        action=_dispenser_action,
    ))
    dispenser.move_to(hold)

    dispenser_button = world.register(GameObject(
        "DISPENSER-BUTTON", desc="dispenser button",
        synonyms=["button"],
        adjectives=["dispenser"],
        flags={Flag.NDESCBIT},
        action=_dispenser_button_action,
    ))
    dispenser_button.move_to(hold)

    hook = world.register(GameObject(
        "HOOK", desc="hook",
        synonyms=["hook", "peg"],
        adjectives=["small", "wall"],
        flags={Flag.NDESCBIT},
        action=_hook_action,
    ))
    hook.move_to(hold)

    drain = world.register(GameObject(
        "DRAIN", desc="drain",
        synonyms=["drain", "grate"],
        adjectives=["small", "floor"],
        flags={Flag.NDESCBIT},
        action=_drain_action,
    ))
    drain.move_to(hold)

    robot_panel = world.register(GameObject(
        "ROBOT-PANEL", desc="robot panel",
        synonyms=["panel"],
        adjectives=["robot", "small", "wall"],
        flags={Flag.NDESCBIT},
        action=_robot_panel_action,
    ))
    robot_panel.move_to(hold)

    babel_fish = world.register(GameObject(
        "BABEL-FISH", desc="babel fish",
        synonyms=["fish"],
        adjectives=["babel", "small", "yellow"],
        flags={Flag.NDESCBIT, Flag.INVISIBLE},
        action=_babel_fish_action,
    ))
    babel_fish.move_to(world.local_globals)

    # Glass case with plotter
    glass_case = world.register(GameObject(
        "GLASS-CASE", desc="glass case",
        synonyms=["case"],
        adjectives=["glass", "sealed"],
        flags={Flag.CONTBIT, Flag.NDESCBIT, Flag.SEARCHBIT},
        action=_glass_case_action,
    ))
    glass_case.move_to(hold)

    keyboard = world.register(GameObject(
        "KEYBOARD", desc="keyboard",
        synonyms=["keyboard", "keys"],
        adjectives=["small"],
        flags={Flag.NDESCBIT},
        action=_keyboard_action,
    ))
    keyboard.move_to(hold)

    switch = world.register(GameObject(
        "SWITCH", desc="small switch",
        synonyms=["switch"],
        adjectives=["small"],
        flags={Flag.NDESCBIT},
        action=_switch_action,
    ))
    switch.move_to(hold)

    plotter = world.register(GameObject(
        "PLOTTER", desc="atomic vector plotter",
        synonyms=["plotter", "device"],
        adjectives=["atomic", "vector"],
        flags={Flag.TAKEBIT, Flag.NDESCBIT},
        size=4,
        action=_plotter_action,
    ))
    plotter.move_to(glass_case)

    # Ford (Vogon ship version -- separate from earth Ford)
    ford = world.register(GameObject(
        "FORD-VOGON", desc="Ford Prefect",
        synonyms=["ford", "prefect"],
        adjectives=["ford"],
        flags={Flag.NARTICLEBIT, Flag.ACTORBIT, Flag.NDESCBIT},
        action=_ford_vogon_action,
    ))

    # Items Ford gives or that appear on the ship
    peanuts = world.register(GameObject(
        "PEANUTS", desc="small packet of peanuts",
        synonyms=["peanuts", "packet", "nuts"],
        adjectives=["small"],
        flags={Flag.TAKEBIT},
        size=2,
        action=_peanuts_action,
    ))

    satchel = world.register(GameObject(
        "SATCHEL", desc="satchel",
        synonyms=["satchel", "bag"],
        adjectives=["ford's", "leather", "battered"],
        flags={Flag.TAKEBIT, Flag.CONTBIT, Flag.SEARCHBIT, Flag.OPENBIT},
        size=10, capacity=20,
        action=_satchel_action,
    ))

    towel = world.register(GameObject(
        "TOWEL", desc="towel",
        synonyms=["towel"],
        adjectives=["large", "fluffy"],
        flags={Flag.TAKEBIT},
        size=4,
        action=_towel_action,
    ))

    guide = world.register(GameObject(
        "GUIDE", desc="Hitchhiker's Guide",
        synonyms=["guide", "book"],
        adjectives=["hitchhiker's", "electronic"],
        flags={Flag.TAKEBIT, Flag.READBIT, Flag.NARTICLEBIT},
        size=3,
        text='The cover reads: "DON\'T PANIC"',
        action=_guide_action,
    ))

    # ---- CAPTAIN'S QUARTERS ----

    captains_quarters = world.register(Room(
        "CAPTAINS-QUARTERS", desc="Captain's Quarters",
        flags={Flag.RLANDBIT, Flag.ONBIT},
        action=_captains_quarters_action,
    ))

    vogon_captain = world.register(GameObject(
        "VOGON-CAPTAIN", desc="Vogon Captain",
        synonyms=["captain", "vogon"],
        adjectives=["vogon"],
        flags={Flag.NARTICLEBIT, Flag.ACTORBIT, Flag.NDESCBIT},
        action=_vogon_captain_action,
    ))
    vogon_captain.move_to(captains_quarters)

    poetry = world.register(GameObject(
        "POETRY", desc="poetry",
        synonyms=["poetry", "poem", "verse"],
        adjectives=["vogon"],
        flags={Flag.NDESCBIT, Flag.NARTICLEBIT},
        action=_poetry_action,
    ))
    poetry.move_to(captains_quarters)

    chair = world.register(GameObject(
        "POETRY-APPRECIATION-CHAIR", desc="poetry appreciation chair",
        synonyms=["chair", "chairs"],
        adjectives=["poetry", "appreciation"],
        flags={Flag.VEHBIT, Flag.NDESCBIT},
        action=_chair_action,
    ))
    chair.move_to(captains_quarters)

    # ---- AIRLOCK ----

    airlock = world.register(Room(
        "AIRLOCK", desc="Airlock",
        flags={Flag.RLANDBIT, Flag.ONBIT},
        action=_airlock_action,
        global_objects=[inner_door, airlock_obj],
    ))

    # ---- Connect rooms ----

    hold.exits = {
        Direction.WEST: BlockedExit(
            "The door to the corridor is locked (from the outside)."
        ),
        Direction.EAST: ConditionalExit(_hold_east_exit),
    }

    # Captain's quarters has no exits -- you arrive via guards
    captains_quarters.exits = {}

    # Airlock exits are blocked
    airlock.exits = {
        Direction.WEST: BlockedExit(
            "The inner door has sealed behind you."
        ),
        Direction.EAST: BlockedExit(
            "The outer door is massive and immovable. Not that you'd "
            "want to open it."
        ),
    }
