#!/usr/bin/python
import pygame

ship = [              (4,1),(5,1),
                      (4,2),
                (3,3),(4,3),
                (3,4),(4,4),
          (2,5),(3,5),(4,5),
    (1,6),(2,6),(3,6),
    (1,7),(2,7),(3,7),
    (1,8),(2,8),(3,8),
    (1,9),(2,9),(3,9),(4,9),
               (3,10),(4,10),(5,10) ]

core = [ (5,2),(6,2), (5,3),(6,3), (5,4),(6,4), (5,5),(6,5),      
        (5,9),(6,9) ]
cockpit = [   (4,6),(5,6),
              (4,7),(5,7),
              (4,8),(5,8),
              (7,6), (6,6),
              (7,7), (6,7),
              (7,8), (6,8) ]

      
import colorsys
def normalize(color): return tuple(map(lambda x: min(1.0,x / 255.0), color))
def reformat(color): return tuple(map(lambda x: min(255,int(round(x * 255))), color))

def plot(surf, point, color, code):
    x, y = point
    surf.set_at(point, color)
    for pt in ((x+1,y),(x-1,y),(x,y+1),(x,y-1)):
        if surf.get_at(pt) == (0, 0, 0, 255):
            hue, saturation, value = get_hsv(pt[0], pt[1], code)
            hsv = (hue,saturation,value+50)
            border = hsv2rgb(hsv)
            surf.set_at(pt, border)

def get_saturation(x,y):
    if (x,y) in cockpit:
        return 100 - (20 * (7 - x))
    elif (x,y) in core:
        return 200
    elif y < 6:
        return 120 - (20 * abs(y - 2))
    elif y < 9:
        return 120 - (20 * (8 - y))
    else:
        return 100 - (20 * (y - 10))

def get_value(x,y):
    if (x,y) in cockpit:
        if y == 8:
            return 230
        else:
            return 255
    elif (x,y) in core:
        return 150
    elif x < 7:
        return 200 - (20 * (6 - x))
    else:
        return 200 - (20 * (x - 7))

def get_hue(x,y,code):
    colors = [ code & 255, code >> 8 & 255, code >> 16 & 255, code >> 24 & 255 ]
    if (x,y) in core + cockpit: # superstructure
        return colors[3]
    elif y < 6: # Nose
        return colors[0]
    elif y < 9: # wings
        return colors[1]
    else: # engines
        return colors[2]

def get_hsv(x,y,code):
    return get_hue(x,y,code), get_saturation(x,y), get_value(x,y)

def hsv2rgb(hsv):
    return reformat(colorsys.hsv_to_rgb(*normalize(hsv)))

def get_rgb(x, y, code):
    return tuple(hsv2rgb(get_hsv(x,y,code)))

def get_cockpit_rgb(x, y, hue):
    saturation = 100 - (20 * (7 - x))
    if y == 8:
        value = 230
    else:
        value = 255
    hsv = normalize((hue, saturation, value))
    return reformat(colorsys.hsv_to_rgb(*hsv))

def draw_ship(code):
    dood = pygame.Surface((12,12))
    dood.fill((0,0,0))
    x, y = core[0] # first core cell for testing
    corecolor = get_rgb(x,y,code)
    for point in core: 
        dood.set_at(point, corecolor)
    for bit in xrange(26,32):
        x,y = cockpit[bit - 26]
        if code & 2**bit:
            dood.set_at((x,y),get_rgb(x,y,code))
            dood.set_at((11-x,y),get_rgb(11-x,y,code))
        else:
            dood.set_at((x,y),corecolor)
            dood.set_at((11 - x,y),corecolor)
    for bit in xrange(0,26):
        if code & 2**bit:
            x, y = ship[bit]
            color = get_rgb(x, y, code)
            plot(dood, (x,y),color, code)
            x = 11 - x
            color = get_rgb(x, y, code)
            plot(dood, (x,y),color, code)
    dood.convert()
    dood.set_colorkey((0,0,0))
    return dood

