# SYMBIAN_UID = 0xA0002E1F

# --------------------------
# A S T E R O I D Z
# --------------------------

import appuifw
import e32
import sysinfo
import audio
from graphics import *
from key_codes import *

import math
import time
import random

RES_X = sysinfo.display_pixels()[0]
RES_Y = sysinfo.display_pixels()[1]


SHOOT_MAX_STEPS     = 30        # shoot steps duration (frames)
MAX_MOVEMENT_SPEED  = 14        # rotation speed
MAX_DIRECTION_SPEED = 8         # movement speed
TIME_PER_SHOOT      = 0.5       # shoot interval


# K E Y B O A R D
class Keyboard(object):
    def __init__(self,onevent=lambda:None):
        self._keyboard_state={}
        self._downs={}
        self._onevent=onevent
    def handle_event(self,event):
        if event['type'] == appuifw.EEventKeyDown:
            code=event['scancode']
            if not self.is_down(code):
                self._downs[code]=self._downs.get(code,0)+1
            self._keyboard_state[code]=1
        elif event['type'] == appuifw.EEventKeyUp:
            self._keyboard_state[event['scancode']]=0
        self._onevent()
    def is_down(self,scancode):
        return self._keyboard_state.get(scancode,0)
    def pressed(self,scancode):
        if self._downs.get(scancode,0):
            self._downs[scancode]-=1
            return True
        return False


# P O I N T
class Point(object):
    def __init__(self, x, y):
        self.x = x
        self.y = y


# R E C T A N G LE
class Rectangle(Point):
    def __init__(self, x, y, width, height):
        self.x = x
        self.y = y
        self.width  = width
        self.height = height

    def translate(self, x, y):
        self.x = self.x + x
        self.y = self.y + y


    def contains(self, x, y):
        return ( self.x <= x and self.right >= x ) and ( self.y <= y and self.bottom >= y )
    
    def containsPoint(self, pt):
        return self.contains(pt.x, pt.y)


    def get_points(self):
        return (Point(self.x, self.y), Point(self.right, self.y), Point(self.right, self.bottom), Point(self.x, self.bottom))


    def intersect(self, rect):
        for line in rect.get_points():
            if self.containsPoint(line):
                return True
        return False
        
        
    def __getattr__(self, name):
        if name == "bottom": return self.y + self.height
        if name == "right":  return self.x + self.width
        raise AttributeError



# R O C K S   1
class Rock1(object):

    MAX_MOVEMENT = 2.5

    def __init__(self, x, y):
        self.xmove  = random.randrange(-Rock1.MAX_MOVEMENT, Rock1.MAX_MOVEMENT, 1, float)
        self.ymove  = random.randrange(-Rock1.MAX_MOVEMENT, Rock1.MAX_MOVEMENT, 1, float)
        self.matrix = [(0,7), (4,0), (7, 3), (10,2), (11,4), (9,7), (10,9), (7,11), (6,9), (3,10)]
        self.rect   = Rectangle(x, y, max(map(lambda line: line[0], self.matrix)), max(map(lambda line: line[1], self.matrix)))

    def update(self):
        self.rect.translate(self.xmove, self.ymove)
        
        if self.rect.x > RES_X:
            self.rect.x = -self.rect.width
        elif self.rect.right < 0:
            self.rect.x = RES_X

        if self.rect.y > RES_Y:
            self.rect.y = -self.rect.height
        elif self.rect.bottom < 0:
            self.rect.y = RES_Y

    def get_matrix(self):
        return map(lambda line: (line[0] + self.rect.x, line[1] + self.rect.y), self.matrix)


    def get_position(self):
        return Point(self.rect.x, self.rect.y)

    def draw(self, drawable):
        drawable.polygon(self.get_matrix(), 0xFFFFFF, None)


    def get_rect(self):
        return self.rect


# R O C K S   2
class Rock2(Rock1):

    MAX_MOVEMENT = 1.5

    def __init__(self, x, y):
        self.xmove  = random.randrange(-Rock2.MAX_MOVEMENT, Rock2.MAX_MOVEMENT, 1, float)
        self.ymove  = random.randrange(-Rock2.MAX_MOVEMENT, Rock2.MAX_MOVEMENT, 1, float)
        self.matrix = [(0,8), (5,1), (10,0), (14,0), (21,7), (18,16), (13,16), (12,10), (8,17), (2,13), (5,9)]
        self.rect   = Rectangle(x, y, max(map(lambda line: line[0], self.matrix)), max(map(lambda line: line[1], self.matrix)))


