#!/usr/bin/python
# -*- coding: utf-8 -*-

LARGURA = 640
ALTURA = 480
FPS = 30
TITULO = 'Demo'
FONTE = 'midia/fontes/d-puntillas-B-to-tiptoe.ttf'

import pygame
from pygmeu import *
import random

class Zumbi(Sprite):
    def __init__(self, pai, nome, x, y, limites):
        Sprite.__init__(self, pai, nome, x, y)
        self.dx = 250.0 * random.choice([-1, 1])
        self.dy = 250.0 * random.choice([-1, 1])
        self.limites = limites
        self.imagem = pygame.image.load('midia/imagens/zombie.png').convert_alpha()


class EstadoZumbiAnimacao(EstadoSprite):
    def ao_entrar(self):
        zumbi = self.dono()
        zumbi.y = zumbi.pai.altura / 2
        zumbi.escala = 1.0
        zumbi.angulo = 0.0
        zumbi.remover_todas_animacoes()
        zumbi.adicionar_animacao(
            AnimacaoLinear(
                'x', zumbi.limites.left, zumbi.limites.right, 2000, 1) +
            AnimacaoLinear(
                'x', zumbi.limites.right, zumbi.limites.centerx, 1000, 1))

    def ao_sair(self):
        zumbi = self.dono()
        aa = AnimacaoLinear('angulo', 0.0, 360.0, 5000, 0)
        zumbi.adicionar_animacao(aa)
        ae1 = AnimacaoLinear('escala', 1.0, 2.0, 1000, 1)
        ae2 = AnimacaoLinear('escala', 2.0, 1.0, 1000, 1)
        ae1.proximas_animacoes.append(ae2)
        ae2.proximas_animacoes.append(ae1)
        zumbi.adicionar_animacao(ae1)


class EstadoZumbiAndando(EstadoSprite):
    def atualizar(self, dt):
        zumbi = self.dono()
        nx = zumbi.x + zumbi.dx * dt / 1000
        ny = zumbi.y + zumbi.dy * dt / 1000
        if nx < zumbi.limites.left:
            nx = zumbi.limites.left + (zumbi.limites.left - nx)
            zumbi.dx = -zumbi.dx
        if zumbi.x > zumbi.limites.right:
            nx = zumbi.limites.right - (nx - zumbi.limites.right)
            zumbi.dx = -zumbi.dx
        if ny < zumbi.limites.top:
            ny = zumbi.limites.top + (zumbi.limites.top - ny)
            zumbi.dy = -zumbi.dy
        if zumbi.y > zumbi.limites.bottom:
            ny = zumbi.limites.bottom - (ny - zumbi.limites.bottom)
            zumbi.dy = -zumbi.dy
        zumbi.x = nx
        zumbi.y = ny


class Mira(Sprite):
    def __init__(self, pai, nome, x, y):
        Sprite.__init__(self, pai, nome, x, y)
        self.imagem = pygame.image.load('midia/imagens/mira.png').convert_alpha()


class EstadoMiraAndando(EstadoSprite):
    def processar_evento(self, e):
        mira = self.dono()
        if e.type == pygame.MOUSEMOTION:
            mira.x, mira.y = e.pos
        elif e.type == pygame.KEYDOWN:
            if e.key == pygame.K_f:
                Jogo.jogo.enviar_evento(msg='tiro')
        elif e.type == pygame.USEREVENT:
            if e.msg == 'tiro':
                mira.adicionar_animacao([
                    AnimacaoLinear('angulo', 0.0, 90.0, 200),
                    AnimacaoLinear('escala', 1.0, 0.8, 100) +
                    AnimacaoLinear('escala', 0.8, 1.0, 100)])


class Pow(Sprite):
    def __init__(self, pai, nome, x, y):
        Sprite.__init__(self, pai, nome, x, y)
        self.imagem = pygame.image.load('midia/imagens/pow.png').convert_alpha()
        self.adicionar_animacao(
            AnimacaoLinear('escala', 0.1, 1.0, 150) +
            AnimacaoEsperar(1000) + [
                AnimacaoLinear('escala', 1.0, 0.1, 350),
                AnimacaoLinear('angulo', 360.0, 0.0, 350),
                AnimacaoLinear(
                    'x',
                    x,
                    x + random.randint(100, 300) * random.choice([-1, 1]),
                    350),
                AnimacaoLinear(
                    'y',
                    y,
                    y + random.randint(100, 300) * random.choice([-1, 1]),
                    350),
                AnimacaoLinear('opacidade', self.opacidade, 0, 350)] +
            AnimacaoValor('lixo', True))


