import osl, pspmp3
import level
import random
import copy
import math
import time
import urllib

osl.initGfx(osl.PF_8888, True)
osl.setQuitOnLoadFailure(True) 
osl.initConsole()
osl.initAudio()

## Sounds Loading
sndMenu = osl.Sound("snd/menu.wav")
sndCancel = osl.Sound("snd/cancel.wav")
sndDestroy_group = osl.Sound("snd/destroy_group.wav")
sndLose = osl.Sound("snd/lose.wav")
sndApplause = osl.Sound("snd/applause.wav")
sndLaunch = osl.Sound("snd/launch.wav")

## Fonts Loading.
font = osl.Font("gfx/font1.oft")
font_info_level = osl.Font("gfx/font.oft")
osl.setBkColor(osl.RGBA(255,255,255,0))

## Image Loading.
splash = osl.Image("gfx/splash/splash.png", osl.IN_RAM, osl.PF_8888)
maskSplash = osl.Image((480, 272), osl.IN_RAM, osl.PF_8888)
maskSplash.clear(osl.RGBA(0, 0, 0, 0))
intro = osl.Image("gfx/intro.png", osl.IN_RAM, osl.PF_8888)
pressStartImg = osl.Image("gfx/press_start.png", osl.IN_RAM, osl.PF_8888)
pause = osl.Image("gfx/pause.png", osl.IN_RAM, osl.PF_8888)
loseImg = osl.Image("gfx/lose.png", osl.IN_RAM, osl.PF_8888)

downloadImg = osl.Image("gfx/download.png", osl.IN_RAM, osl.PF_8888)
downloadImg.x = (480/2)-(downloadImg.sizeX/2)
downloadImg.y = (272/2)-(downloadImg.sizeY/2)
chooseLevelSetImg = osl.Image("gfx/chooseLevelSet.png", osl.IN_RAM, osl.PF_8888)
freccia_s = osl.Image("gfx/freccia_s.png", osl.IN_RAM, osl.PF_8888)
freccia_d = osl.Image("gfx/freccia_d.png", osl.IN_RAM, osl.PF_8888)

info_menu = osl.Image("gfx/info_menu.png", osl.IN_RAM, osl.PF_8888)
info_menu.x = 10
info_menu.y = 272 - info_menu.sizeY -12
menuImg = [osl.Image("gfx/menu_1.png", osl.IN_RAM, osl.PF_8888),\
           osl.Image("gfx/menu_2.png", osl.IN_RAM, osl.PF_8888),\
           osl.Image("gfx/menu_3.png", osl.IN_RAM, osl.PF_8888),\
           osl.Image("gfx/menu_4.png", osl.IN_RAM, osl.PF_8888)]
menuImg[0].x = 28
menuImg[0].y = 22
menuImg[1].x = 165-50
menuImg[1].y = 116-31
menuImg[2].x = 302-83
menuImg[2].y = 209-62
menuImg[3].x = 302
menuImg[3].y = 209
menuImgSel = [osl.Image("gfx/menu_1_sel.png", osl.IN_RAM, osl.PF_8888),\
              osl.Image("gfx/menu_2_sel.png", osl.IN_RAM, osl.PF_8888),\
              osl.Image("gfx/menu_3_sel.png", osl.IN_RAM, osl.PF_8888),\
              osl.Image("gfx/menu_4_sel.png", osl.IN_RAM, osl.PF_8888)]
menuImgSel[0].x = 28
menuImgSel[0].y = 22
menuImgSel[1].x = 165-50
menuImgSel[1].y = 116-31
menuImgSel[2].x = 302-83
menuImgSel[2].y = 209-62
menuImgSel[3].x = 302
menuImgSel[3].y = 209

mask = osl.Image((480, 272), osl.IN_VRAM, osl.PF_8888) # It MUST be in V_RAM, othervise setDrawBuffer() doesn't work currectly.
mask.clear(osl.RGBA(255, 255, 255, 255)) 
bg = osl.Image("gfx/bg.png", osl.IN_RAM, osl.PF_8888)
wall = osl.Image("gfx/wall.png", osl.IN_RAM, osl.PF_8888)
wall1 = osl.Image("gfx/wall1.png", osl.IN_RAM, osl.PF_8888)

mascotte = [osl.Image("gfx/mascotte/%d.png" %x, osl.IN_RAM, osl.PF_8888) for x in range(1, 7)]

imageBalls = []
for i in enumerate(['blue','red','orange','pink','black','grey','green','yellow']):
    imageBalls.append(osl.Image("gfx/%s_ball.png" %i[1], osl.IN_RAM, osl.PF_8888))