# R O C K S   3
class Rock3(Rock1):

    MAX_MOVEMENT = 0.5

    def __init__(self, x, y):
        self.xmove  = random.randrange(-Rock3.MAX_MOVEMENT, Rock3.MAX_MOVEMENT, 1, float)
        self.ymove  = random.randrange(-Rock3.MAX_MOVEMENT, Rock3.MAX_MOVEMENT, 1, float)
        self.matrix = [(21,0),(31, 15), (46, 5), (56, 20), (48, 29), (53, 42), (29, 51), (10, 47), (0, 32), (3, 9)]
        self.rect   = Rectangle(x, y, max(map(lambda line: line[0], self.matrix)), max(map(lambda line: line[1], self.matrix)))


# E N E M Y
class Enemy(Rock1):

    MAX_MOVEMENT = 1.5

    def __init__(self, x, y, max_steps = 10, handle_remove=None):
        self.xmove  = random.randrange(-Enemy.MAX_MOVEMENT, Enemy.MAX_MOVEMENT, 1, float)
        self.ymove  = random.randrange(-Enemy.MAX_MOVEMENT, Enemy.MAX_MOVEMENT, 1, float)
        self.steps   = 0
        self.n_steps = max_steps
        self.shoot   = None
        self.handle_remove = handle_remove
        self.matrix  = [(9,0), (13,0), (16,4), (23,8), (16,12), (7, 12), (0,8), (7,4), (9,0), (7,4), (16,4), (23,8), (0,8), (7,4)]
        self.rect    = Rectangle(x, y, max(map(lambda line: line[0], self.matrix)), max(map(lambda line: line[1], self.matrix)))

    def update(self):
        Rock1.update(self)
                
        if self.shoot:
            self.shoot.update()
        
        self.steps += 1
        if self.steps > 200:
            self.n_steps -= 1
            if self.n_steps < 0:
                if self.handle_remove:
                    self.handle_remove(self)
                    return
            self.steps = 0
            self.xmove = random.randrange(-Enemy.MAX_MOVEMENT, Enemy.MAX_MOVEMENT, 1, float)
            self.ymove = random.randrange(-Enemy.MAX_MOVEMENT, Enemy.MAX_MOVEMENT, 1, float)
            
            if self.shoot is None:
                self.shoot = Shoot(self.get_position(), Point(random.randrange(-8,8), random.randrange(-8,8)), handle_remove = self.handle_remove_shoot)

    def handle_remove_shoot(self, element):
        self.shoot = None

    def draw(self, drawable):
        Rock1.draw(self, drawable)
        if self.shoot:
            self.shoot.draw(drawable)


# S H O O T
class Shoot(object):
    
    def __init__(self, position, direction, handle_remove=None):
        self.point     = position;
        self.direction = direction;
        self.steps = 0;
        self.handle_remove = handle_remove;

    def update(self):
        self.steps = self.steps+1;
        self.point.x = self.point.x + self.direction.x;
        self.point.y = self.point.y + self.direction.y;
        
        if self.point.x  > RES_X:
            self.point.x = 0
        elif self.point.x  < 0:
            self.point.x = RES_X

        if self.point.y > RES_Y:
            self.point.y = 0;
        elif self.point.y  < 0:
            self.point.y = RES_Y


        if self.steps > SHOOT_MAX_STEPS:
            if self.handle_remove:
                self.handle_remove(self)


    def get_position(self):
        return self.point

    def get_rect(self):
        return [(self.point.x, self.point.y), (self.point.x+2, self.point.y+2)]

    def draw(self, drawable):
        drawable.rectangle(self.get_rect(), 0xFFFFFF, 0xFFFFFF)


