#!/usr/bin/python3
        # Header Block
	# Index 0/1: block-size word (datasize is always 19)
	# Index 2: 00=header; ff=data
	# Index 3: datatype (BASIC, NUMARRY, CHARARRAY, CODE)
	# Index 4-13: name
	# 14,15 : data block length
	# 16,17 : origin
	# 18,19: param 2

	# Data
	# Index 0/1: block-size
	# Index 2: ff
        # Index 3: data (length of data is block-size - 2)

import sys
class Memory:
    def __init__(self):
        self.mem = [0]*49152
        self.base = 16384

    def write_byte(self,addr,val):
        self.mem[addr-self.base] = val

    def write_file(self,name):
        print("Writing output file {}".format(name))
        try:
            f = open(name, "wb")
            f.write(bytearray([0]*27)) # Fake header
            f.write(bytearray(self.mem))
        except IOError:
            print("Error opening file {}".format(name))


class TapFile:
    def __init__(self):
        self.block_num = 1
        self.block_start = 0
        self.block_size = 0
        self.load_addr = None;
        self.mem = Memory()

    def write(self,name):
        self.mem.write_file(name)

    def read_byte(self,offset):
        return self.contents[self.block_start+offset]

    def read_word(self,offset):
        return self.contents[self.block_start+offset] + 256 * self.contents[self.block_start+offset+1]

    def load(self, filename):
        try:
            f = open(filename, "rb")
            self.contents = f.read(-1)
        except IOError:
            print("Error opening file")

    def next_block(self):
        self.block_start += self.block_size + 2
        self.block_num += 1
        return self.block_start < len(self.contents)

    def process_block(self):
        self.block_size = self.read_word(0)
        block_type = self.read_byte(2)
        if (block_type == 0):
            self.read_header()
        elif (block_type == 255):
            self.read_data()
        else:
            print("Unknown block type {}".format(block_type))
        return self.next_block()

    def read_header(self):
        datatype = self.read_byte(3)
        name = ""
        for i in range(4,14):
            name += chr(self.read_byte(i))
        self.length = self.read_word(14)
        self.start_address = self.read_word(16)
        if datatype == 3:
            self.load_addr = self.start_address
        else:
            self.load_addr = None
        print("Block {}; ".format(self.block_num), end='')
        print("Datatype: {}; ".format(datatype), end='')
        print("Name: {}; ".format(name), end='')
        print("Start address: {}; ".format(self.start_address), end='')
        print("Length: {}".format(self.length))

    def read_data(self):
        print("Block {}; ".format(self.block_num), end='')
        print("Data Size: {}".format(self.block_size-2))
        if self.load_addr != None:
                print("Loading into memory at: {}".format(self.load_addr))
                for i in range(0,self.block_size-2):
                    if self.load_addr + i > 65535:
                        break
                    self.mem.write_byte(self.load_addr+i, self.read_byte(i+3))
                    
def start():
	tap = TapFile()
	inFile = sys.argv[1]
	outFile = sys.argv[2]
	if (len(sys.argv) > 3):
	    maxBlock = int(sys.argv[3])
	else:
	    maxBlock = 255
	tap.load(inFile)
	while tap.process_block() and tap.block_num <= maxBlock:
	    pass
	
	# Fake a Quill system messages base address
	tap.mem.write_byte(0x5c7b,0x33)
	tap.mem.write_byte(0x5c7c,0x69)
	tap.write(outFile)

if (len(sys.argv) < 3):
        print("Usage: tap2sna.py <input filename> <output filename> [max TAP block to process]")
else:
    start()