class MeuTexto(Texto):
    def ao_definir_texto(self):
        if self.animacoes:
            self.remover_todas_animacoes()
        else:
            self.opacidade = 0
        self.adicionar_animacao(
            AnimacaoLinear('opacidade', self.opacidade, 255, (255 - self.opacidade) / 255.0 * 1000 + 1) +
            AnimacaoEsperar(2000) +
            AnimacaoLinear('opacidade', 255, 0, 1000))


class Pontuacao(Texto):
    def __init__(self, pai, nome, x, y, fonte, tamanho, cor):
        Texto.__init__(self, pai, nome, x, y, fonte, tamanho, cor, u'')
        self.pontuacao = 0

    def incrementar(self, n):
        self.definir_pontuacao(self.pontuacao + n)

    def definir_pontuacao(self, pontuacao):
        self.pontuacao = pontuacao
        self.definir_texto('{:,}'.format(self.pontuacao))

    def ao_definir_texto(self):
        if self.animacoes:
            self.remover_todas_animacoes()
        else:
            self.escala = 1.0
        self.adicionar_animacao(
            AnimacaoLinear('escala', self.escala, 1.3, (1.3 - self.escala) / 1.3 * 100 + 1) +
            AnimacaoLinear('escala', 1.3, 1.0, 200))

    def processar_evento(self, e):
        Sprite.processar_evento(self, e)
        if e.type == pygame.USEREVENT:
            if e.msg == 'acertou':
                self.incrementar(250)


class BarraDeProgresso(Sprite):
    def __init__(self, pai, nome, x, y, largura, altura, minimo, maximo):
        Sprite.__init__(self, pai, nome, x, y)
        self.largura = largura
        self.altura = altura
        self.minimo = minimo
        self.maximo = maximo
        self.posicao = minimo
        self.imagem = pygame.Surface((largura, altura)).convert_alpha()
        self._atualizar_imagem()

    def definir_posicao(self, posicao):
        posicao = min(max(posicao, self.minimo), self.maximo)
        if posicao != self.posicao:
            self.posicao = posicao
            self._atualizar_imagem()

    def _atualizar_imagem(self):
        razao = float(self.posicao) / (self.maximo - self.minimo)
        rect = pygame.Rect(0, 0, self.largura * razao, self.altura)
        n = int(255 * razao)
        cor = pygame.Color(255 - n, n, 0)
        self.imagem.fill(pygame.Color(255, 255, 255))
        pygame.draw.rect(self.imagem, cor, rect)
        pygame.draw.rect(
            self.imagem, pygame.Color(0, 127, 0), self.imagem.get_rect(), 3)


class MinhaCena(Cena):
    def __init__(self, pai, nome, largura, altura):
        Cena.__init__(self, pai, nome, largura, altura)
        self.temporizador = Temporizador(20000)
        Zumbi(
            self, 'zumbi',
            largura / 2, altura / 2, pygame.Rect(0, 0, largura, altura))
        Camada(self, 'camada_pow')
        Mira(self, 'mira', largura / 2, altura / 2)
        MeuTexto(self, 'texto', self.largura / 2, self.altura / 2,
            FONTE, 64, pygame.Color(255, 0, 0), u'')
        Pontuacao(self, 'pontuacao', self.largura / 2, self.altura * 0.9,
            FONTE, 32, pygame.Color(255, 0, 0))
        BarraDeProgresso(self, 'barra',
            self.largura / 2, self.altura - 10,
            self.largura - 10, 10,
            0, self.temporizador.duracao)
        self.imagem = pygame.image.load('midia/imagens/cena.jpg').convert_alpha()
        self.maquina_de_estados.avancar(EstadoMinhaCenaAnimacao())

    def processar_evento(self, e):
        Cena.processar_evento(self, e)
        if e.type == pygame.USEREVENT:
            if e.msg == 'tiro':
                self.testar_colisao()
            elif e.msg == 'acertou':
                Pow(self['camada_pow'], 'pow', e.mira.x, e.mira.y)

    def pintar(self, tela):
        Cena.pintar(self, tela)
        tela.blit(self.imagem, (0, 0))

    def testar_colisao(self):
        mira = self['mira']
        zumbi = self['zumbi']
        if mira.testar_colisao(zumbi):
            Jogo.jogo.enviar_evento(msg='acertou', mira=mira)