# S H I P
class Ship(object):
    def __init__(self, size, x, y):
        self.matrix = [[-size/2, -size/2],[size/2, 0], [-size/2, size/2], [-size/2 + size/6, 0]]
        self.rotation = 0;
        self.x = x + (size/2);
        self.y = y + (size/2);
        self.centerx = 0
        self.centery = 0
        self.degrees   = []
        self.radius    = []
        self.size = size
        
        for a in range( len(self.matrix) ):
            line = self.matrix[a];
            hyp  = float(math.sqrt(math.pow((self.centerx - line[0]), 2) + math.pow((self.centery - line[1]), 2)));
            opposite = float(self.centery - line[1]);
            adiacent = float(self.centerx - line[0]);
            sina = float(opposite/hyp);
            grad = float(sina*180/math.pi);
            
            if line[0] < self.centerx:
                grad = grad + 180;
            else:
                grad = -grad;
            
            self.degrees.append(grad);
            self.radius.append(hyp);
        
    def move(self, x, y):
        self.x = self.x + x;
        self.y = self.y + y;
        
        if self.x > RES_X:
            self.x = 0
        elif self.x < 0:
            self.x = RES_X

        if self.y > RES_Y:
            self.y = 0
        elif self.y < 0:
            self.y = RES_Y
        

    def rotate(self, degrees=0):
        self.rotation = self.rotation + degrees;
        radiants = 0.0
        
        for a in range( len(self.matrix) ):
            line   = self.matrix[a]
            radius = self.radius[a]
            deg    = self.degrees[a]
            
            radiants = (math.pi/180 * (self.rotation + deg));
            line[0] = self.centerx + (math.cos(radiants) * radius);
            line[1] = self.centery + (math.sin(radiants) * radius);            

    def get_matrix(self):
        return map(lambda line: (line[0] + self.x, line[1] + self.y), self.matrix)

    def get_position(self):
        return Point(self.x, self.y)

    def set_position(self, point):
        self.x = point.x
        self.y = point.y

    def draw(self, drawable):
        drawable.polygon(self.get_matrix(), 0xFFFFFF, None)


