#!/usr/bin/env python
from PIL import Image
import sys, os

def convert(filename):

    img = Image.open(filename)
    width, height = img.size

    if width  != 128 or height != 272:
        raise IOError('Error: Image size needs to be 128x272')

    pixels = list(img.getdata())

    # Foreground
    fg_offset = 0
    fg_tile_offset = fg_offset + 1024
    fg_palette = get_palette(pixels[fg_offset:fg_tile_offset])
    fg_tiles = get_tiles(pixels[fg_tile_offset:fg_tile_offset + 16384], fg_palette)

    # Background
    bg_offset = fg_tile_offset + 16384
    bg_tile_offset = bg_offset + 1024

    bg_palette = get_palette(pixels[bg_offset:bg_tile_offset])
    bg_tiles = get_tiles(pixels[bg_tile_offset:bg_tile_offset + 16384], bg_palette)
    
    # Save the data
    name, _ = os.path.splitext(filename)
    with open(name + '.plt', 'wb') as f:
        f.write(''.join([''.join([chr(i) for i in entry]) for entry in bg_palette]))
        f.write(''.join([''.join([chr(i) for i in entry]) for entry in fg_palette]))

    print 'Saved palette "%s.plt"' % name

    with open(name + '.chr', 'wb') as f:
        f.write(fg_tiles)
        f.write(bg_tiles)
    
    print 'Saved data "%s.chr"' % name


def get_palette(pixels):
    return [sorted([pixels[i] for i in range(p * 32, (p + 1) * 32, 8)]) for p in range(4)]


def get_tiles(pixels, palette):

    tiles = []
    for ty in range(16):
        for tx in range(16):

            tile = []
            for py in range(8):
                for px in range(8):
                    tile.append(pixels[(ty * 8 + py) * 128 + (tx * 8 + px)])
            
            tiles.append(tile)

    # convert colors to palette entries
    for t in tiles:

        colors = sorted(list(set(t)))
        entry = find_palette_entry(colors, palette)

        if entry is None:
            print 'Error: Tile colors do not match any palette entry'
            return None

        for p in range(64):
            t[p] = entry.index(t[p])

    # convert to NES format
    char_tiles = []
    for t in tiles:

        lows = []
        highs = []
        
        for y in range(8):

            low, high = 0, 0
            for x in range(8):
                pixel = t[y * 8 + x]
                low *= 2
                low += pixel & 1

                high *= 2
                high += (pixel >> 1) & 1 

            lows.append(chr(low))
            highs.append(chr(high))
        
        char_tiles.extend(lows)
        char_tiles.extend(highs)
            
    return ''.join(char_tiles)

def find_palette_entry(colors, palette):
    for entry in palette:

        match = True
        for c in range(len(colors)):
            if not colors[c] in entry:
                match = False
                break
            
        if match:
            return entry
    
    return None

if __name__ == '__main__':
    if len(sys.argv) < 2:
        print 'Usage: img2chr [filename]'

    else:
        convert(sys.argv[1])