imageBalls_little = [] # for level preview
for i in enumerate(['blue','red','orange','pink','black','grey','green','yellow']):
    imageBalls_little.append(osl.Image("gfx/%s_ball_l.png" %i[1], osl.IN_RAM, osl.PF_8888))
balls_effect = [osl.Image("gfx/balls_effect/%d.png" %x, osl.IN_RAM, osl.PF_8888) for x in range(8)]

throwerImg = osl.Image("gfx/thrower_179.png")
throwerImg.centerX = throwerImg.sizeX // 2
throwerImg.centerY = throwerImg.sizeY // 2
##################

class Game:
        def __init__(self, levelSet, nLevel, maxLevel):
            self.curLevel = nLevel
            self.maxLevel = maxLevel
            self.levelSet = levelSet
            if self.levelSet == 'random':
                level.initialize(self.levelSet)
            self.board = Board([20/1.5+40, 60/1.5-15], self.curLevel)
            self.keys = [False, False, False]
            self.pad = None
            self.returnToMainMenu = False
            self.pause = False
            self.timerInfoLev = 0

        def run(self, pad):
            self.pad = pad

            self.board.render()

            if not self.board.lose and not self.pause:
                self.update()
                if self.pad.pressed_start and not self.board.win:
                    time.sleep(0.2)
                    self.pause = True
            elif self.board.lose:
                loseImg.draw()
                if self.pad.pressed_start:
                    self.keys[2] = False
                    self.board = Board([20/1.5+40, 60/1.5-15], self.curLevel)
                elif self.pad.pressed_select:
                    time.sleep(1)
                    self.returnToMainMenu = True
            elif self.pause:
                pause.draw()
                if self.pad.pressed_start:
                    time.sleep(0.2)
                    self.pause = False
                if self.pad.pressed_select:
                    time.sleep(0.2)
                    self.returnToMainMenu = True
                

        def update(self):
            osl.setTextColor(osl.RGBA(0,0,0,255))
            if self.timerInfoLev < 70:
                font_info_level.set()
                if self.levelSet != 'random':
                    osl.drawString(100,170, "LEVEL %d" %self.curLevel)
                else:
                    osl.drawString(90-15,170, "RANDOM LEVEL")
                self.timerInfoLev += 1
            if self.board.win:
                if self.levelSet != 'random':
                    font_info_level.set()
                    osl.setTextColor(osl.RGBA(0,0,0,255))
                    osl.drawString(105,60, "LEVEL")
                    osl.drawString(75,90, "COMPLETED IN")
                    time_lvl = (self.board.time[1][0]*60 + self.board.time[1][1]) - (self.board.time[0][0]*60 + self.board.time[0][1])
                    osl.drawString(75,120, str(time_lvl) + " seconds")
                    font.set()
                    osl.drawString(71,180, "(press X to continue)")
                    if time_lvl < level.getHighScore(self.levelSet.split('.')[0], self.curLevel) or \
                       level.getHighScore(self.levelSet.split('.')[0], self.curLevel) == None: # New Record
                        font_info_level.set()
                        osl.setTextColor(osl.RGBA(0,0,255,255))
                        osl.drawString(80,150, "NEW RECORD!!!")
                    if self.pad.pressed_cross:
                        if time_lvl < level.getHighScore(self.levelSet.split('.')[0], self.curLevel) or \
                           level.getHighScore(self.levelSet.split('.')[0], self.curLevel) == None: # New Record (salvo nuovo record)
                            level.setHighScore(self.levelSet.split('.')[0], self.curLevel, time_lvl)
                        if self.curLevel != self.maxLevel:
                            sndApplause.play()
                            self.curLevel += 1
                            self.board = Board([20/1.5+40, 60/1.5-15], self.curLevel)
                            self.timerInfoLev = 0
                            time.sleep(0.2)
                        else:
                            self.returnToMainMenu = True
                            time.sleep(0.2)
                else:
                    self.returnToMainMenu = True
                    time.sleep(0.2)
            else:
                self.runActions()
                self.board.updateAnimation()

        def runActions(self):
            if self.keys[0]:
                self.board.incAngle()
                self.keys[0] = False
            if self.keys[1]:
                self.board.decAngle()
                self.keys[1] = False
            if self.keys[2]:
                self.board.throwBall()
                self.keys[2] = False
                sndLaunch.play()
            if self.pad.pressed_left or self.pad.held_left:
                        self.keys[0] = True
                        if not self.board.inAnimation: self.board.downTimer += 1
            elif self.pad.pressed_right or self.pad.held_right:
                        self.keys[1] = True
                        if not self.board.inAnimation: self.board.downTimer += 1
            elif self.pad.pressed_cross and len(self.board.balls) != 0:
                        self.keys[2] = True
            elif not self.board.inAnimation:
                    self.board.downTimer += 4

