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, Dialog_OK
from pygsear import conf
conf.MAX_TICK = 500

from pygsear.locals import PI

from pygame import Rect
from pygame.locals import K_UP, K_DOWN, K_LEFT, K_RIGHT, K_q
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 = 5
        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(PI/2)
            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(3*PI/2)
            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)
        self.set_closest()
        if not self.frozen:
            self.nudge(dx=-self.WALK)

    def right(self, ev=None):
        self.set_rotation(0)
        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):
        car_file = random.choice(['car01.png', 'car02.png', 'car03.png', 'car04.png', 'car_limo01.png', 'car_truck01.png'])
        RotatedImage.__init__(self, filename=car_file, 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()

    def move(self):
        if not self.onscreen(slack=200):
            self.kill()
        RotatedImage.move(self)


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)

        self.floating = 1

    def move(self):
        if not self.onscreen(300):
            self.kill()
        Image.move(self)


class Turtle(MultiRotated):
    def __init__(self, lane, vx, afloat, submerged):
        MultiRotated.__init__(self, filenames=['turtle0.png', 'turtle1.png', 'turtle2.png', 'turtle3.png', 'turtle4.png'], steps=2)
        if vx > 0:
            x = -200
        else:
            x = conf.WINWIDTH + 50
            self.set_rotation(PI)

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

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

        self.floating = 1
        self.sink_level = 0
        self.sync_image()

        self.afloat = afloat
        self.submerged = submerged

        sink_turtle = TIMEOUT_Event(self.afloat, callback=self.sink)
        conf.game.events.add(sink_turtle)
        conf.game.spawn_events.add(sink_turtle)

    def move(self):
        if not self.onscreen(300):
            self.kill()
        MultiRotated.move(self)

    def sync_image(self):
        image = 'turtle%s.png' % self.sink_level
        self.switch_images(image)

    def sink(self, ev=None):
        print 'sink'
        self.sink_level += 1
        if self.sink_level < 4:
            sink_rotate = TIMEOUT_Event(400, callback=self.sink)
            conf.game.events.add(sink_rotate)
            conf.game.spawn_events.add(sink_rotate)
        else:
            self.floating = 0
            rise_turtle = TIMEOUT_Event(self.submerged, callback=self.rise)
            conf.game.events.add(rise_turtle)
            conf.game.spawn_events.add(rise_turtle)

        self.sync_image()

    def rise(self, ev=None):
        self.sink_level -= 1
        self.floating = 1
        if self.sink_level > 0:
            rise_rotate = TIMEOUT_Event(400, callback=self.rise)
            conf.game.events.add(rise_rotate)
            conf.game.spawn_events.add(rise_rotate)
        else:
            sink_turtle = TIMEOUT_Event(self.afloat, callback=self.sink)
            conf.game.events.add(sink_turtle)
            conf.game.spawn_events.add(sink_turtle)

        self.sync_image()

class LogLane:
    def __init__(self, lane_number, speed, delay, variation, logs, turtles, afloat, submerged):
        self.number = lane_number + 5
        self.speed = speed
        self.delay = delay
        self.variation = variation
        self.nlogs = logs
        self.nturtles = turtles
        self.turtle_afloat = afloat
        self.turtle_submerged = submerged

        self.logs = conf.game.addGroup()

        self.spawn()

    def spawn(self, ev=None):
        log_types = [Log]*self.nlogs + [Turtle]*self.nturtles
        log_type = random.choice(log_types)

        if log_type == Log:
            log = log_type(self.number, self.speed)
        else:
            log = log_type(self.number, self.speed, self.turtle_afloat, self.turtle_submerged)
        self.logs.add(log)
        conf.game.sprites.add(log)
        conf.game.logs.add(log)

        min_delay = self.delay
        max_delay = min_delay + self.variation
        delay = random.uniform(min_delay, max_delay)

        next_spawn_event = TIMEOUT_Event(delay=delay, callback=self.spawn)
        conf.game.events.add(next_spawn_event)
        conf.game.spawn_events.add(next_spawn_event)


class Lane:
    def __init__(self, lane_number, speed, delay, variation):
        self.number = lane_number
        self.speed = speed
        self.delay = delay
        self.variation = variation

        self.cars = conf.game.addGroup()

        self.spawn()

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

        min_delay = self.delay
        max_delay = min_delay + self.variation
        delay = random.uniform(min_delay, max_delay)

        next_spawn_event = TIMEOUT_Event(delay=delay, callback=self.spawn)
        conf.game.events.add(next_spawn_event)
        conf.game.spawn_events.add(next_spawn_event)


