from pygsear.Drawable import Image, MultiImage, RotatedImage, MultiRotated
from pygsear.Game import Game
from pygsear.Event import KEYDOWN_Event, TIMEOUT_Event
from pygsear.Widget import Score
from pygsear import conf
from pygsear.locals import PI

from pygame import Rect
from pygame.locals import K_UP, K_DOWN, K_LEFT, K_RIGHT
import random
import time


class Frog(MultiRotated):
    def __init__(self):
        MultiRotated.__init__(self, filenames=('frogng1.png', 'frogng2.png'), steps=4)
        self.set_crect(Rect(0, 0, 38, 38))

        self.JUMP = 60
        self.WALK = 30
        self.lives = 3
        self.restart()

    def restart(self, ev=None):
        self.set_rotation(0)
        self.set_closest()
        self.center(y=-5)
        self.lane = 0
        self.floating = 0 # set if standing on something that floats
        self.frozen = 0
        self.jumping = 0
        self.path.set_velocity(vx=0)

    def unjump(self, ev=None):
        self.jumping = 0
        self.switch_images('frogng1.png')

    def up(self, ev=None):
        if not self.jumping:
            self.set_rotation(0)
            self.set_closest()
            self.nudge(dy=-self.JUMP)
            self.switch_images('frogng2.png')
            self.jumping = 1
            self.lane += 1
            conf.game.events.add(TIMEOUT_Event(80, callback=self.unjump))

    def down(self, ev=None):
        if not self.frozen:
            self.set_rotation(PI)
            self.set_closest()
            self.nudge(dy=self.JUMP)
            self.switch_images('frogng2.png')
            self.jumping = 1
            self.lane -= 1
            conf.game.events.add(TIMEOUT_Event(80, callback=self.unjump))

    def left(self, ev=None):
        self.set_rotation(PI/2)
        self.set_closest()
        if not self.frozen:
            self.nudge(dx=-self.WALK)

    def right(self, ev=None):
        self.set_rotation(3*PI/2)
        self.set_closest()
        if not self.frozen:
            self.nudge(dx=self.WALK)


class DeadFrog(MultiImage):
    def __init__(self):
        MultiImage.__init__(self, filenames=('sploosh1.png', 'sploosh2.png', 'sploosh3.png', 'sploosh4.png',
                                                'croak1.png', 'croak2.png', 'croak3.png', 'croak4.png'))
        self.center(y=-5)
        self.lane = 0
        self.reset()

    def reset(self):
        self.splooshing = 0
        self.croaking = 0
        self.croaked = 0
        self.path.set_velocity(vx=0)

    def croak(self):
        if not self.croaking:
            self.croaking = 1
            self.frozen = 1
            self.handle_croaking()

    def handle_croaking(self, ev=None):
        croaking = self.croaking
        if not croaking or croaking == 5:
            conf.game.events.add(TIMEOUT_Event(150, callback=self.uncroak))
        else:
            img = 'croak%s.png' % croaking
            self.flip(img)
            self.croaking += 1
            conf.game.events.add(TIMEOUT_Event(150, callback=self.handle_croaking))

    def uncroak(self, ev=None):
        self.reset()
        conf.game.events.add(TIMEOUT_Event(150, callback=conf.game.next_life))

    def sploosh(self):
        if not self.splooshing and not self.croaking:
            self.splooshing = 1
            self.frozen = 1
            self.handle_splooshing()

    def handle_splooshing(self, ev=None):
        splooshing = self.splooshing
        if not splooshing or splooshing == 5:
            self.splooshing = 0
            self.croak()
        else:
            img = 'sploosh%s.png' % splooshing
            self.flip(img)
            self.splooshing += 1
            conf.game.events.add(TIMEOUT_Event(150, callback=self.handle_splooshing))

    def move(self):
        if not self.frozen:
            MultiImage.move(self)


class Pad(MultiImage):
    def __init__(self):
        MultiImage.__init__(self, filenames=('pad.png', 'padfull.png'))
        self.flip('pad.png')
        self.occupied = 0

    def occupy(self):
        self.occupied = 1
        self.flip('padfull.png')

    def unoccupy(self):
        self.occupied = 0
        self.flip('pad.png')


class Car(RotatedImage):
    def __init__(self, lane, vx):
        color = random.choice(['01', '02', '03', '04'])
        fn = 'car%s.png' % color
        RotatedImage.__init__(self, filename=fn, steps=2)
        if vx > 0:
            x = -100
        else:
            x = conf.WINWIDTH + 148
            self.set_rotation(PI)

        y = conf.WINHEIGHT - 25 - (60 * lane)

        self.set_position(x, y)
        self.path.set_velocity(vx=vx)
        self.set_closest()


class Log(Image):
    def __init__(self, lane, vx):
        Image.__init__(self, filename='log.png')
        if vx > 0:
            x = -200
        else:
            x = conf.WINWIDTH + 50

        y = conf.WINHEIGHT - 50 - (60 * lane)

        self.set_position(x, y)
        self.path.set_velocity(vx=vx)


