import os
import binascii
from ..assembler import ASM
from ..utils import formatText

import pkgutil

def hasBank3E(rom):
    return rom.banks[0x3E][0] != 0x00

def generate_name(l, i):
    if i < len(l):
        name = l[i]
    else:
        name = f"player {i}"
    name = name[:16]
    assert(len(name) <= 16)
    return 'db "' + name + '"' + ', $ff' * (17 - len(name)) + '\n'


# Bank $3E is used for large chunks of custom code.
#   Mainly for new chest and dropped items handling.
def addBank3E(rom, seed, player_id, player_name_list):
    # No default text for getting the bow, so use an unused slot.
    rom.texts[0x89] = formatText("Found the {BOW}!")
    rom.texts[0xD9] = formatText("Found the {BOOMERANG}!")  # owl text slot reuse
    rom.texts[0xBE] = rom.texts[0x111]  # owl text slot reuse to get the master skull message in the first dialog group
    rom.texts[0xC8] = formatText("Found {BOWWOW}! Which monster put him in a chest? He is a good boi, and waits for you at the Swamp.")
    rom.texts[0xC9] = 0xC0A0  # Custom message slot
    rom.texts[0xCA] = formatText("Found {ARROWS_10}!")
    rom.texts[0xCB] = formatText("Found a {SINGLE_ARROW}... joy?")

    # Create a trampoline to bank 0x3E in bank 0x00.
    # There is very little room in bank 0, so we set this up as a single trampoline for multiple possible usages.
    # the A register is preserved and can directly be used as a jumptable in page 3E.
    # Trampoline at rst 8
    # the A register is preserved and can directly be used as a jumptable in page 3E.
    rom.patch(0, 0x0008, "0000000000000000000000000000", ASM("""
        ld   h, a
        ld   a, [$DBAF]
        push af
        ld   a, $3E
        call $080C ; switch bank
        ld   a, h
        jp $4000
    """), fill_nop=True)

    # Special trampoline to jump to the damage-entity code, we use this from bowwow to damage instead of eat.
    rom.patch(0x00, 0x0018, "000000000000000000000000000000", ASM("""
        ld   a, $03
        ld   [$2100], a
        call $71C0
        ld   a, [$DBAF]
        ld   [$2100], a
        ret
    """))

    def get_asm(name):
        return pkgutil.get_data(__name__, "bank3e.asm/" + name).decode().replace("\r", "")

    rom.patch(0x3E, 0x0000, 0x2F00, ASM("""
        call MainJumpTable
        pop af
        jp $080C ; switch bank and return to normal code.

MainJumpTable:
        rst  0 ; JUMP TABLE
        dw   MainLoop                             ; 0
        dw   RenderChestItem                      ; 1
        dw   GiveItemFromChest                    ; 2
        dw   ItemMessage                          ; 3
        dw   RenderDroppedKey                     ; 4
        dw   RenderHeartPiece                     ; 5
        dw   GiveItemFromChestMultiworld          ; 6
        dw   CheckIfLoadBowWow                    ; 7
        dw   BowwowEat                            ; 8
        dw   HandleOwlStatue                      ; 9
        dw   ItemMessageMultiworld                ; A
        dw   GiveItemAndMessageForRoom            ; B
        dw   RenderItemForRoom                    ; C
        dw   StartGameMarinMessage                ; D
        dw   GiveItemAndMessageForRoomMultiworld  ; E
        dw   RenderOwlStatueItem                  ; F
        dw   UpdateInventoryMenu                  ; 10
        dw   LocalOnlyItemAndMessage              ; 11
StartGameMarinMessage:
        ; Injection to reset our frame counter
        call $27D0 ; Enable SRAM
        ld   hl, $B000
        xor  a
        ldi  [hl], a ;subsecond counter
        ld   a, $08  ;(We set the counter to 8 seconds, as it takes 8 seconds before link wakes up and marin talks to him)
        ldi  [hl], a ;second counter
        xor  a
        ldi  [hl], a ;minute counter
        ldi  [hl], a ;hour counter

        ld   hl, $B010
        ld   a, $01  ;tarin's gift gets skipped for some reason, so inflate count by 1
        ldi  [hl], a ;check counter low
        xor  a
        ldi  [hl], a ;check counter high

        ; Show the normal message
        ld   a, $01
        jp $2385

TradeSequenceItemData:
    ; tile attributes
    db $0D, $0A, $0D, $0D, $0E, $0E, $0D, $0D, $0D, $0E, $09, $0A, $0A, $0D
    ; tile index
    db $1A, $B0, $B4, $B8, $BC, $C0, $C4, $C8, $CC, $D0, $D4, $D8, $DC, $E0

UpdateInventoryMenu:
        ld   a, [wTradeSequenceItem]
        ld   hl, wTradeSequenceItem2
        or   [hl]
        ret  z
        
        ld   hl, TradeSequenceItemData
        ld   a, [$C109]
        ld   e, a
        ld   d, $00
        add  hl, de

        ; Check if we need to increase the counter
        ldh  a, [$E7] ; frame counter
        and  $0F
        jr   nz, .noInc
        ld   a, e
        inc  a
        cp   14
        jr   nz, .noWrap
        xor  a
.noWrap:
        ld   [$C109], a
.noInc:

        ; Check if we have the item
        ld   b, e
        inc  b
        ld   a, $01

        ld   de, wTradeSequenceItem
.shiftLoop:
        dec  b
        jr   z, .shiftLoopDone
        sla  a
        jr   nz, .shiftLoop
        ; switching to second byte
        ld   de, wTradeSequenceItem2
        ld   a, $01
        jr   .shiftLoop
.shiftLoopDone:
        ld   b, a
        ld   a, [de]
        and  b
        ret  z ; skip this item

        ld   b, [hl]
        push hl

        ; Write the tile attribute data
        ld   a, $01
        ldh  [$4F], a

        ld   hl, $9C6E
        call WriteToVRAM
        inc  hl  
        call WriteToVRAM
        ld   de, $001F
        add  hl, de
        call WriteToVRAM
        inc  hl  
        call WriteToVRAM

        ; Write the tile data
        xor  a
        ldh  [$4F], a
        
        pop  hl
        ld   de, 14
        add  hl, de
        ld   b, [hl]
        
        ld   hl, $9C6E
        call WriteToVRAM
        inc  b
        inc  b
        inc  hl  
        call WriteToVRAM
        ld   de, $001F
        add  hl, de
        dec  b
        call WriteToVRAM
        inc  hl  
        inc  b
        inc  b
        call WriteToVRAM
        ret

WriteToVRAM:
        ldh  a, [$41]
        and  $02
        jr   nz, WriteToVRAM
        ld   [hl], b
        ret
LocalOnlyItemAndMessage:
        call GiveItemFromChest
        call ItemMessage
        ret
    """ + get_asm("multiworld.asm")
        + get_asm("link.asm")
        + get_asm("chest.asm")
        + get_asm("bowwow.asm")
        + get_asm("message.asm")
        + get_asm("itemnames.asm")
        + "".join(generate_name(["The Server"] + player_name_list, i ) for i in range(100)) # allocate
        + 'db "another world", $ff\n'
        + get_asm("owl.asm"), 0x4000), fill_nop=True)
    # 3E:3300-3616: Multiworld flags per room (for both chests and dropped keys)
    # 3E:3800-3B16: DroppedKey item types
    # 3E:3B16-3E2C: Owl statue or trade quest items
    
    # Put 20 rupees in all owls by default.
    rom.patch(0x3E, 0x3B16, "00" * 0x316, "1C" * 0x316)
   

    # Prevent the photo album from crashing due to serial interrupts
    rom.patch(0x28, 0x00D2, ASM("ld a, $09"), ASM("ld a, $01"))
