__author__ = 'Anselmo'

#Explosion sprites
#https://www.touchdevelop.com/lvxdmali/info

from PPlay.window import *
from PPlay.gameimage import *
from PPlay.sprite import *
from PPlay.collision import *
from PPlay.sound import *

import random
import math

window = Window(512,512)
window.set_title("Hello World PPlay")
window.set_background_color((0,0,255))
window.update()

keyboard = window.get_keyboard()

SLOW_SPEED = 100
NORMAL_SPEED = 200
FAST_SPEED = 300

NEXT_ENEMY_SCORE_INC = 10000
TIME_SCORE_INC = 10

SHIP_TOP_BORDER = 300
SHIP_BOTTOM_BORDER = 448
SHIP_LEFT_BORDER = 0
SHIP_RIGHT_BORDER = 448

ANIMATION_DURATION = 500
AUDIO_FADEOUT = 500
SHOT_DELAY_TIME = 500
SHOT_DELAY = window.delta_time()*SHOT_DELAY_TIME
SPAWN_DELAY = 1000
INVINCIBILITY_DELAY = 5000
FIRST_ENEMIES_ARRIVAL_TIME = 10000


MAX_LEVEL = 4

OPENNING = 0
INITIALIZATION = 1
PLAYING = 2
ENDING = 3

SMALL = 0
BIG = 1
ASTEROIDSMALL = 2
ASTEROID = 3
ENEMYFIGHTER = 4


score = 0
lives = 4
level = 0
nextEnemyScore = 10000
beginningTime = 0

gameLoopState = OPENNING

background = dict(image = GameImage("spacebackgroundseamless.jpg"), scrollSpeed = 2000)


fighterSpeed = 200
shipSpeed = 100
shotSpeed = 300

audio = Sound("entrance.ogg")
#soundtrack = Sound("lenoverde.ogg")
explosionHit = Sound("explosion-01.ogg")
explosion = Sound("explosion-03.ogg")

def drawBackground(background):
    posx,posy = background['image'].x,background['image'].y
    scrollSpeed = background['scrollSpeed']
    image = background['image']
    newPosx = posx
    newPosy = posy+scrollSpeed*window.delta_time()
    if (newPosy>image.height):
        newPosy = 0
    image.set_position(newPosx,newPosy-image.height)
    image.draw()
    image.set_position(newPosx,newPosy)
    image.draw()


def spaceFighterPositionUpdate(spaceFighter):

    spaceFighter['sprite'].move_key_x(fighterSpeed*window.delta_time())
    spaceFighter['sprite'].move_key_y(fighterSpeed*window.delta_time())

    if spaceFighter['sprite'].y>SHIP_BOTTOM_BORDER:
        spaceFighter['sprite'].y = SHIP_BOTTOM_BORDER
    if spaceFighter['sprite'].y<SHIP_TOP_BORDER:
        spaceFighter['sprite'].y = SHIP_TOP_BORDER
    if spaceFighter['sprite'].x>SHIP_RIGHT_BORDER:
        spaceFighter['sprite'].x = SHIP_RIGHT_BORDER
    if spaceFighter['sprite'].x<0:
        spaceFighter['sprite'].x = SHIP_LEFT_BORDER


def spaceFighterDraw(spaceFighter):
    spaceFighter['sprite'].update()
    spaceFighter['sprite'].draw()


def spaceFighterShotsPositionsUpdate(spaceFighterShots,shotSpeed):
    for shot in spaceFighterShots:
        shot.move_y(shotSpeed*window.delta_time())
        if (shot.y<shot.height):
            spaceFighterShots.remove(shot)


def spawnEnemy(level):

     choice = random.randint(0,level)

     if (choice == 0):
         enemy = dict(sprite = Sprite("smallship.png",4), angle = 0.0, type = SMALL )
         enemy['sprite'].set_total_duration(ANIMATION_DURATION)
     elif (choice == 1):
         enemy = dict(sprite = Sprite("spaceship.png",4), angle = 0.0, type = BIG )
         enemy['sprite'].set_total_duration(ANIMATION_DURATION)
     elif (choice == 2):
         enemy = dict(sprite = Sprite("asteroidsmall.png",1), angle = 0.0, type = ASTEROIDSMALL)
         enemy['sprite'].set_total_duration(ANIMATION_DURATION)
     elif (choice == 3):
         enemy = dict(sprite = Sprite("asteroid.png",8), angle = 0.0, type = ASTEROID)
         enemy['sprite'].set_total_duration(ANIMATION_DURATION)
     elif (choice == 4):
         enemy = dict(sprite = Sprite("enemyfighter.png",16), angle = 0.0, type = ENEMYFIGHTER)
         enemy['sprite'].set_total_duration(4*ANIMATION_DURATION)

     xposition = random.randint(enemy['sprite'].width/2,window.width-enemy['sprite'].width)
     yposition = -enemy['sprite'].height

     enemy['sprite'].set_position(xposition,yposition)
     enemy['shotTick'] = window.last_time

     return enemy