class Board(object):
    def __init__(self, pos, lvl):
        self.pos = pos
        self.size = [240/1.5, 350/1.5]
        self.nextBallPos = [[27/1.5, 324/1.5][0] + pos[0], [27/1.5, 324/1.5][1] + pos[1]]
        throwerPos = [[70/1.5, 264/1.5][0] + pos[0], [70/1.5, 264/1.5][1] + pos[1] ]
        self.thrower = Thrower(self.pos)
        self.balls = []
        self.throwAngle = 0
        self.thrownBall = None
        self.isTop8BallLine = True
        self.ballMatrix = [[None for i in range([12, 8][1]+2)] for i in range([12, 8][0])]
        self.animations = []
        self.inAnimation = False
        self.toDraw = True
        self.lose = False
        self.win = False
        self.downTimer = 0
        self.mascotteTim = 0
        self.offsetY = 0
        self.ball_with_effect = False
        self.perScendere = False
        self.time = [(int(time.strftime("%M")), int(time.strftime("%S"))), (None, None)]

        self.nLevel = lvl
        for i in level.ball_position[self.nLevel-1]:
            b = Ball(color = i[2])
            b.pos = [i[0], i[1]]
            self.addToBallMatrixWithoutCanc(b)
            self.balls.append(b)

        colors = []
        for ball in self.balls:
            if not ball.color in colors:
                colors.append(ball.color)
        self.ball = Ball(color = random.choice(colors))
        self.ball.setCenterPos(self.thrower.getCenterPos())
        self.nextBall = Ball(color = random.choice(colors))
        self.nextBall.setCenterPos(self.nextBallPos)

    def addToBallMatrixWithoutCanc(self, ball):
        ## To call when you wanna create a new level.
        pos = ball.getPosInBoard(self.pos, self.isTop8BallLine)
        self.ballMatrix[pos[0]][pos[1]] = ball

    def addToBallMatrix(self, ball):
        pos = ball.getPosInBoard(self.pos, self.isTop8BallLine)
        self.ballMatrix[pos[0]][pos[1]] = ball
        sameColor = self.getBallsWithSameColor(ball.color, pos, [])
        if pos[0] == 10 - self.offsetY and len(sameColor) < 3: 
            sndLose.play()                      
            self.lose = True                    
            return                              
        if len(sameColor) >= 3:
            self.removeFromBallMatrix(sameColor)
        looseBalls = self.getLooseBalls()
        self.removeFromBallMatrix(looseBalls)
        if len(sameColor) >= 3:
            sndDestroy_group.play()
            sameColor.extend(looseBalls)
            self.animations.append(ballAnimation(sameColor))
            self.inAnimation = True

    def getLooseBalls(self):
        visitedBallsMatrix = [[False for i in range([11, 8][1]+2)] for i in range([11, 8][0])]
        for j in xrange([11, 8][1]):
            ball = self.ballMatrix[0][j]
            if ball:
                self.visitBallMatrix(visitedBallsMatrix, [0, j])

        looseBalls = []
        for ball in self.balls:
            boardPos = ball.getPosInBoard(self.pos, self.isTop8BallLine)
            if not visitedBallsMatrix[boardPos[0]][boardPos[1]]:
                looseBalls.append(ball)

        return looseBalls

    def visitBallMatrix(self, visitedBallMatrix, pos):
        for i in xrange(2):
            if pos[i] < 0 or pos[i] > [11, 8][i]:
                return

        pi = int(pos[0])
        pj = int(pos[1])
        ball = self.ballMatrix[pi][pj]
        if not ball or visitedBallMatrix[pi][pj]:
             return

        visitedBallMatrix[pi][pj] = True
        if (self.isTop8BallLine and pi % 2 == 0) or (not self.isTop8BallLine and pi % 2 == 1):
            for i in xrange(-1, 2):
                for j in xrange(-1, 2):
                    if (i,j) != (0,0) and (i,j) != (-1,-1) and (i,j) != (+1, -1):
                        self.visitBallMatrix(visitedBallMatrix, [pi+i, pj+j])
        else:
            for i in xrange(-1, 2):
                for j in xrange(-1, 2):
                    if (i,j) != (0,0) and (i,j) != (-1,+1) and (i,j) != (+1, +1):
                        self.visitBallMatrix(visitedBallMatrix, [pi+i, pj+j])


    def removeFromBallMatrix(self, balls):
        for ball in balls:
            pos = ball.getPosInBoard(self.pos, self.isTop8BallLine)
            self.ballMatrix[pos[0]][pos[1]] = None
            try:
                self.balls.remove(ball)
            except:
                pass

    def getBallsWithSameColor(self, color, pos, ballsFound=[]):
        for i in xrange(2):
            if pos[i] < 0 or pos[i] > [11, 8][i]:
                return []

        pi = int(pos[0])
        pj = int(pos[1])
        ball = self.ballMatrix[pi][pj]
        if not ball or color != ball.color or ball in ballsFound:
            return []

        ballsFound.append(ball)
        if (self.isTop8BallLine and pi % 2 == 0) or (not self.isTop8BallLine and pi % 2 == 1):
            for i in xrange(-1, 2):
                for j in xrange(-1, 2):
                    if (i,j) != (0,0) and (i,j) != (-1,-1) and (i,j) != (+1, -1):
                        self.getBallsWithSameColor(color, [pi+i, pj+j], ballsFound)
        else:
            for i in xrange(-1, 2):
                for j in xrange(-1, 2):
                    if (i,j) != (0,0) and (i,j) != (-1,+1) and (i,j) != (+1, +1):
                        self.getBallsWithSameColor(color, [pi+i, pj+j], ballsFound)

        return ballsFound

    def ceckWin(self):
        if len(self.balls) == 0 and not self.inAnimation:
            self.time[1] = (int(time.strftime("%M")), int(time.strftime("%S")))
            return True
        return
        
    def updateAnimation(self):
        if self.ceckWin():
            self.win = True
        
        if self.downTimer > 2000 and not self.thrownBall and not self.perScendere: # discesa parete
            self.downTimer = 0
            self.initializeBallsEffect()
            self.perScendere = True
            self.ball_with_effect = True
        elif self.downTimer > 2300 and self.perScendere: # e' passato troppo tempo, la parete scende e "spara" la palla
            self.throwBall()
            self.downTimer = 0
            self.initializeBallsEffect()
            self.perScendere = True
            self.ball_with_effect = True

        if len(self.animations) != 0:
            for anim in self.animations:
                if anim.isFinished():
                    self.inAnimation = False
                    self.animations.remove(anim)
                else:
                    anim.update()

        if self.thrownBall:
            ball = self.thrownBall
            ball.pos[0] = ball.pos[0] + math.cos(self.throwAngle)*6
            ball.pos[1] = ball.pos[1] - math.sin(self.throwAngle)*6

            if ball.pos[0]  <= self.pos[0]:
                ball.pos[0] = self.pos[0] + 1
                self.throwAngle  = math.pi - self.throwAngle
            if ball.pos[0] >= self.pos[0] -1  + self.size[0] - 2 * ball.radius:
                self.throwAngle  = math.pi - self.throwAngle

            for b in self.balls:
                if ball.collides(b):
                    self.toDraw = True
                    ball.roundPosAroundBall(b)
                    self.thrownBall = None
                    self.balls.append(ball)
                    self.addToBallMatrix(ball)
                    self.throwAngle = 0
                    ball = None
                    if self.perScendere:
                        self.perScendere = False
                        self.pos[1] += 21
                        self.offsetY += 1
                        for i in range(len(self.balls)):
                            if self.balls[i].pos[1] > 195-21:
                                sndLose.play()
                                self.lose = True
                            self.balls[i].pos[1] += 21
                    break
        
            if ball and ball.pos[1] <= self.pos[1]:
                            self.toDraw = True
                            ball.roundPosToBoardTop(self.pos, self.isTop8BallLine)
                            self.thrownBall = None
                            self.balls.append(ball)
                            self.addToBallMatrix(ball)
                            self.throwAngle = 0

    def drawMascotte(self):
        self.mascotteTim += 1
        TIMER = 140
        OFFSET = 7
        if self.lose:
            tile = mascotte[5]
            mascotte[5].x = 94-(47-31)
            mascotte[5].y = 226-(48-32)
        if self.mascotteTim < TIMER:
            tile = mascotte[0]
            mascotte[0].x = 94
            mascotte[0].y = 226
        elif self.mascotteTim >= TIMER and self.mascotteTim < TIMER+OFFSET:
            tile = mascotte[1]
            mascotte[1].x = 94
            mascotte[1].y = 226
        elif self.mascotteTim >= TIMER+OFFSET and self.mascotteTim < TIMER+(OFFSET*2):
            tile = mascotte[2]
            mascotte[2].x = 94
            mascotte[2].y = 226
        elif self.mascotteTim >= TIMER+(OFFSET*2) and self.mascotteTim < TIMER+(OFFSET*3):
            tile = mascotte[3]
            mascotte[3].x = 94
            mascotte[3].y = 226
        elif self.mascotteTim >= TIMER+(OFFSET*3) and self.mascotteTim < TIMER+(OFFSET*4):
            tile = mascotte[4]
            mascotte[4].x = 94
            mascotte[4].y = 226
        else:
            tile = mascotte[0]
            mascotte[0].x = 94
            mascotte[0].y = 226
            self.mascotteTim = 0
        tile.draw()

    def initializeBallsEffect(self):
        for ball in self.balls:
            ball.nEffect = 0

    def render(self):
        self.drawMascotte()                # Render della mascotte
        self.nextBall.render()             # Render della prossima palla
        self.ball.render()                 # Render della palla da lanciare
        self.thrower.render()              # Render del thrower
        if self.thrownBall:                # Render della palla lanciata
            self.thrownBall.render()
        if self.toDraw:                    # Render delle palle SENZA effetti
            mask.clear(osl.RGBA(255, 255, 255, 255)) 
            osl.setDrawBuffer(mask) 
            for ball in self.balls:
                ball.render()
            osl.setDrawBuffer(osl.DEFAULT_BUFFER)
            self.toDraw = False
        if self.perScendere:               # Render delle palle CON effetti
            for ball in self.balls:
                ball.render(self.ball_with_effect)                 
        for anim in self.animations:       # Render delle animazioni
	    anim.render() 
	if self.offsetY != 0:              # Render del muro
            for i in range(self.offsetY):
                if i != self.offsetY - 1:
                    wall1.x = 53
                    wall1.y = 24+(i*21)
                    wall1.draw()
                else:
                    wall.x = 53
                    wall.y = 24+(i*21)
                    wall.draw()
        font.set()
        osl.setTextColor(osl.RGBA(192,192,192,255))
        if self.nLevel != 'random':
            osl.drawString(323, 120, 'LEVEL: %d' %self.nLevel)
        else:
            osl.drawString(301, 120, 'RANDOM LEVEL')

    def incAngle(self):
        self.thrower.incAngle()

    def decAngle(self):
        self.thrower.decAngle()

    def throwBall(self):
        if self.thrownBall:
            return
        self.throwAngle = self.thrower.angle
        self.thrownBall = self.ball
        self.ball = self.nextBall
        self.ball.setCenterPos(self.thrower.getCenterPos())
        colors = []
        for ball in self.balls:
            if not ball.color in colors:
                colors.append(ball.color)
        self.nextBall = Ball(color = random.choice(colors))
        self.nextBall.setCenterPos(self.nextBallPos)