# G A M E
class Game(object):
    def __init__(self):
        self.bg_color = (0,0,0)
        self.line_color = (255,255,255)
        self.exit_flag = 0
        self.score = 0
        
        self.direction = 0.0
        self.speed1 = 0.0
        self.speed2 = 0.0
        self.level  = 1
        self.lives  = [Ship(12, 5, 5),Ship(12, 20, 5),Ship(12, 35, 5)]
        
        for line in self.lives:
            line.rotate(-90)
        
        self.status = 0
        self.status_counter = 0
        self.shoots = []
        self.rocks  = [ Rock3(random.randrange(RES_X), random.randrange(RES_Y),), 
                        Rock2(random.randrange(RES_X), random.randrange(RES_Y),),
                        Rock2(random.randrange(RES_X), random.randrange(RES_Y),),
                        Enemy(random.randrange(RES_X), random.randrange(RES_Y), 30, handle_remove=self.handle_remove_enemy)
                        ]
        self.enemy  = None
        self.max_speed1 = 10.0
        self.max_speed2 = 10.0
        
        self.last_shoot = (time.time())

        self.keyboard = Keyboard()
        self.ship = None # Ship(12, 120, 160)
        self.ship_rect = Rectangle(80, 120, 80, 80)


        self.canvas = appuifw.Canvas(event_callback = self.keyboard.handle_event, redraw_callback = self.redraw)
        self.canvas.bind(EKeyUpArrow, lambda: self.onUpArrow())
        self.canvas.bind(EKey2, lambda: self.onUpArrow())
        
        self.canvas.bind(EKeySelect, lambda: self.onEnterArrow())
        self.canvas.bind(EKey5, lambda: self.onEnterArrow())
        
        self.draw   = Draw(self.canvas)
        appuifw.app.body = self.canvas
        self.redraw(())

    def redraw(self, rect):
        self.draw.clear(self.bg_color)
        self.layout()
        

    def layout(self):

        for line in self.lives:
            line.draw(self.draw)
    
        if self.status == 0:
            for line in self.rocks:
                line.update()
                line.draw(self.draw)    # draw each rock            
        elif self.status == 1 and self.ship:
            matrix = self.ship.get_matrix()        
            rocks_tmp = []
            if self.rocks:
                for line in self.rocks:
                    for shoot in self.shoots:
                        if line.rect.containsPoint(shoot.get_position()):
                            if isinstance(line, Rock3):
                                rocks_tmp.append(Rock2(line.get_position().x, line.get_position().y))
                                rocks_tmp.append(Rock2(line.get_position().x, line.get_position().y))
                            elif isinstance(line, Rock2):
                                rocks_tmp.append(Rock1(line.get_position().x, line.get_position().y))
                                rocks_tmp.append(Rock1(line.get_position().x, line.get_position().y))
                            self.rocks.remove(line)
                            self.shoots.remove(shoot)
                            break
                    for pt in matrix:
                        if line.rect.contains(pt[0], pt[1]):
                            if self.lives:
                                self.status = 0
                                self.status_counter = 0
                                self.ship = None
                                self.lives.pop()
                                return
                            else:
                                appuifw.note(u"You die")
                                self.quit()

                self.ship.draw(self.draw)   # draw the sip

                if rocks_tmp:
                    self.rocks = self.rocks + rocks_tmp

                for line in self.rocks:
                    line.update()
                    line.draw(self.draw)    # draw each rock

                for line in self.shoots:
                    line.update()
                    line.draw(self.draw)    # draw the shoots
            else:
                appuifw.note(u"Compliments :)")
                self.quit()




    def fire(self):
        if self.ship:
            if (time.time()- self.last_shoot) > TIME_PER_SHOOT or len(self.shoots) < 2:
                
                direction = Point(0,0)
                direction.x = math.cos(math.pi/180 * self.ship.rotation)*10;
                direction.y = math.sin(math.pi/180 * self.ship.rotation)*10;
                s = Shoot(self.ship.get_position(), direction, handle_remove = self.handle_remove_shoot)
                self.shoots.append(s)
                
                self.last_shoot = time.time()


    def handle_remove_shoot(self, instance):
        try:
            self.shoots.remove(instance)
        except:
            pass


    def handle_remove_enemy(self, instance):
        try:
            self.rocks.remove(instance)
        except:
            pass


    def quit(self):
        self.exit_flag = 1


    def onUpArrow(self):
        if self.ship:
            self.max_speed1 = abs((math.cos(math.pi/180 * self.ship.rotation))*10);
            self.max_speed2 = abs((math.sin(math.pi/180 * self.ship.rotation))*10);


    def onEnterArrow(self):
        self.fire()        


    def run(self):
        appuifw.app.exit_key_handler = self.quit
        while not self.exit_flag:
        
            if self.status == 0:
                self.status_counter += 1
                
                if self.status_counter > 200:
                    for line in self.rocks:
                        if not self.ship_rect.intersect(line.get_rect()):
                            self.ship = Ship(12, 120, 160)
                            self.direction = 0
                            self.speed1 = 0
                            self.speed2 = 0
                            self.status = 1
                            self.status_counter = 0
                            break
               
            elif self.status == 1 and self.ship:
                if self.keyboard.is_down(EScancodeLeftArrow) or self.keyboard.is_down(EKey4):
                    if self.direction > - MAX_DIRECTION_SPEED:
                        self.direction = self.direction - 0.2
                elif self.keyboard.is_down(EScancodeRightArrow) or self.keyboard.is_down(EKey6):
                    if self.direction < MAX_DIRECTION_SPEED:
                        self.direction = self.direction + 0.2

                if not self.keyboard.is_down(EScancodeLeftArrow) and not self.keyboard.is_down(EScancodeRightArrow) \
                    and not self.keyboard.is_down(EKey4) and not self.keyboard.is_down(EKey6):
                    self.direction = self.direction / 1.05

                self.ship.rotate(self.direction);

                # movement
                if self.keyboard.is_down(EScancodeUpArrow) or self.keyboard.is_down(EKey2):
                    if self.speed1 > -self.max_speed1 and self.speed1 < self.max_speed1:
                        self.speed1 += (math.cos(math.pi/180 * self.ship.rotation));

                    if self.speed2 > -self.max_speed1 and self.speed2 < self.max_speed2:
                        self.speed2 += (math.sin(math.pi/180 * self.ship.rotation));
                else:
                    self.speed1 = self.speed1/1.01
                    self.speed2 = self.speed2/1.01

                self.ship.move(self.speed1/15, self.speed2/15);

            self.redraw(())
            e32.ao_yield()



running = 1
appuifw.app.screen = 'full'

appuifw.note(u"Rember this is just a test\nUse 2,4,5,6 or Up, Left, Right, Enter")
game = Game()
game.run()