'''
GAME: Ultima V Warriors of Destiny
@author: Truc Tran
April 1, 2020
'''

# File location
FILEPATH = ""
FILENAME = FILEPATH + "/SAVED.GAM"

# Statistic attribute names & offsets(hex)
STATS = [["STR", 0x000E], ["DEX", 0x000F], ["INT", 0x0010], ["MAGIC", 0x0011],
         ["HP", 0x0012], ["HM", 0x0014], ["EX", 0x0016]]
# Statistic values corresponding to stats attributes names list
STATSMAX = [99, 99, 99, 99, 999, 999, 9999]

ITEMS = [["GOLD", 0x0204], ["KEYS", 0x0206], ["GEMS", 0x0207], ["MAGIC CARPET", 0x020A],
        ["SKULL KEYS", 0x020B], ["BLACK BADGE", 0x0218], ["MAGIC AXE", 0x0240]]
# Statistic values corresponding to items list, max for gold is 9999, the rest are up to 1 byte
ITEMSMAX = [9999, 255, 255, 255, 255, 255, 255]

# Player names & offsets(hex) - MAX 8 characters/name = 16 hex
CHARS = [["TRUCTRAN", 0x0002], ["SHAMINO", 0x0022], ["IOLO", 0x0042], ["MARIAH", 0x0062],
         ["GEOFFREY", 0x0082], ["JAANA", 0x00A2], ["JULIA", 0x00C2], ["DUPRE", 0x00E2],
         ["KATRINA", 0x0102], ["SENTRI", 0x0122], ["GWENNO", 0x0142], ["JOHNE", 0x0162],
         ["GORN", 0x0182], ["MAXWELL", 0x01A2], ["TOSHI", 0x01C2], ["SADUJ", 0x01E2]]

def printLine():
    '''
    Print line :)
    '''
    print('\n')

def readDataFromFile(fileName)->list:
    '''
    Read the function and return the data as a list of bytes
    @param: file
    @return: a list of data
    '''
    with open(fileName, mode='rb') as f:
        content = list(bytes(f.read()))
    return content

def writeDataToFile(fileName, DATA):
    '''
    Saved the modification data
    @ param: fileName - location of file,
             DATA: list of data in bytes
    '''
    with open(fileName, mode='wb') as f:
        f.write(bytes(DATA))

DATA = readDataFromFile(FILENAME)

def setStatsVal(charNo, statsNo):
    '''
    Set the statistic value of choosen character
    @ param: charNo: character number/position in character name list
             statsNo: statistic number/position in statistic name list
    '''
    printLine()
    charName = CHARS[charNo][0]
    charAdd = CHARS[charNo][1]
    statsName = STATS[statsNo][0]
    statsAdd = STATS[statsNo][1]
    statsVal = int(DATA[statsAdd])
    prompt = "Edit {0:s} (range: 0 - {1:d}) (-1 to Exit): ".format(statsName, STATSMAX[statsNo])
    print("-----------{0:s}, {1:s}: {2:d}-----------".format(charName, statsName, statsVal))
    # Ask user to pick an option
    # only set the value in the range allowed: 0 -> 255 (max decimal in 8 bits)
    val = int(input(prompt))

    while(True):
        if val == -1:
            print("Exiting...")
            exit()
        elif val >= 0 and val <= STATSMAX[statsNo]:
            # when the value is smaller than 1 byte
            if STATSMAX[statsNo] < 256:
                # modify the value in memory
                DATA[charAdd - 2 + statsAdd] = val

            else: # when the value is 2 bytes
                # cast to bytearray, create 2 bytes from the integer
                two_bytes = val.to_bytes(2, byteorder='little', signed=True)
                mutable_bytes = bytearray(two_bytes)

                offset = 0
                # modify each byte in the bytearray , type(byte) is int
                for byte in list(mutable_bytes):
                    # writeFile already cast to byte
                    DATA[charAdd - 2 + statsAdd + offset] = byte
                    offset = offset + 1

            writeDataToFile(FILENAME, DATA)
            print("UPDATED:  {0:s}, {1:s}: {2:d}".format(charName, statsName, val))
        else:
            print("Invalid input! Please enter again.")

        val = int(input(prompt))

