import sys
import time
import math
import array
import Image
import math
import pickle

def readz80(z80file):
    z80 = array.array('B', open(z80file).read()).tolist()
    mem = []
    comp = z80[30:]
    while True:
      if comp == [0, 0xed, 0xed, 0]:
        break
      if comp[0:2] == [0xed, 0xed]:
        mem += [comp[3]] * comp[2]
        comp = comp[4:]
      else:
        mem.append(comp.pop(0))
    assert len(mem) == 0xc000
    return array.array('B', mem)

def color(a, bright):
    if bright:
        mx = 255
    else:
        mx = 0xa0
    g = mx * ((a & 4) != 0)
    r = mx * ((a & 2) != 0)
    b = mx * ((a & 1) != 0)
    return [r,g,b]

# All this is taken from
# http://jswremakes.emuunlim.com/Mmt/Manic%20Miner%20Room%20Format.htm

stretch = [
  0x00, 0x03, 0x0c, 0x0f,
  0x30, 0x33, 0x3c, 0x3f,
  0xc0, 0xc3, 0xcc, 0xcf,
  0xf0, 0xf3, 0xfc, 0xff]
def pix16(b):
    return [stretch[(b >> 4)], stretch[b & 15]]

import gameduino
import gameduino.remote
import gameduino.compress
import gameduino.prep as gdprep

m = readz80("mm2.z80")
screens = m[45056 - 16384:]

def img8x8(d):
    i8 = Image.fromstring("1", (8,8), d[:8].tostring())
    r = Image.new("1", (16,16))
    r.paste(i8, (0,0))
    return r

def img16x16(d):
    return Image.fromstring("1", (16,16), d.tostring())