class Ball(object):
    def __init__(self, pos = [0,0], color = None):
        if color == None:
                self.color = random.randint(0, len(imageBalls)-1)
        else:
                self.color = color
        self.ballImage = imageBalls[self.color]
        self.radius = 7*1.5
        self.pos = copy.copy(pos)
        self.boardPos = [-1, -1]
        self.nEffect = 0

    def setCenterPos(self, pos):
        for a in xrange(2):
            self.pos[a] = pos[a] - self.radius

    def centerDistance(self, ball):
        a = self.pos[0] - ball.pos[0]
        b = self.pos[1] - ball.pos[1]
        return math.sqrt(a**2 + b**2)

    def collides(self, ball):
        return self.centerDistance(ball) <  self.radius + self.radius - 4

    def render(self, with_effect = False):
        if not with_effect:
            self.ballImage.x = int(self.pos[0])
            self.ballImage.y = int(self.pos[1])
            self.ballImage.draw()
        else:
            if self.nEffect >= 0:
                balls_effect[self.nEffect].x = int(self.pos[0])
                balls_effect[self.nEffect].y = int(self.pos[1])
                balls_effect[self.nEffect].draw()
            if self.nEffect < 7:
                self.nEffect += 1
            else:
                self.nEffect = -30

    def roundPosAroundBall(self, ball):
        a = float(self.pos[0] - ball.pos[0])
        b = float(self.pos[1] - ball.pos[1])
        beta = math.acos(a / math.sqrt(a**2 + b**2))
        piThirds = math.pi / 3.0
        beta = piThirds * math.floor(0.5 + beta/piThirds)
        if self.pos[1] < ball.pos[1]:
                beta = - beta
        self.pos[0] = 2 * self.radius * math.cos(beta) + ball.pos[0]
        self.pos[1] = 2 * self.radius * math.sin(beta) + ball.pos[1]


    def roundPosToBoardTop(self, offsetPos = (0,0) , isTop8BallLine = True):
        [i, j] = self.getPosInBoard(offsetPos, isTop8BallLine)

        if isTop8BallLine:
            compensation = 0
        else:
            compensation = self.radius

        self.pos[0] = int(j * self.radius * 2.0 + compensation)
        self.pos[1] = int(i * math.sin(math.pi/3.0) * self.radius * 2.0)

        self.pos[0] = offsetPos[0] + self.pos[0]
        self.pos[1] = offsetPos[1] + self.pos[1]

    def getPosInBoard(self, offsetPos = (0, 0), isTop8BallLine = True):
        x = self.pos[0] - offsetPos[0]
        y = self.pos[1] - offsetPos[1]

        i = math.floor(0.5 + y / (math.sin(math.pi/3.0) * self.radius * 2.0))
        if (isTop8BallLine and i % 2 == 0) or (not isTop8BallLine and i % 2 == 1):
            compensation = 0
        else:
            compensation = - self.radius
        j = math.floor(0.5 + (x - compensation)  / (self.radius * 2.0))

        return [int(i),int(j)]