class PixelShip(pygame.sprite.Sprite):
    """A sprite type that has pixelly goodness"""
    def __init__(self, code, location=(0,0), group=(), scale=(22,22)):
        pygame.sprite.Sprite.__init__(self, group)
        self.code = code
        self.scale = scale
        self.location = location
        self.ship = pygame.transform.scale2x(draw_ship(code))
        self.ship = pygame.transform.scale(self.ship, scale)
        self.image = self.ship
        self.rect = self.image.get_rect()
        self.move_to(location)
        self.zoom = self.rect.width / 11
    def move_to(self, location):
        self.location = location
        self.rect.center = location
    def update(self, angle, movement):
        self.image = pygame.transform.rotate(self.ship, 0 - angle)
        self.rect = self.image.get_rect()
        x, y = self.location
        dx, dy = movement
        self.move_to((x + dx, y + dy))
    def new_code(self, code):
        self.code = code
        self.ship = pygame.transform.scale2x(draw_ship(code))
        self.ship = pygame.transform.scale(self.ship, self.scale)
        self.image = self.ship
    def get_component(self, point):
        if not self.rect.collidepoint(*point):
            return False
        x = (point[0] - self.rect.left) / self.zoom
        y = (point[1] - self.rect.top) / self.zoom
        if (x,y) in cockpit + core:
            return "core"
        elif y < 6:
            return "nose"
        elif y < 9:
            return "wing"
        else:
            return "engine"


def main(code):
    from random import randint
    pygame.init()
    screen = pygame.display.set_mode((320, 200))
    screen.fill((0,0,0))
    pygame.display.set_caption('Spaceship Code: 0x%08x' % code)
    print "Spaceship Code: 0x%08x" % code


    big = PixelShip(code, location=screen.get_rect().center, scale=(144,144))
    ships = pygame.sprite.RenderUpdates(big)
    spinning = pygame.sprite.Group()

    clock = pygame.time.Clock()
    a=0
    spinloc = screen.get_rect().right - 20, screen.get_rect().bottom 
    sample = 50,50
    ex = PixelShip(big.code, group=ships, location=sample)
    while 1:
        clock.tick(8)
        a = (a + 15) % 360
        shift=False
        for event in pygame.event.get():
            if event.type == pygame.QUIT:
                pygame.quit()
                return
            elif event.type == pygame.MOUSEBUTTONDOWN:
                click = pygame.mouse.get_pos()
                for s in spinning.sprites():
                    if s.rect.collidepoint(*click):
                        code = s.code
                        shift=True
                if not shift:
                    part = big.get_component(click)
                    if not part:
                        code = randint(0, 2**32 - 1)
                    elif part == "core":
                        code = (code & 0x00ffffff) | randint(0, 255) << 24
                    elif part == "engine":
                        code = (code & 0xff00ffff) | randint(0, 255) << 16
                    elif part == "wing":
                        code = (code & 0xffff00ff) | randint(0, 255) << 8
                    elif part == "nose":
                        code = (code & 0xffffff00) | randint(0, 255)
                print "Spaceship Code: 0x%08x" % code
                pygame.display.set_caption('Spaceship Code: 0x%08x' % code)
                ex.move_to(spinloc)
                spinning.add(ex)
                ex = PixelShip(code, group=ships, location=sample)
                big.kill()
                big = PixelShip(code, location=screen.get_rect().center,
                        group=ships, scale=(144,144))
                shift = True
        if shift:
            for s in spinning.sprites():
                if s.rect.top < s.rect.height: s.kill()
            spinning.update(a,(0,-28))
        else:
            spinning.update(a,(0,0))
            ex.update(a,(0,0))
        ships.clear(screen, lambda s,r: s.fill((0,0,0),r))
        pygame.display.update(ships.draw(screen))

if __name__ == '__main__': 
    from sys import argv
    from random import randint
    if len(argv) > 2:
        main(*map(int,argv[1:4]))
    elif len(argv) == 2:
        num=argv[1]
        main(int(num[2:],16))
    else:
        main(randint(0, 2**32 - 1))
# ex: ts=4 sw=4 tw=72 expandtab