MAX_LEVEL = 1
class Level_0:
    lane1_speed = 100
    lane1_delay = 2000
    lane1_variation = 3000

    lane2_speed = 120
    lane2_delay = 2000
    lane2_variation = 1000

    lane3_speed = -140
    lane3_delay = 1500
    lane3_variation = 3000

    lane4_speed = -90
    lane4_delay = 3000
    lane4_variation = 1000


    loglane1_speed = 80
    loglane1_delay = 5500
    loglane1_variation = 3500
    loglane1_logs = 1
    loglane1_turtles = 0
    loglane1_turtle_afloat = 0
    loglane1_turtle_submerged = 0

    loglane2_speed = -100
    loglane2_delay = 4500
    loglane2_variation = 2500
    loglane2_logs = 10
    loglane2_turtles = 1
    loglane2_turtle_afloat = 15000
    loglane2_turtle_submerged = 1


    loglane3_speed = 60
    loglane3_delay = 5000
    loglane3_variation = 6000
    loglane3_logs = 10
    loglane3_turtles = 3
    loglane3_turtle_afloat = 15000
    loglane3_turtle_submerged = 1


class Level_1:
    lane1_speed = 200
    lane1_delay = 3000
    lane1_variation = 4000

    lane2_speed = 130
    lane2_delay = 3000
    lane2_variation = 2000

    lane3_speed = -150
    lane3_delay = 1600
    lane3_variation = 4000

    lane4_speed = -100
    lane4_delay = 4000
    lane4_variation = 2000


    loglane1_speed = 90
    loglane1_delay = 5600
    loglane1_variation = 3600
    loglane1_logs = 5
    loglane1_turtles = 1
    loglane1_turtle_afloat = 6000
    loglane1_turtle_submerged = 3000

    loglane2_speed = -200
    loglane2_delay = 4600
    loglane2_variation = 2600
    loglane2_logs = 1
    loglane2_turtles = 0
    loglane2_turtle_afloat = 0
    loglane2_turtle_submerged = 0

    loglane3_speed = 70
    loglane3_delay = 6000
    loglane3_variation = 7000
    loglane3_logs = 5
    loglane3_turtles = 2
    loglane3_turtle_afloat = 3000
    loglane3_turtle_submerged = 1000


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='Score:', position=(610, 550))
        self.sprites.add(self.score)

        self.lives = Score(text='Lives:', position=(20, 550))
        self.sprites.add(self.lives)
        self.lives.set_points(self.frog.lives)
        self.lives.updateScore()

        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()
        self.logs = self.addGroup()
        self.spawn_events = self.addEventGroup()

        self.set_level()

        self.events.add(KEYDOWN_Event(key=K_q, callback=self.quick_finish))

    def set_level(self):
        if not hasattr(self, 'level_number'):
            self.level_number = 0
        elif self.level_number < MAX_LEVEL:
            self.level_number += 1

        level_type = 'Level_%s' % self.level_number
        Level = globals()[level_type]
        level = Level()

        for n in [1, 2, 3, 4]:
            speed_key = 'lane%s_speed' % n
            delay_key = 'lane%s_delay' % n
            variation_key = 'lane%s_variation' % n

            speed = getattr(level, speed_key)
            delay = getattr(level, delay_key)
            variation = getattr(level, variation_key)

            lane = Lane(n, speed=speed, delay=delay, variation=variation)
            self.sprites.add(lane.cars)
            self.cars.add(lane.cars)

        for n in [1, 2, 3]:
            speed_key = 'loglane%s_speed' % n
            delay_key = 'loglane%s_delay' % n
            variation_key = 'loglane%s_variation' % n
            logs_key = 'loglane%s_logs' % n
            turtles_key = 'loglane%s_turtles' % n
            turtle_afloat_key = 'loglane%s_turtle_afloat' % n
            turtle_submerged_key = 'loglane%s_turtle_submerged' % n

            speed = getattr(level, speed_key)
            delay = getattr(level, delay_key)
            variation = getattr(level, variation_key)
            logs = getattr(level, logs_key)
            turtles = getattr(level, turtles_key)
            turtle_afloat = getattr(level, turtle_afloat_key)
            turtle_submerged = getattr(level, turtle_submerged_key)

            lane = LogLane(n, speed=speed, delay=delay, variation=variation,
                                logs=logs, turtles=turtles,
                                afloat=turtle_afloat, submerged=turtle_submerged)
            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 and log.floating:
                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
            self.lives.set_points(self.frog.lives)
            self.lives.updateScore()

    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 quick_finish(self, ev=None):
        for pad in self.pads.sprites():
            pad.occupy()
            self.check_finished()

    def check_finished(self):
        pads_occupied = [pad.occupied for pad in self.pads.sprites()].count(1)
        if pads_occupied == 5:
            self.events.add(TIMEOUT_Event(450, callback=self.clear_pads))

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

        self.spawn_events.kill()
        self.cars.kill()
        self.logs.kill()

        self.events.add(TIMEOUT_Event(750, callback=self.next_level))

    def next_level(self, ev=None):
        #         dlg = Dialog_OK(message="Level Complete!")
        #         dlg.center()
        #         dlg.modal()

        self.frog.lives += 1
        self.lives.set_points(self.frog.lives)
        self.lives.updateScore()
        self.set_level()


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