class Thrower(object):
    def __init__(self, offsetPos):
        self.size = [100/1.5, 100/1.5]
        self.pos = [offsetPos[0] + [70/1.5, 264/1.5][0] + 7, offsetPos[1] + [70/1.5, 264/1.5][1]]
        self.posImg = [offsetPos[0] + [70/1.5, 264/1.5][0] + self.size[0]/2, offsetPos[1] + [70/1.5, 264/1.5][1] + self.size[1]/2 - 7]
        self.angle = math.pi / 2
        self.angleInc = math.pi / 180

    def render(self):
        throwerImg.angle = 90 - ((self.angle/math.pi * 180) - 90)
        throwerImg.x = int(self.posImg[0]+7)
        throwerImg.y = int(self.posImg[1]+7)
        throwerImg.draw()
        
    def incAngle(self):
        self.angle = self.angle + self.angleInc
        if self.angle >  math.pi - 3*self.angleInc:
            self.angle = math.pi - 3*self.angleInc

    def decAngle(self):
        self.angle = self.angle - self.angleInc
        if self.angle <  3*self.angleInc:
            self.angle = 3*self.angleInc

    def getCenterPos(self):
        return [self.pos[0] + self.size[0]/2, self.pos[1] + self.size[1] / 2]

class ballAnimation(object):
	def __init__(self, balls):
		self.balls = copy.copy(balls)
		self.maxHeight = 272
		self.speed = 0
		self.acceleration = 0.2

	def render(self):
		for ball in self.balls:
			ball.render()

	def update(self):
		self.speed += self.acceleration
		for ball in self.balls:
			ball.pos[1] = ball.pos[1]+int(self.speed)

	def isFinished(self):
		for ball in self.balls:
			if ball.pos[1] < self.maxHeight:
				return False
		return True

