import struct
import time
import array
import math
import itertools
import wave

RAM_PIC       = 0x0000    # Screen Picture, 64 x 64 = 4096 bytes
RAM_CHR       = 0x1000    # Screen Characters, 256 x 16 = 4096 bytes
RAM_PAL       = 0x2000    # Screen Character Palette, 256 x 8 = 2048 bytes

IDENT         = 0x2800
REV           = 0x2801
FRAME         = 0x2802
VBLANK        = 0x2803
SCROLL_X      = 0x2804
SCROLL_Y      = 0x2806
JK_MODE       = 0x2808
J1_RESET      = 0x2809
SPR_DISABLE   = 0x280a
SPR_PAGE      = 0x280b
IOMODE        = 0x280c

BG_COLOR      = 0x280e
SAMPLE_L      = 0x2810
SAMPLE_R      = 0x2812
RING_START    = 0x2814
RING_END      = 0x2815

SCREENSHOT_Y  = 0x281e

PALETTE16A    = 0x2840    # 16-color palette RAM A, 32 bytes
PALETTE16B    = 0x2860    # 16-color palette RAM B, 32 bytes
PALETTE4A     = 0x2880    # 4-color palette RAM A, 8 bytes
PALETTE4B     = 0x2888    # 4-color palette RAM A, 8 bytes
COMM          = 0x2890    # Communication buffer
COLLISION     = 0x2900    # Collision detection RAM, 256 bytes
VOICES        = 0x2a00    # Voice controls
J1_CODE       = 0x2b00    # J1 coprocessor microcode RAM
SCREENSHOT    = 0x2c00    # screenshot line RAM

RAM_SPR       = 0x3000    # Sprite Control, 512 x 4 = 2048 bytes
RAM_SPRPAL    = 0x3800    # Sprite Palettes, 4 x 256 = 2048 bytes
RAM_SPRIMG    = 0x4000    # Sprite Image, 64 x 256 = 16384 bytes

def RGB(r, g, b):
    return ((r >> 3) << 10) | ((g >> 3) << 5) | (b >> 3)

TRANSPARENT = (1 << 15)

class Gameduino(object):

    def __init__(self):
        self.mem = array.array('B', [0] * 32768)

    def wrstr(self, a, s):
        if not isinstance(s, str):
            s = s.tostring()
        self.mem[a:a+len(s)] = array.array('B', s)

    def rd(self, a):
        return self.mem[a]

    def rdstr(self, a, n):
        return self.mem[a:a+n].tostring()

    def dump(self, a, l):
        for i in range(0, l, 16):
            d16 = self.rdstr(a + i, 16)
            print "%04x  %s" % (a + i, " ".join(["%02x" % ord(c) for c in d16]))

    def wr(self, a, v):
        self.wrstr(a, chr(v))

    def fill(self, a, v, c):
        self.wrstr(a, chr(v) * c)

    def putstr(self, x, y, v):
        a = y * 64 + x
        self.wrstr(a, v)

    def wr16(self, a, v):
        self.wrstr(a, struct.pack("<H", v))

    def wr32(self, a, v):
        self.wrstr(a, struct.pack("<L", v))

    def voice(self, v, wave, freq, lamp, ramp = None):
        if ramp is None:
            ramp = lamp
        self.wr32(VOICES + (4 * v), freq | (wave << 15) | (lamp << 16) | (ramp << 24))

    def silence(self):
        for i in range(64):
            self.voice(i, 0, 4 * 440, 0, 0)
    
    def copy(self, a, v):
        self.wrstr(a, v)

    def microcode(self, src):
      self.wr(J1_RESET, 1)
      self.copy(J1_CODE, src)
      self.wr(J1_RESET, 0)

    def sprite(self, spr, x, y, image, palette, rot, jk = 0):
        self.wr32(RAM_SPR + (4 * spr),
                  x | (rot << 9) | (palette << 12) | (y << 16) | (image << 25) | (jk << 31))

    def memory(self):
        """ Returns current image of memory as a 32768 byte string """
        return self.mem.tostring()

    def writewave(self, duration, dst):
        sintab = [int(127 * math.sin(2 * math.pi * i / 128.)) for i in range(128)]
        nsamples = 8000 * duration
        master = [i/8000. for i in range(nsamples)]
        lacc = [0] * nsamples
        racc = [0] * nsamples
        for v in range(64):
            if v == self.rd(RING_START):
                print v, max(lacc)
                ring = [s/256 for s in lacc]
                lacc = [0] * nsamples
                racc = [0] * nsamples

            (freq,la,ra) = struct.unpack("<HBB", self.rdstr(VOICES + 4 * v, 4))
            if la or ra:
                tone = [sintab[int(m * freq * 32) & 0x7f] for m in master]
                if self.rd(RING_START) <= v < self.rd(RING_END):
                    lacc = [o + la * (r * t) / 256 for (o,r,t) in zip(lacc, ring, tone)]
                    racc = [o + ra * (r * t) / 256 for (o,r,t) in zip(racc, ring, tone)]
                else:
                    lacc = [o + la * t for (o,t) in zip(lacc, tone)]
                    racc = [o + ra * t for (o,t) in zip(racc, tone)]
        merged = [None,None] * nsamples
        merged[0::2] = lacc
        merged[1::2] = racc
        raw = array.array('h', merged)
        w = wave.open(dst, "wb")
        w.setnchannels(2)
        w.setsampwidth(2)
        w.setframerate(8000)
        w.writeframesraw(raw)
        w.close()

def readarray(filename):
    return array.array('B', open(filename).read())


