from array import array
import Image

def dump(hh, name, data):
    print >>hh, "static PROGMEM prog_uchar %s[] = {" % name
    bb = array('B', data.tostring())
    for i in range(0, len(bb), 16):
        if (i & 0xff) == 0:
            print >>hh
        for c in bb[i:i+16]:
            print >>hh, "0x%02x, " % c,
        print >>hh
    print >>hh, "};"

def rgbpal(imdata):
    # For RGBA imdata, return list of (r,g,b) triples and the palette
    li = array('B', imdata).tolist()
    rgbas = zip(li[0::4], li[1::4], li[2::4], li[3::4])
    palette = list(set(rgbas))
    return (rgbas, palette)

def getch(im, x, y):
    # return the RGBA data for the 8x8 character at (x, y) in im
    # if the 8x8 RGB contains more than 4 colors, quantize it using
    # `scolorq <http://www.cs.berkeley.edu/~dcoetzee/downloads/scolorq/>`_.

    sub88 = im.crop((x, y, x + 8, y + 8))
    sub88d = sub88.tostring()

    (_, pal) = rgbpal(sub88d)
    if len(pal) > 4:
        return sub88.convert('RGB').convert('P', palette=Image.ADAPTIVE, colors=4).convert("RGBA")
    else:
        return sub88.convert('RGBA')

def rgb555(r, g, b):
    return ((r / 8) << 10) + ((g / 8) << 5) + (b / 8)

def rgba1555(r, g, b, a):
    return ((a < 128) << 15) + ((r / 8) << 10) + ((g / 8) << 5) + (b / 8)

def binary(imdata):
    # imdata is 8x8x4 RGBA character, string of length 256
    # return the pixel data and palette data for it
    assert len(imdata) == (4 * 8 * 8)
    (rgbs, palette) = rgbpal(imdata)
    indices = [palette.index(c) for c in rgbs]
    indices_b = ""
    for i in range(0, len(indices), 4):
        c =   ((indices[i] << 6) +
               (indices[i + 1] << 4) +
               (indices[i + 2] << 2) +
               (indices[i + 3]))
        indices_b += (chr(c))
    palette = (palette + ([(0,0,0,255)] * 4))[:4]   # unused palette entries: opaque black
    ph = array('H', [rgba1555(*p) for p in palette])
    return (indices_b, ph)

def encode(im):
    preview = Image.new("RGB", im.size)
    charset = {} # dict that maps 16x16 images to byte charcodes
    picture = [] # 64x64 byte picture RAM
    for y in range(0, im.size[1], 8):
        for x in range(0, im.size[0], 8):
            iglyph = getch(im, x, y)
            preview.paste(iglyph, (x, y))
            glyph = iglyph.tostring()
            if not glyph in charset:
                charset[glyph] = len(charset)
            picture.append(charset[glyph])
    # preview.save("preview.png")
    # print "Used", len(charset), "glyphs"
    picd = array('B', picture)
    cd = array('B', [0] * 16 * len(charset))
    pd = array('H', [0] * 4 * len(charset))
    for d,i in charset.items():
        for y in range(8):
            (char, pal) = binary(d)
            cd[16 * i:16 * (i+1)] = array('B', char)
            pd[4 * i:4 * (i+1)] = pal
    return (picd, cd, pd)

def alphamask(im):
    # im is an RGBA image.  Return a copy of im with any transparent pixels set to black.

    assert im.mode == "RGBA"
    alpha = im.split()[3]
    mask = Image.eval(alpha, lambda a: 255 if a <= 128 else 0)
    mask.save("preview.png")
    r = im.copy()
    r.paste((0,0,0,0), mask)
    return r