class Main(object):
    def __init__(self):
        self.game = None
        self.mode = 'splash'
        self.exit = False
        self.sel = 1
        self.whatLevSet = 0
        self.currSelLev = 0
        self.alpahSplash = 254
        self.timerSplashScreen = 0
        self.introTimer = 0
        self.pad = None
        self.downloadInfo = []
        self.curDownload = 0

        self.soundStared = False
        pspmp3.init(1)

    def __showSplashScreen(self):
        self.timerSplashScreen += 1
        splash.draw()
        maskSplash.clear(osl.RGBA(0, 0, 0, self.alpahSplash))
        maskSplash.draw()
        if self.timerSplashScreen > 230:
            self.alpahSplash += 2
            maskSplash.clear(osl.RGBA(0, 0, 0, self.alpahSplash))
            if self.alpahSplash == 254:
                time.sleep(1)
                self.mode = 'intro'
        elif self.alpahSplash != 0:
            self.alpahSplash -= 2
        
    def __showIntro(self):
        if not self.soundStared:
            pspmp3.stop()
            pspmp3.load("snd/introzik.mp3")
            pspmp3.play(True)
            self.soundStared = True

        intro.draw()

        if self.introTimer > 50:
            pressStartImg.x = 351
            pressStartImg.y = 231
            pressStartImg.draw()
        if self.introTimer > 100:
            self.introTimer = 0
        if self.pad.pressed_start:
            self.introTimer = 0
            time.sleep(0.5)
            self.mode = 'menu'
        self.introTimer += 1

    def __drawInfoMenu(self, scelta):
        info_menu.draw()
        font.set()
        osl.setTextColor(osl.RGBA(255,255,255,255))
        info = ("Choose a level from levelset and play it", "Play a level generated randomly", "Download new levels from the Internet",\
                "Exit from the game",)
        osl.drawString(16,272 - info_menu.sizeY - 10, info[scelta-1])
        
    def __initialMenu(self):
        if not self.soundStared:
            pspmp3.stop()
            pspmp3.load("snd/introzik.mp3")
            pspmp3.play(True)
            self.soundStared = True
            
        intro.draw()
        
        menuImg[0].draw()
        menuImg[1].draw()
        menuImg[2].draw()
        menuImg[3].draw()

        self.__drawInfoMenu(self.sel)

        menuImgSel[self.sel-1].draw()
        
        if osl.getDialogType():
            osl.drawDialog()
            if osl.getDialogStatus() == 0 and osl.dialogGetResult() != osl.DIALOG_CANCEL:
                osl.endDialog()
                url = urllib.urlopen("http://www.gefa.altervista.org/PortableBubble/server/getLevelSetList.php")
                buf = url.read()
                url.close()
                listaLevSet = buf.split(";")
                del listaLevSet[len(listaLevSet)-1]
                self.downloadInfo = []
                for lvl in listaLevSet:
                    lvl_info = urllib.urlopen("http://www.gefa.altervista.org/PortableBubble/server/info/%s"%lvl)
                    buf = lvl_info.read()
                    self.downloadInfo.append((lvl.split('.')[0], buf.split(';')[0], buf.split(';')[1])) # 1 -> n lvls; 2 -> description.
                    lvl_info.close()
                self.mode = 'download'
            elif osl.dialogGetResult() == osl.DIALOG_CANCEL:
                osl.endDialog()
        else:
            if self.pad.pressed_down and self.sel != 4:
                sndMenu.play()
                self.sel += 1
            elif self.pad.pressed_up and self.sel != 1:
                sndMenu.play()
                self.sel -= 1
                
        if self.sel == 1:
            if self.pad.pressed_cross:
                self.sel = 0
                self.mode = 'chooseLevelSet'
                level.initialize(level.lista_level_set[0]+".txt")
                time.sleep(0.5)
        elif self.sel == 2:
            if self.pad.pressed_cross:
                self.game = Game('random', 1, 1)
                self.game.board.nLevel = 'random'
                self.mode = 'game'
                self.soundStared = False
                time.sleep(1)
        elif self.sel == 3:
            if self.pad.pressed_cross:
                osl.initNetDialog()
        elif self.sel == 4:
            if self.pad.pressed_cross:
                self.exit = True
                
    def __chooseLevelSet(self):
        intro.draw()

        menuImgSel[0].draw()
        menuImg[1].draw()
        menuImg[2].draw()
        menuImg[3].draw()

        chooseLevelSetImg.x = (480/2)-(chooseLevelSetImg.sizeX/2)
        chooseLevelSetImg.y = (272/2)-(chooseLevelSetImg.sizeY/2)
        chooseLevelSetImg.draw()

        self.__drawInfoMenu(1)

        ## Rendering Preview Level
        pari_dispari = 0
        cont = 0
        for i in level.levels[self.currSelLev]:
            for j in i[0:len(i)-1]:
                if j != '-':
                    if pari_dispari % 2 == 0:
                        imageBalls_little[int(j)].x = chooseLevelSetImg.x + 251 + 10*cont
                    else:
                        imageBalls_little[int(j)].x = chooseLevelSetImg.x + 251 + 5 + 10*cont
                    imageBalls_little[int(j)].y = chooseLevelSetImg.y + 28 + 10*pari_dispari
                    imageBalls_little[int(j)].draw()
                cont += 1
            cont = 0
            pari_dispari += 1
            

        if self.sel != 0:
            freccia_s.x = 141-50
            freccia_s.y = 187
            freccia_s.draw()
        if len(level.lista_level_set) != 1 and self.sel != len(level.lista_level_set) - 1:
            freccia_d.x = 327-50
            freccia_d.y = 187
            freccia_d.draw()
       
        font.set()
        osl.setTextColor(osl.RGBA(255,255,255,255))
        osl.drawString(224-50,78-4, level.lista_level_set[self.sel])
        osl.drawString(228-50,101-3, level.authors[self.sel])
        osl.drawString(192-50,123-3, str(self.currSelLev+1)+'/'+str(level.n_levels[self.sel])+' (L/R)')
        osl.drawString(209-50,148-4, str(level.getHighScore(level.lista_level_set[self.sel], self.currSelLev+1)).replace("None", "???")+" seconds")

        if self.pad.pressed_right and self.sel != len(level.lista_level_set) - 1:
            self.sel += 1
            self.whatLevSet += 1
            self.currSelLev = 0
            level.initialize(level.lista_level_set[self.whatLevSet]+".txt")
        elif self.pad.pressed_left and self.sel != 0:
            self.sel -= 1
            self.whatLevSet -= 1
            self.currSelLev = 0
            level.initialize(level.lista_level_set[self.whatLevSet]+".txt")
        if self.pad.pressed_L and self.currSelLev != 0:
            self.currSelLev -= 1
        elif self.pad.pressed_R and self.currSelLev + 1 != level.n_levels[self.sel]:
            self.currSelLev += 1
        if self.pad.pressed_cross:
            self.game = Game(level.lista_level_set[self.sel]+".txt", self.currSelLev + 1, level.n_levels[self.sel])
            self.mode = 'game'
            self.soundStared = False;
        if self.pad.pressed_triangle:
            sndCancel.play()
            self.mode = 'menu'
            self.whatLevSet = 0
            self.sel = 1
            self.currSelLev = 0

    def __game(self):
        if not self.soundStared:
            pspmp3.stop()
            pspmp3.load("snd/frozen-mainzik-1p.mp3")
            pspmp3.play(True)
            self.soundStared = True

        mask.draw()
        bg.draw()
        
        if not self.game.returnToMainMenu:
            self.game.run(self.pad)
        else:
            mask.clear(osl.RGBA(255, 255, 255, 255)) 
            self.soundStared = False
            self.mode = 'menu'
            self.sel = 1
            self.whatLevSet = 0
            
    def __download(self):
        intro.draw()

        menuImg[0].draw()
        menuImg[1].draw()
        menuImgSel[2].draw()
        menuImg[3].draw()

        self.__drawInfoMenu(2)
        
        downloadImg.draw()
                
        font.set()
        
        y = 30
        i = 0
        for lvl in self.downloadInfo:
            if i == self.curDownload:
                osl.setTextColor(osl.RGBA(128,0,0,255))
                osl.drawString(34 + (480/2)-(downloadImg.sizeX/2), y + (272/2)-(downloadImg.sizeY/2), lvl[0])
            else:
                osl.setTextColor(osl.RGBA(0,0,0,255))
                osl.drawString(34 + (480/2)-(downloadImg.sizeX/2), y + (272/2)-(downloadImg.sizeY/2), lvl[0])
            y += 15
            i += 1

        osl.setTextColor(osl.RGBA(0,0,0,255))
        osl.drawString(183 + (480/2)-(downloadImg.sizeX/2), 30 + (272/2)-(downloadImg.sizeY/2), self.downloadInfo[self.curDownload][0])
        osl.drawString(183 + (480/2)-(downloadImg.sizeX/2), 64 + (272/2)-(downloadImg.sizeY/2), self.downloadInfo[self.curDownload][1])
        y = 86 + (272/2)-(downloadImg.sizeY/2)
        for i in range(10): # 10 linees
            osl.drawString(183 + (480/2)-(downloadImg.sizeX/2), y, self.downloadInfo[self.curDownload][2][i*23:i*23+23])
            y += 15
            
        if self.pad.pressed_down and self.curDownload != len(self.downloadInfo) - 1:
            self.curDownload += 1
        elif self.pad.pressed_up and self.curDownload != 0:
            self.curDownload -= 1
        elif self.pad.pressed_cross: # Download Level Set
            url = urllib.urlopen("http://www.gefa.altervista.org/PortableBubble/server/levelSet/%s.txt"%self.downloadInfo[self.curDownload][0])
            lvlset = url.read()
            url.close()
            lvlset_file = open('lvls/%s.txt'%self.downloadInfo[self.curDownload][0],'w')
            lvlset_file.write(lvlset)
            lvlset_file.close()
            lvlset_file = open('lvls/%s.rec'%self.downloadInfo[self.curDownload][0],'w')
            lvlset_file.close()
            osl.initMessageDialog("The '%s' level set has been downloaded and installed. To play the new levels you must restart the game."%self.downloadInfo[self.curDownload][0], 0)
        elif self.pad.pressed_triangle:
            self.mode = 'menu'
            self.curDownload = 0
            osl.netTerm()

        if osl.getDialogType():
            osl.drawDialog()
            if osl.getDialogStatus() == 0:
                self.mode = 'menu'
                self.curDownload = 0
                osl.netTerm()
                osl.endDialog()

    def update(self):
        self.pad = osl.Controller()

        if self.mode == 'splash':
            self.__showSplashScreen()
        elif self.mode == 'intro':
            self.__showIntro()
        elif self.mode == 'menu':
            self.__initialMenu()
        elif self.mode == 'chooseLevelSet':
            self.__chooseLevelSet()
        elif self.mode == 'download':
            self.__download()
        elif self.mode == 'game':
            self.__game()

main = Main()
skip = False

if __name__ == '__main__':
    while not main.exit:
        if not skip: 
            osl.startDrawing()
            main.update()
            osl.endDrawing()
        osl.endFrame() 
        skip = osl.syncFrame()
        osl.audioVSync()
    osl.endGfx()
