import machine
import sdcard
import uos

def hexdump(src: bytes, baseAddr: int = 0):
    FILTER = ''.join([(len(repr(chr(x))) == 3) and chr(x) or '.' for x in range(256)])
    lines = []
    for addr in range(0, len(src), 16):
        realAddr = addr + baseAddr
        line = f'{realAddr:08X}  '
        chars = src[addr : addr + 16]
        line += ' '.join(['{:02X}'.format(x) for x in chars])
        line += '  '
        line += ''.join(['{}'.format((x <= 127 and FILTER[x]) or '.') for x in chars])
        lines.append(line)
    return lines

# emulated FAT12 filesystem
class FakeFATBlockDev:
    def __init__(self, filename, filedata):
        # filesize works, filename and filedata are ignored for now
        self.filename = filename
        self.filedata = filedata
        self.filesize = 2048 #len(filedata)
    
    def readblocks(self, block_num, buf, offset=0):
        bl = len(buf)
        print(f'block_num={block_num}, offset={offset}, buf_len={bl}')
        blk = None
        if block_num == 0:
            # boot sector
            blk = bytearray.fromhex("EBFE904D53444F53352E3000020101000100023B00F801003F00FF000000000000000000800029000021524E4F204E414D452020202046415420202020200000") + bytearray(0x1BE) + bytearray.fromhex("55AA")
        elif block_num == 1:
            # FAT (this is a hack, just static FAT data describing a file bigger than the )
            blk = bytearray.fromhex("F8FFFF03400005600007800009A0000BC0000DE0000F000111200113400115600117800119A0011BC0011DE0011F000221200223400225600227800229A0022BC0022DE0022F000331200333400335600337800339A0033BC0033DE0033F000441200443400445600447800449A0044BC0044DE0044F000551200553400555600557800559A0055BC0055DE0055F000661200663400665600667800669A0066BC0066DE0066F000771200773400775600777800779A0077BC0077DE0077F000881F0FF00000000000000000000000000") + bytearray(0x140)

            # the following was my attempt to build this FAT dynamically, but 12-bit little-endian packing got the best of me and it doesn't work properly
            #blk = bytearray(512)
            #blk[0] = 0xF8
            #blk[1] = 0xFF
            #blk[2] = 0xFF
            #shift = 0
            #maxidx = 0
            #carry = 0
            #for i in range(3,64):
            #    idx = ((i-3)*12) // 8
            #    if i % 2 == 0:
            #        blk[idx+3] = (blk[idx] & 0x0F) | ((i << 4) & 0xF0);
            #    else:
            #        blk[idx+3] = i & 0xFF
            #    #blk[idx+3] |= (i << (4 if (i % 2) == 0 else 0)) & 0xFF
            #    #blk[idx+3] |= carry
            #    #carry = (i >> 8) & 0xF
            #    maxidx = max(maxidx, i)
            #blk[maxidx+1] = 0xF0
            #blk[maxidx+2] = 0xFF
            #print(''.join('{:02X}'.format(b) for b in blk))
        elif block_num == 2:
            # directory entry
            # pack file size value into hex string
            sizeHex = '{:02X}'.format(self.filesize & 0xFF) + '{:02X}'.format((self.filesize >> 8) & 0xFF) + '{:02X}'.format((self.filesize >> 16) & 0xFF) + '{:02X}'.format((self.filesize >> 24) & 0xFF)
            # create an entry for app.py (right now the filename provided to this class is ignored)
            blk = bytearray.fromhex("41505020202020205059202018000000215200000000000021520200" + sizeHex) + bytearray(0x1E0)
        elif block_num >= 34 and block_num <= 37:
            # 4-blocks of file data (2048 bytes) with each block full of 'AAAAAA' prefixed by the block number, to demonstrate
            blk = bytearray(512)
            blkstr = f'block {block_num} :) '
            for i in range(512):
                blk[i] = 0x41
            for i in range(len(blkstr)):
                blk[i] = ord(blkstr[i])
        else:
            # empty block
            blk = bytearray(512)

        # copy the result back to the read buffer
        for i in range(len(buf)):
            buf[i] = blk[offset + i]

    def writeblocks(self, block_num, buf, offset=None):
        # do nothing, writing is disallowed
        return
    
    def ioctl(self, op, arg):
        if op == 4: # block count
            return 256
        if op == 5: # block size
            return 512
        if op == 6: # block erase
            return 0

class RAMBlockDev:
    def __init__(self, block_size, num_blocks):
        self.block_size = block_size
        self.data = bytearray(block_size * num_blocks)

    def readblocks(self, block_num, buf, offset=0):
        addr = block_num * self.block_size + offset
        for i in range(len(buf)):
            buf[i] = self.data[addr + i]

    def writeblocks(self, block_num, buf, offset=None):
        if offset is None:
            # do erase, then write
            for i in range(len(buf) // self.block_size):
                self.ioctl(6, block_num + i)
            offset = 0
        addr = block_num * self.block_size + offset
        for i in range(len(buf)):
            self.data[addr + i] = buf[i]

    def ioctl(self, op, arg):
        if op == 4: # block count
            return len(self.data) // self.block_size
        if op == 5: # block size
            return self.block_size
        if op == 6: # block erase
            return 0

    def find(self, c, start=0):
        for i in range(start, len(self.data)):
            if self.data[i] == c:
                return i
        return None

bdev2 = FakeFATBlockDev("foo", "testing")
uos.mount(bdev2, '/ramdisk2')
with open('/ramdisk2/app.py', 'r') as f:
    print(f.read())


#bdev = RAMBlockDev(512, 256)
#uos.VfsFat.mkfs(bdev)
#uos.mount(bdev, '/ramdisk1')
#with open('/ramdisk1/app.py', 'w') as f:
#    for i in range(8):
#        f.write(str(i) * 8192)

#print(bdev.find(ord('X')))

#buf = bytearray(512)
#for n in range(0, 4):
#    bdev.readblocks(n, buf)
#    for line in hexdump(buf, baseAddr = n * 512):
#        print(line)
$abcdeabcde151015202530354045505560fghijfghij