def pal555(im, ncol = 256):
    """ im is a source sprite image.  Convert the image to indexed (using ncol colors)
    """ 
    if im.mode == "RGBA":
        alpha = im.split()[3]
        mask = Image.eval(alpha, lambda a: 255 if a <= 128 else 0)
        # Convert the image into P mode but only use 255 colors in the palette out of 256
        im.paste((0,0,0), mask)
        im = im.convert('RGB').convert('P', palette=Image.ADAPTIVE, colors=ncol)
        # Set all pixel values below 128 to 255,
        # and the rest to 0
        # Paste the color of index 255 and use alpha as a mask
        im.paste(ncol-1, mask)
        transparent = ncol - 1
    else:
        im = im.convert('RGB').convert('P', palette=Image.ADAPTIVE, colors=ncol)
        transparent = None
    # Return the RGB555 palette used by im
    lut = im.resize((ncol,1))
    lut.putdata(range(ncol))
    palstr = lut.convert("RGB").tostring()
    rgbs = zip(*(array('B', palstr[i::3]) for i in range(3)))
    rgb555 = [(((r / 8) << 10) | ((g / 8) << 5) | (b / 8)) for (r,g,b) in rgbs]
    if transparent:
        rgb555[transparent] = 0x8000
    return (im, rgb555)

def tile(im):
    (w,h) = im.size
    n = im.resize((16, (w/16) * h))
    i = 0   # tile counter
    for y in range(0, h, 16):
        for x in range(0, w, 16):
            n.paste(im.crop((x,y,x+16,y+16)), (0, 16 * i))
            i += 1
    return n

def isnonblank(im, pal):
    (w,h) = im.size
    colors = set([im.getpixel((i,j)) for i in range(w) for j in range(h)])
    if len(colors) == 1:
        return pal[list(colors)[0]] != 0x8000
    else:
        return True

class ImageRAM(object):

    def __init__(self):
        self.data = array('B', [0] * 16384)
        self.nxtpage = 0    # next available page
        self.nxtbit = 0     # next available bit

    def bump(self, b):
        self.nxtbit += b
        if 8 == self.nxtbit:
            self.nxtbit = 0
            self.nxtpage += 1

    def add(self, page, size):
        assert size in (4,16,256)
        assert max(page) < size
        assert len(page) == 256

        bits = {4:2, 16:4, 256:8}[size]
        while (self.nxtbit % bits) != 0:
            self.bump(2)
        if size == 4:
            pal = self.nxtbit / 2
        elif size == 16:
            pal = self.nxtbit / 4
        else:
            pal = 0
        pg = self.nxtpage
        for i in range(256):
            self.data[256 * self.nxtpage + i] |= (page[i] << self.nxtbit)
        self.bump(bits)
        return (pg, pal)
    def used(self):
        if self.nxtbit == 0:
            past = self.nxtpage
        else:
            past = self.nxtpage + 1
        return array('B', self.data[:256*past])

import math

def spectrum(specfile, cutoff = 32, volume = 255):
    snd = [[float(t) for t in l.split()] for l in open(specfile) if not "Freq" in l]
    snd = [(f,db) for (f,db) in snd if 40 < f < 8192]
    snd = sorted(snd, reverse=True, key=lambda t:t[1])
    top = snd[:cutoff]
    amps = [(f,math.pow(2, .1 * db)) for (f, db) in top]
    samps = sum([a for (f,a) in amps])
    return [(f, int(volume * a / samps)) for (f, a) in amps]

PALETTE256A = 0
PALETTE256B = 1
PALETTE256C = 2
PALETTE256D = 3
PALETTE4A_BITS01 = (0x8 + (0 << 1))
PALETTE4A_BITS23 = (0x8 + (1 << 1))
PALETTE4A_BITS45 = (0x8 + (2 << 1))
PALETTE4A_BITS67 = (0x8 + (3 << 1))
PALETTE4AS = (PALETTE4A_BITS01, PALETTE4A_BITS23, PALETTE4A_BITS45, PALETTE4A_BITS67)

PALETTE16A_BITS0123 = (0x4 + (0 << 1))
PALETTE16A_BITS4567 = (0x4 + (1 << 1))
PALETTE16AS = (PALETTE16A_BITS0123, PALETTE16A_BITS4567)
PALETTE16B_BITS0123 = (0x4 + (0 << 1) + 1)
PALETTE16B_BITS4567 = (0x4 + (1 << 1) + 1)
PALETTE16BS = (PALETTE16B_BITS0123, PALETTE16B_BITS4567)