class Level:
    def __init__(self, id, lvl):
        """
        background  512-char background image
        bgchars     8 character graphics
        bgattr      8 character attributes
        border      border color
        item        item sprite, 16x16 1-color image
        items       up to 5 items (x,y)
        portalattr  portal attribute byte
        portal      portal 16x16 graphic
        portalxy    portal position (x,y)
        hguardians  up to 4 h-guardians (attr, x, y, d, x0, x1)
        """

        self.id = id 
        self.name = lvl[512:512+32].tostring()
        a2c = {}    # attribute to c mapping
        self.bgchars = [None] * 8
        self.bgattr = [None] * 8
        self.bgpal = [None] * 8
        for c in range(8):
            o = 544 + 9 * c
            attr = lvl[o]
            data = lvl[o+1:o+9]
            if not attr in a2c:
                a2c[attr] = c
            self.bgchars[c] = data
            self.bgattr[c] = attr
            if c == 4:  # conveyor
                conv_top = data[0]
                conv_bot = data[3]
        self.background = array.array('B', [a2c[a] for a in lvl[:512]])
        self.border = lvl[627]
        self.item = lvl[692:700]
        self.items = []
        def getxy(dd):
            """ decode a packed screen coordinate """
            x = dd[0] & 31
            y = (dd[0] >> 5) + ((dd[1] & 1) << 3)
            return (8 * x, 8 * y)

        for i in range(5):
            idata = lvl[629 + 5*i:629 + 5 * (i+1)]
            if idata[0] == 255:
                break
            if idata[0]:
                (x, y) = getxy(idata[1:3])
                self.items.append((x, y))

        self.portalattr = lvl[655]
        self.portal = lvl[656:688]
        self.portalxy = getxy(lvl[688:690])
        self.willyxy = getxy(lvl[620:622])
        self.willyxy = (self.willyxy[0], self.willyxy[1] + ((lvl[616] >> 1) & 7))
        self.willyd = lvl[618];
        self.willyf = lvl[617];

        self.guardian = [lvl[768+32*i:768+32*(i+1)] for i in range(8)]

        self.hguardians = [(0,0,0,0,0,0)] * 8
        for i in range(4):
            gdata = lvl[702 + 7*i: 702 + 7*(i+1)]
            if gdata[0] == 255:
                break
            if gdata[0]:
                attr = gdata[0]
                x,y = getxy(gdata[1:3])
                d = gdata[4]
                x0 = 8 * (gdata[5] & 0x1f)
                x1 = 8 * (gdata[6] & 0x1f)
                assert x0 < x1
                self.hguardians[i] = (attr, x, y, d, x0, x1)
        if self.id != 4:    # special for Eugene's lair
            for i in range(4):
                gdata = lvl[733 + 7*i: 733 + 7*(i+1)]
                if gdata[0] == 255:
                    break
                if gdata[0]:
                    attr = gdata[0]
                    y = gdata[2]
                    x = gdata[3] * 8
                    d = gdata[4]
                    y0 = gdata[5]
                    y1 = gdata[6]
                    self.hguardians[4+i] = (attr, x, y, d, y0, y1)
        self.conveyordir = 1 - lvl[623] # Game uses 1=right here, sigh
        self.air = 8 * ((lvl[700] - 32) - 4) + len([c for c in bin(lvl[701]) if (c == '1')])
        self.special = lvl[736:768]
        assert (0 < self.air <= 224)
        print self.name, self.hguardians

    def dumpmap(self, hh, cd):
        compressed = cd.toarray(self.background.tostring())
        gdprep.dump(hh, "map_%02d" % self.id, compressed)
        return len(compressed)

    def dump(self, hh):
        print >>hh, "{ //", self.name
        def init(a):
            return ",".join(["%d" % v for v in a])
        def da(a):
            print >>hh, "{"
            print >>hh, init(a)
            print >>hh, "},"

        da(array.array('B', self.name))
        print >>hh, "map_%02d," % self.id
        print >>hh, "%d," % self.border
        # da(self.background)
        print >>hh, "{", ",".join([init(cc) for cc in self.bgchars]), "},"
        da(self.bgattr)
        da(self.item)
        print >>hh, "{", ",".join(["{%d,%d}" % xy for xy in self.items]), "},"
        da(self.portal)
        print >>hh, "%d," % self.air
        print >>hh, "%d," % self.conveyordir
        print >>hh, "%d," % self.portalattr
        print >>hh, "%d,%d," % self.portalxy
        print >>hh, "{", ",".join([init(cc) for cc in self.guardian]), "},"
        print >>hh, "{", ",".join(["{%d,%d,%d,%d,%d,%d}" % xx for xx in self.hguardians]), "},"
        print >>hh, "%d,%d," % self.willyxy
        print >>hh, "%d," % self.willyd
        print >>hh, "%d," % self.willyf
        print >>hh, "%d," % self.bidir
        print >>hh, "},"

    def render(self, gd):
        gd.putstr(0, 16, self.name)
        for c in range(8):
            chrdata = array.array('B', sum([pix16(d) for d in self.bgchars[c]], []))
            gd.wrstr(gameduino.RAM_CHR + 16 * c, chrdata)

            ink = gameduino.RGB(*color(self.bgattr[c] & 7, self.bgattr[c] & 64))
            paper = gameduino.RGB(*color((self.bgattr[c] >> 3) & 7, self.bgattr[c] & 64))
            gd.wr16(gameduino.RAM_PAL + 8 * c, paper)
            gd.wr16(gameduino.RAM_PAL + 8 * c + 6, ink)
        
        for y in range(16):
            li = self.background[32*y:32*y+32]
            gd.wrstr(gameduino.RAM_PIC + 64 * y, li)

        for i in range(8):
            gd.wr16(gameduino.RAM_SPRPAL + 2 * i, gameduino.RGB(*color(i)))
        gd.wr16(gameduino.RAM_SPRPAL + 2 * 8, gameduino.TRANSPARENT)

        def loadimg(slot, im, zero, one):
            xlat = {0:zero, 255:one}
            itemdata = [xlat[ord(c)] for c in im.convert("L").tostring()]
            gd.wrstr(gameduino.RAM_SPRIMG + 256 * slot, array.array('B', itemdata))

        loadimg(0, img8x8(self.item), 8, 7)
        loadimg(1, img16x16(self.portal), 7&(self.portalattr>>3), (self.portalattr&7))
        for i in range(8):
            loadimg(2 + i, img16x16(self.guardian[i]), 8, 7)

        for i,(x,y) in enumerate(self.items):
            gd.sprite(i, x, y, 0, 0, 0)
            
        (x,y) = self.portalxy
        gd.sprite(6, x, y, 1, 0, 0)

        for i,(a,x,y,d,x0,x1) in enumerate(self.hguardians):
            gd.sprite(7+i, x, y, 2 + i, 0, 0)