class EstadoMinhaCenaAnimacao(EstadoSprite):
    def __init__(self):
        EstadoSprite.__init__(self)
        self.temporizador = Temporizador(6000)

    def ao_entrar(self):
        cena = self.dono()
        cena['zumbi'].maquina_de_estados.avancar(EstadoZumbiAnimacao())
        cena['texto'].definir_texto(u'Acerte o zumbi!')
        cena['pontuacao'].definir_pontuacao(0)

    def processar_evento(self, e):
        if e.type == pygame.KEYDOWN:
            if e.key == pygame.K_ESCAPE:
                self.avancar(EstadoMinhaCenaEmJogo())

    def atualizar(self, dt):
        if self.temporizador.expirou():
            self.avancar(EstadoMinhaCenaEmJogo())


class EstadoMinhaCenaEmJogo(EstadoSprite):
    def ao_entrar(self):
        cena = self.dono()
        cena.temporizador.reiniciar()
        cena['zumbi'].remover_todas_animacoes()
        cena['zumbi'].x = cena.largura / 2
        cena['zumbi'].y = cena.altura / 2
        cena['zumbi'].maquina_de_estados.avancar(EstadoZumbiAndando())
        cena['mira'].maquina_de_estados.avancar(EstadoMiraAndando())
        cena['barra'].definir_posicao(cena.temporizador.duracao)
        cena['texto'].remover_todas_animacoes()
        cena['texto'].opacidade = 0
        Jogo.jogo.enviar_evento(msg='em_jogo')

    def atualizar(self, dt):
        cena = self.dono()
        cena['barra'].definir_posicao(
            cena.temporizador.duracao - cena.temporizador.transcorrido())
        if cena.temporizador.expirou():
            self.avancar(EstadoMinhaCenaFim())


class EstadoMinhaCenaFim(EstadoSprite):
    def ao_entrar(self):
        cena = self.dono()
        zumbi = cena['zumbi']
        zumbi.maquina_de_estados.avancar(EstadoSprite())
        zumbi.adicionar_animacao(
            AnimacaoLinear('x', zumbi.x, cena.largura * 1.2, 1000))
        cena['mira'].maquina_de_estados.avancar(EstadoSprite())
        cena['texto'].definir_texto(u'Fim de jogo')
        self.temporizador = Temporizador(5000)

    def ao_sair(self):
        cena = self.dono()
        cena['zumbi'].y = cena.altura / 2
        cena['zumbi'].dx *= random.choice([-1, 1])
        cena['zumbi'].dy *= random.choice([-1, 1])
        cena['zumbi'].remover_todas_animacoes()
        cena['mira'].x = cena.largura / 2
        cena['mira'].y = cena.altura / 2

    def atualizar(self, dt):
        if self.temporizador.expirou():
            self.avancar(EstadoMinhaCenaAnimacao())


class MeuJogo(Jogo):
    def __init__(self, nome, largura, altura, fps, titulo):
        Jogo.__init__(self, nome, largura, altura, fps, titulo)
        pygame.mouse.set_visible(False)
        MinhaCena(self, 'minha_cena', largura, altura)
        Texto(self, 'texto_pausa', largura / 2, altura / 2,
            FONTE, 128, pygame.Color(0, 127, 0), u'Pausa')
        self['texto_pausa'].opacidade = 0
        som = GerenciadorDeSom(self, 'som')
        som.adicionar_som('tiro', 'midia/sons/tiro.ogg')
        som.adicionar_som('acertou', 'midia/sons/acertou.ogg', parar=True)
        som.adicionar_som('em_jogo', 'midia/sons/em_jogo.ogg', parar=True)

    def processar_evento(self, e):
        Jogo.processar_evento(self, e)
        if e.type == pygame.KEYDOWN:
            if e.key == pygame.K_PAUSE:
                self.pausa = not self.pausa
                if self.pausa:
                    self['texto_pausa'].opacidade = 255
                else:
                    self['texto_pausa'].opacidade = 0
            elif e.key == pygame.K_d:
                self.debug_filhos()
        return self.pausa

def main():
    MeuJogo('meu_jogo', LARGURA, ALTURA, FPS, TITULO).executar()


if __name__ == "__main__":
    main()