def firstEnemiesSpawn(enemies,beginningTime):
    if (window.last_time<beginningTime+FIRST_ENEMIES_ARRIVAL_TIME):
        window.draw_text("Enemies approaching",120,230,32,(255,255,0),"Arial",True,False)
        if (window.last_time>beginningTime+(FIRST_ENEMIES_ARRIVAL_TIME//2)):
            background['scrollSpeed']-=5
        if (background['scrollSpeed']<100):
            background['scrollSpeed']= 100


    elif enemies['firstTimeSpawn']:
        for i in range(0,5):
            enemies['list'].append(spawnEnemy(gameState['level']))
        enemies['firstTimeSpawn'] = False
        background['scrollSpeed'] -= 10


def moveShip(speed,angle,time):
    deltax = speed * math.cos(angle)*time
    deltay = speed * time
    return deltax,deltay

def moveEnemyFighter(speed,angle,time):
    deltax = 2*speed * math.cos(angle)*time
    deltay = speed * time
    return deltax,deltay

def moveSmallAsteroid(speed,time):
    deltax = 0 #random.randint(-speed,speed) * time
    deltay = speed * time

    return deltax,deltay

def moveAsteroid(speed,angle,time):
    deltax = math.sin(angle)*speed*time
    deltay = -math.cos(angle)*speed*time

    return deltax,deltay+(speed/2*time)


def enemyPositionUpdate(enemies,enemy,time, level):
    angle = enemy['angle']
    if (enemy['type'] == SMALL):
        deltax,deltay = moveShip(shipSpeed*1.5,angle,time)
    if (enemy['type'] == BIG):
        deltax,deltay = moveShip(shipSpeed,angle,time)
    elif (enemy['type'] == ASTEROID):
        deltax,deltay = moveAsteroid(shipSpeed,angle,time)
    elif (enemy['type'] == ASTEROIDSMALL):
        deltax,deltay = moveSmallAsteroid(shipSpeed*5,time)
    elif (enemy['type'] == ENEMYFIGHTER):
        deltax,deltay = moveEnemyFighter(shipSpeed,angle,time)

    angle += 0.01
    if (angle > math.pi):
        angle = -math.pi
    enemy['angle'] = angle


    enemy['sprite'].move_x(deltax)
    enemy['sprite'].move_y(deltay)

    if enemy['sprite'].y>SHIP_BOTTOM_BORDER+enemy['sprite'].height:
        enemies['list'].remove(enemy)
        enemies['list'].append(spawnEnemy(level))

def enemiesPositionUpdate(enemies,level):
    time = window.delta_time()
    for enemy in enemies['list']:
        enemyPositionUpdate(enemies,enemy,time,level)
        enemy['sprite'].update()
        enemy['sprite'].draw()

def enemiesDraw():
    for enemy in enemies['list']:
        enemy['sprite'].update()
        enemy['sprite'].draw()



def enemyShotsPositionsUpdate(enemyShots,shotSpeed):
    for shot in enemyShots:
        shot.move_y(shotSpeed*window.delta_time())
        if (shot.y>window.height):
            enemyShots.remove(shot)


def createSmallEnemyShot(enemy):
    shot = Sprite("shot1.png",1)
    shot.set_total_duration(0)
    shot.set_position(enemy['sprite'].x+enemy['sprite'].width//2, enemy['sprite'].y+enemy['sprite'].height+2)
    return shot

def createBigEnemyShot(enemy):
    shot1 = Sprite("shot1.png",1)
    shot1.set_total_duration(0)
    shot1.set_position(enemy['sprite'].x+enemy['sprite'].width//2-4, enemy['sprite'].y+enemy['sprite'].height)
    shot2 = Sprite("shot1.png",1)
    shot2.set_total_duration(0)
    shot2.set_position(enemy['sprite'].x+enemy['sprite'].width//2+4, enemy['sprite'].y+enemy['sprite'].height)
    return shot1,shot2

def createFighterEnemyShot(enemy):
    shots = []
    for i in range(-3,4):
        shot = Sprite("shot1.png",1)
        shot.set_total_duration(0)
        shot.set_position(enemy['sprite'].x+enemy['sprite'].width//2+i*8, enemy['sprite'].y+enemy['sprite'].height-abs(i)*4)
        shots.append(shot)
    return shots

def createEnemyShots(enemies):
    for enemy in enemies['list']:
        choice = random.randint(0,10)
        if choice>9:
            if (enemy['type'] != ASTEROIDSMALL and enemy['type'] != ASTEROID) and enemy['sprite'].y>0:
                currentTime = window.last_time
                if (enemy['type']==SMALL) and currentTime-enemy['shotTick']>5*SHOT_DELAY:
                        shot = createSmallEnemyShot(enemy)
                        enemies['shots'].append(shot)
                        enemy['shotTick'] = window.last_time
                elif (enemy['type']==BIG) and currentTime-enemy['shotTick']>SHOT_DELAY:
                    shot1,shot2 = createBigEnemyShot(enemy)
                    enemies['shots'].append(shot1)
                    enemies['shots'].append(shot2)
                    enemy['shotTick'] = window.last_time
                elif (enemy['type']==ENEMYFIGHTER) and currentTime-enemy['shotTick']>SHOT_DELAY:
                    shots = createFighterEnemyShot(enemy)
                    for shot in shots:
                        enemies['shots'].append(shot)
                    enemy['shotTick'] = window.last_time



def shotsDraw(shots):
    for shot in shots:
        shot.update()
        shot.draw()


def detectCollision(enemies,spaceFighter,gameState,explosions):
    spaceFighterCollided = False
    for enemy in enemies['list']:
        for shot in spaceFighter['shots']:
            if (shot.collided_perfect(enemy['sprite'])):
                explosionHit.play()
                explosionHit.fadeout(AUDIO_FADEOUT)
                spaceFighter['shots'].remove(shot)
                enemies['list'].remove(enemy)
                explosions.append(createExplosion(enemy['sprite'].x,enemy['sprite'].y))
                enemies['list'].append(spawnEnemy(level))
                if (enemy['type'] == ENEMYFIGHTER):
                    gameState['score'] += 2000
                elif (enemy['type'] == ASTEROID):
                    gameState['score'] += 1000
                elif (enemy['type'] == ASTEROIDSMALL):
                    gameState['score'] += 800
                elif (enemy['type'] == BIG):
                    gameState['score'] += 500
                elif (enemy['type'] == SMALL):
                    gameState['score'] += 200
                break

    if (spaceFighter['canCollide']):
        for enemy in enemies['list']:
            if (spaceFighter['sprite'].collided_perfect(enemy['sprite'])):
                explosion.play()
                explosion.fadeout(1000)
                enemies['list'].remove(enemy)
                enemies['list'].append(spawnEnemy(level))
                spaceFighterCollided = True
                break

        for shot in enemies['shots']:
            if (shot.collided_perfect(spaceFighter['sprite'])):
                explosion.play()
                explosion.fadeout(1000)
                enemies['shots'].remove(shot)
                spaceFighterCollided = True
                break

    if (spaceFighterCollided):
        spaceFighter['canCollide'] = False
        spaceFighter['collisionTick'] = window.last_time
        #spaceFighter['sprite'].set_sequence(4,8)
        explosions.append(createExplosion(spaceFighter['sprite'].x,spaceFighter['sprite'].y))
        #spaceFighter['sprite'].hide()
        gameState['lives'] -= 1

    return gameState


def createExplosion(posx,posy):
    explosion = dict(sprite = Sprite("explosion.png",20), clockTick = window.last_time)
    explosion['sprite'].set_position(posx,posy)
    explosion['sprite'].set_total_duration(ANIMATION_DURATION)
    return explosion

def explosionsUpdate(explosions):
    for explosion in explosions:
        if window.last_time-explosion['clockTick']>500:
            explosions.remove(explosion)

def explosionsDraw(explosions):
    for explosion in explosions:
        explosion['sprite'].update()
        explosion['sprite'].draw()




def keyBoardInput(spaceFighter):

    if (keyboard.key_pressed("ESC")):
        window.close()
    elif (keyboard.key_pressed("SPACE")):
        currentTime = window.last_time
        if (currentTime-spaceFighter['shotTick']>SHOT_DELAY//2) and spaceFighter['sprite'].drawable:
            shot = Sprite("shot1.png",1)
            shot.set_total_duration(0)
            shot.set_position(spaceFighter['sprite'].x+spaceFighter['sprite'].width//2-4,spaceFighter['sprite'].y+2)
            spaceFighter['shots'].append(shot)
            spaceFighter['shotTick'] = window.last_time


####################################################################################################



def openning(gameLoopState):

    window.set_background_color((0,0,255))
    window.draw_text("Press SPACE to start",100,230,40,(255,255,0),"Arial",True,True)
    window.draw_text("LabGames 2014-2",130,300,40,(255,255,0),"Helvetic",True,True)

    if (keyboard.key_pressed("SPACE")):
        gameLoopState = INITIALIZATION

    return gameLoopState


def initialization(gameLoopState):

    background['scrollSpeed'] = 2000

    gameState = dict(score = 0, lives = 3, level = 0, beginningTime = window.last_time, nextEnemyScore = NEXT_ENEMY_SCORE_INC)

    spaceFighter = dict( sprite = Sprite("spacefighter.png",8),speed = NORMAL_SPEED,\
                    shotTick = window.delta_time(), canCollide = True, collisionTick = window.delta_time(), firstTimeSpawn = True,\
                    shots = [])
    spaceFighter['sprite'].set_sequence(0,3)
    spaceFighter['sprite'].set_position(window.width//2,window.height - spaceFighter['sprite'].height*2)
    spaceFighter['sprite'].set_total_duration(ANIMATION_DURATION)

    explosions = []


    enemies = dict(list = [], shots = [], firstTimeSpawn = True )

    audio.play()
    window.delay(1000)
    #soundtrack.play()
    gameLoopState = PLAYING

    return gameLoopState, gameState, spaceFighter, enemies, explosions



####################################################################################################


def gameStage(gameLoopState,gameState,spaceFighter,enemies,explosions):

    drawBackground(background)

    if (not spaceFighter['canCollide']):
        if (window.last_time-spaceFighter['collisionTick']>INVINCIBILITY_DELAY//4):
            spaceFighter['sprite'].unhide()
        if (window.last_time-spaceFighter['collisionTick']>INVINCIBILITY_DELAY):
            spaceFighter['sprite'].set_sequence(0,3)
            spaceFighter['canCollide'] = True
    keyBoardInput(spaceFighter)

    firstEnemiesSpawn(enemies,gameState['beginningTime'])

    spaceFighterPositionUpdate(spaceFighter)

    spaceFighterShotsPositionsUpdate(spaceFighter['shots'],-shotSpeed)

    enemiesPositionUpdate(enemies,gameState['level'])

    #createEnemyShots(enemies)

    #enemyShotsPositionsUpdate(enemies['shots'],shotSpeed)

    spaceFighterDraw(spaceFighter)

    enemiesDraw()

    shotsDraw(spaceFighter['shots'])

    #shotsDraw(enemies['shots'])

    explosionsUpdate(explosions)

    explosionsDraw(explosions)

    detectCollision(enemies,spaceFighter,gameState,explosions)

    if  gameState['score']>gameState['nextEnemyScore']:
        newSpaceShip = spawnEnemy(gameState['level'])
        enemies['list'].append(newSpaceShip)
        gameState['nextEnemyScore'] += NEXT_ENEMY_SCORE_INC
        gameState['level'] += 1
        if (gameState['level']>MAX_LEVEL):
            gameState['level'] = MAX_LEVEL


    gameState['score'] += window.delta_time()*TIME_SCORE_INC

    window.draw_text("Score: "+str(int(gameState['score'])),5,5,30,(255,255,255),"Arial",True,False)
    window.draw_text("Lives: "+str(int(gameState['lives'])),400,5,30,(255,255,255),"Arial",True,False)

    if (gameState['lives']<=0):
        gameLoopState = ENDING
        soundtrack.fadeout(10000)

    return gameLoopState, gameState, spaceFighter, enemies, explosions

####################################################################################################

def ending(gameLoopState,gameState):

    window.draw_text("Your score was: "+str(int(gameState['score'])),120,230,32,(255,255,0),"Arial",True,False)
    window.draw_text("Continue(Y/N)",170,300,32,(255,255,0),"Arial",True,False)

    if keyboard.key_pressed("y"):
        gameLoopState = OPENNING
    elif keyboard.key_pressed("n"):
        window.close()
    return gameLoopState

####################################################################################################


gameLoopState = OPENNING

while True:
    if (gameLoopState == OPENNING):
        gameLoopState = openning(gameLoopState)
    elif (gameLoopState == INITIALIZATION):
        gameLoopState, gameState, spaceFighter, enemies, explosions  = initialization(gameLoopState)
    elif (gameLoopState == PLAYING):
        gameLoopState,gameState, spaceFighter, enemies, explosions = gameStage(gameLoopState,gameState, spaceFighter,\
                                                                               enemies, explosions)
    else:
        gameLoopState = ending(gameLoopState,gameState)
    window.update()