def setItemsVal(itemNo):
    '''
    Set the item value
    @ param: itemNo: item number/position in item name list
    '''
    printLine()
    itemName = ITEMS[itemNo][0]
    itemAdd = ITEMS[itemNo][1]
    itemVal = int(DATA[itemAdd])
    prompt = "Edit {0:s} (range: 0 - {1:d}) (-1 to Exit): ".format(itemName, ITEMSMAX[itemNo])
    # Ask user to pick an option
    val = int(input(prompt))

    while(True):
        if val == -1:
            print("Exiting...")
            exit()
        elif val >= 0 and val <= ITEMSMAX[itemNo]:
            # when the value is smaller than 1 byte
            if ITEMSMAX[itemNo] < 256:
                # modify the value in memory
                DATA[itemAdd] = val

            else: # when the value is 2 bytes
                # cast to bytearray, create 2 bytes from the integer
                two_bytes = val.to_bytes(2, byteorder='little', signed=True)
                mutable_bytes = bytearray(two_bytes)

                offset = 0
                # modify each byte in the bytearray , type(byte) is int
                for byte in list(mutable_bytes):
                    # writeFile already cast to byte
                    DATA[itemAdd + offset] = byte
                    offset = offset + 1

            writeDataToFile(FILENAME, DATA)
            print("-----------UPDATED:  {0:s}:{1:d}-----------".format(itemName, val))
        else:
            print("Invalid input! Please enter again.")

        val = int(input(prompt))

def characterMenu():
    '''
    Display the character name menu and ask user option to access character data
    '''
    printLine()
    print("CHARACTER MENU: ")

    no = 1
    # range of items in the list
    lowerBound = 0
    upperBound = 16

    # Display character menu
    for i in CHARS:
        print(str(no) + " " + i[0])
        no = no + 1
    # Ask user to pick an option
    prompt = "Please enter a player number (1-16): "
    charNo = int(input(prompt))

    while(True):
        # 0 to exit
        if charNo == -1:
            print("Exiting...")
            exit()
        # to convert the number to list index
        charNo = charNo - 1

        if charNo < lowerBound or charNo > 16:
            print("Invalid input! ")
            charNo
        else:
            print("Opening player \"" + CHARS[charNo][0] + "\" Stastistic...")
            statsMenu(charNo)

        charNo = int(input(prompt))

def itemMenu():
    '''
    Display the item name menu and its value
    Ask user option to access or modify the data
    '''
    printLine()
    print("ITEM MENU: ")

    no = 1
    lowerBound = 0
    upperBound = 6

    # display the items name and their value
    for index in range(len(ITEMS)):
        i = ITEMS[index]
        if ITEMSMAX[index] < 256:
            itemVal = int(DATA[i[1]])
            print(str(no) + " " + i[0] + ": " + str(itemVal))
        else:
            num0 = int(DATA[i[1]])
            num1 = int(DATA[i[1] + 1])
            num = num0 + num1 * 256
            print(str(no) + " " + i[0] + ": " + str(num))
        no = no + 1
    # Ask user to pick an option
    prompt = "Please enter a item number (1-7): "
    itemNo = int(input(prompt))

    while(True):
        # 0 to exit
        if itemNo == -1:
            print("Exiting...")
            exit()
        # to convert the number to index
        itemNo = itemNo - 1

        if itemNo < lowerBound or itemNo > upperBound:
            print("Invalid input! ")
            itemNo
        else:
            setItemsVal(itemNo)

        itemNo = int(input(prompt))

def statsMenu(charNo):
    '''
    Display the statistic name menu and its value
    Ask user option to access or modify the data
    '''
    printLine()
    print("STATISTIC MENU: ")

    no = 1
    lowerBound = 0
    upperBound = 6
    # display the stats name and their value

    for index in range(len(STATS)):
        i = STATS[index]
        # when the data only take 1 byte
        if STATSMAX[index] < 256:
            statsVal = int(DATA[i[1]])
            print(str(no) + " " + i[0] + ": " + str(statsVal))
        else: # when the data take more than 1 byte
            num0 = int(DATA[i[1]])
            num1 = int(DATA[i[1] + 1])
            # convert the total to binary ( *256)
            num = num0 + num1 * 256
            print(str(no) + " " + i[0] + ": " + str(num))
        no = no + 1

    # Ask user to pick an option
    prompt = "Please enter an attribute number (1-7): "
    statsNo = int(input(prompt))

    while(True):
        # 0 to exit
        if statsNo == -1:
            print("Exiting...")
            exit()
            return
        # to convert the number to index
        statsNo = statsNo - 1
        if statsNo < lowerBound or statsNo > upperBound:
            print("Invalid input! ")
            statsNo
        else:
            setStatsVal(charNo, statsNo)

        statsNo = int(input(prompt))

def mainMenu():
    prompt = "PLease choose an option (-1: Exit, 1: Players, 2: Items): "
    invalid = "The input is invalid. "
    negativeOne = "Exit Program!"
    # Ask user to pick an option
    option = int(input(prompt))

    while(True):
        if option == -1:
            print(negativeOne)
            exit()
        if option == 1:
            characterMenu()
        elif option == 2:
            itemMenu()
        else:
            print(invalid)

        option = int(input(prompt))

if __name__ == "__main__":
    mainMenu()