print repr(m[40192-16384:40448-16384].tostring())
message = '.  .  .  .  .  .  .  .  .  .  . MANIC MINER . . \x7f BUG-BYTE ltd. 1983 . . By Matthew Smith . . . Gameduino conversion by James Bowman .  .  . Pin 6 = Left .  .  . Pin 4 = Jump .  .  . Pin 3 = Right .  .  . Guide Miner Willy through 19 lethal caverns .  .  .  .  .  .  .  .  .  .  .'
tune = m[34188-16384:34252-16384]
print len(tune), tune 
levels = [Level(i, screens[1024*i:1024*(i+1)]) for i in range(19)]
def notefreq(note): # http://www.oocities.org/andrewbroad/spectrum/willy/music.html 
    return (128 * 261.63) / note
    
if 0:
    gd = gameduino.remote.Gameduino(sys.argv[1], 115200)
    gd.wr(gameduino.J1_RESET, 1)
    gd.ascii()
else:
    gd = None
hh = open("../../manicminer.h", "w")
gdprep.dump(hh, "specpal", array.array('H', [gameduino.RGB(*color(7&i, i & 8)) for i in range(16)]))
gdprep.dump(hh, "specfont", array.array('B', open("gw03.rom").read()[0x3d00:]))
gdprep.dump(hh, "willy", m[33280-16384:33536-16384])
gdprep.dump(hh, "music1", tune)
gdprep.dump(hh, "boot", levels[2].special)
gdprep.dump(hh, "plinth", levels[1].special)
gdprep.dump(hh, "eugene", levels[4].special)
gdprep.dump(hh, "lightswitch", array.array('B', levels[7].bgchars[7]))
cd = gameduino.compress.Codec(7, 5)
print sum([lvl.dumpmap(hh, cd) for lvl in levels]), "in maps"
print >>hh, "static PROGMEM prog_uint32_t blue_danube[] = {"
print >>hh, ",".join("%d" % n for n in pickle.load(open("tune")))
print >>hh, "};"
print >>hh, 'prog_uchar message[] PROGMEM = "%s";' % message

for lvl in levels:
    lvl.bidir = lvl.id in [0,1,2,3,4,5,6,9,15]

print >>hh, "static PROGMEM level levels[] = {"
for lvl in levels:
    if gd:
        lvl.render(gd)
    lvl.dump(hh)
    time.sleep(0)
print >>hh, "};"

def airlevel(n):
    mask = int("1" * n + "0" * (8-n), 2)
    return [0,0,mask,mask,mask,mask,0,0]
gdprep.dump(hh, "airs", array.array('B', sum([airlevel(n) for n in range(9)], [])))

if 0:
    for s in range(8):
        Image.fromstring("1", (16,16), lvl[768 + 32 * s:768 + 32 * (s+1)].tostring()).save("s%d.png" % s)
    Image.fromstring("1", (16,8 * 16), lvl[768:1024]).save("out.png")

    # char 4 is conveyor, animate:
    while True:
        conv_top = ((conv_top << 1) & 255) | (conv_top >> 7)
        gd.wrstr(gameduino.RAM_CHR + 16 * 4, array.array('B', pix16(conv_top)))
        conv_bot = ((conv_bot >> 1) & 255) | ((conv_bot & 1) << 7)
        gd.wrstr(gameduino.RAM_CHR + 16 * 4 + 6, array.array('B', pix16(conv_bot)))
        time.sleep(.1)