class LogLane:
    def __init__(self, lane_number, freq, speed, sigma, delaysigma):
        self.number = lane_number
        self.freq = freq
        speed = abs(speed)
        speed = random.normalvariate(speed, sigma)
        self.delaysigma = delaysigma

        assert 6 <= lane_number <= 8, 'logs only allowed in the water'

        if lane_number % 2:
            direction = -1
        else:
            direction = 1
        self.vx = direction * speed

        self.logs = conf.game.addGroup()

        self.spawn()

    def spawn(self, ev=None):
        log = Log(self.number, self.vx)
        self.logs.add(log)
        conf.game.sprites.add(log)
        conf.game.logs.add(log)

        delay = random.normalvariate(1000/self.freq, self.delaysigma)
        conf.game.events.add(TIMEOUT_Event(delay=delay, callback=self.spawn))


class Lane:
    def __init__(self, lane_number, freq, speed, sigma, delaysigma):
        self.number = lane_number
        self.freq = freq
        speed = abs(speed)
        speed = random.normalvariate(speed, sigma)
        self.delaysigma = delaysigma

        assert 1 <= lane_number <= 4, 'cars only allowed on the road'

        if lane_number > 2:
            self.vx = -speed
        else:
            self.vx = speed

        wavelength = speed / freq

        self.cars = conf.game.addGroup()

        x = random.randrange(int(wavelength))
        while x < conf.WINWIDTH:
            car = Car(lane_number, self.vx)
            self.cars.add(car)
            car_x, car_y = car.get_position()
            car.set_position(x, car_y)
            x += wavelength

        self.spawn()

    def spawn(self, ev=None):
        car = Car(self.number, self.vx)
        self.cars.add(car)
        conf.game.sprites.add(car)
        conf.game.cars.add(car)

        delay = random.normalvariate(1000/self.freq, self.delaysigma)
        conf.game.events.add(TIMEOUT_Event(delay=delay, callback=self.spawn))


class FrogGame(Game):
    def initialize(self):
        self.set_background(filename='bg.png')

        self.pads = self.addGroup()
        for p in range(5):
            pad = Pad()
            pad.set_position(50 + 155*p, 5)
            self.sprites.add(pad)
            self.pads.add(pad)

        frog = Frog()
        self.sprites.add(frog, level=1)
        self.frog = frog

        self.dead = DeadFrog()

        self.score = Score(text='', position=(610, 550))
        self.sprites.add(self.score)

        self.events.add(KEYDOWN_Event(key=K_UP, callback=frog.up))
        self.events.add(KEYDOWN_Event(key=K_DOWN, callback=frog.down))
        self.events.add(KEYDOWN_Event(key=K_LEFT, callback=frog.left))
        self.events.add(KEYDOWN_Event(key=K_RIGHT, callback=frog.right))

        self.cars = self.addGroup()
        for n in [1, 2, 3, 4]:
            lane = Lane(n, 0.3, 120, 40, 500)
            self.sprites.add(lane.cars)
            self.cars.add(lane.cars)

        self.logs = self.addGroup()
        for n in [6, 7, 8]:
            lane = LogLane(n, 0.2, 100, 20, 700)
            self.sprites.add(lane.logs)
            self.logs.add(lane.logs)

    def checkCollisions(self):
        if self.frog.lane == 9:
            pads = self.pads.sprites()
            pad = self.frog.collidelist(pads)
            if pad and not pad.occupied:
                pad.occupy()
                self.score.addPoints(1)
                self.score.updateScore()
                self.check_finished()
                self.frog.restart()

        if self.frog.lane == 5:
            self.frog.floating = 0
            self.frog.path.set_velocity(vx=0)

        if 6 <= self.frog.lane <= 9:
            logs = self.logs.sprites()
            log = self.frog.collidelist(logs)
            if log:
                self.frog.floating = 1
                vx, vy = log.path.get_velocity()
                self.frog.path.set_velocity(vx=vx)
            else:
                self.frog.floating = 0
                self.frog.path.set_velocity(vx=0)

            if not self.frog.floating:
                self.sploosh_frog()

        if 1 <= self.frog.lane <=4:
            cars = self.cars.sprites()
            car = self.frog.collidelist(cars)
            if car:
                self.croak_frog()

        if not self.frog.onscreen(50):
            self.frog.onscreen(slack=0, jail=1)
            self.croak_frog()

    def croak_frog(self):
        if not self.frog.frozen:
            self.frog.frozen = 1
            self.dead.set_position(self.frog.get_position())
            self.dead.nudge(dx=-24, dy=-24)
            self.frog.kill()
            self.sprites.add(self.dead, level=-1)
            self.dead.croak()
            self.frog.lives -= 1

    def sploosh_frog(self):
        if not self.frog.frozen:
            self.frog.frozen = 1
            self.dead.set_position(self.frog.get_position())
            self.dead.nudge(dx=-24, dy=-24)
            self.frog.kill()
            self.sprites.add(self.dead, level=-1)
            self.dead.sploosh()
            self.frog.lives -= 1

    def next_life(self, ev=None):
        if self.frog.lives > 0:
            self.dead.kill()
            self.frog.restart()
            self.sprites.add(self.frog, level=1)
        else:
            self.stop = 1

    def check_finished(self):
        pads_occupied = [pad.occupied for pad in self.pads.sprites()].count(1)
        if pads_occupied == 5:
            print 'level complete'
            self.events.add(TIMEOUT_Event(750, callback=self.next_level))

    def next_level(self, ev=None):
        for pad in self.pads.sprites():
            pad.unoccupy()


if __name__ == '__main__':
    g = FrogGame()
    g.mainloop()

