from game_object import *

class Person(GameObjectAnimatedByKeyword):
    DIR_LEFT = 1
    DIR_RIGHT = 2
    DIR_NOT_MOVING = 3

    def __init__(self, level, colnum, rownum, *groups):
        # Caller must set_arect after creating
        GameObjectAnimatedByKeyword.__init__(self, level, colnum, rownum, *groups)

        self.left_pressed = False
        self.right_pressed = False
        self.dir_moving = self.DIR_NOT_MOVING

        self.horiz_accel = 0
        self.horiz_veloc = 0

        self.vert_accel = 0
        self.vert_veloc = 0
        self.falling = False

        self.pos = self.arect.left, self.arect.top

    def update_image(self):
        if self.dir_moving == self.DIR_RIGHT:
            self.animate(False)
        elif self.dir_moving == self.DIR_LEFT:
            self.animate(True)
        else:
            # nothing to update
            return

    def update(self, *args):
        self.update_image()
        self.update_position()

    def start_move_right(self):
        self.right_pressed = True
        if self.dir_moving != self.DIR_RIGHT:
            self.dir_moving = self.DIR_RIGHT
            self.start_move()

    def start_move_left(self):
        self.left_pressed = True
        if self.dir_moving != self.DIR_LEFT:
            self.dir_moving = self.DIR_LEFT
            self.start_move()

    def stop_move_right(self):
        self.right_pressed = False
        if self.dir_moving == self.DIR_RIGHT:
            if self.left_pressed:
                self.dir_moving = self.DIR_LEFT
                self.start_move()
            else:
                self.dir_moving = self.DIR_NOT_MOVING
                self.stop_move()

    def stop_move_left(self):
        self.left_pressed = False
        if self.dir_moving == self.DIR_LEFT:
            if self.right_pressed:
                self.dir_moving = self.DIR_RIGHT
                self.start_move()
            else:
                self.dir_moving = self.DIR_NOT_MOVING
                self.stop_move()

    def start_move(self):
        self.move_start = pygame.time.get_ticks() / 1000. # in seconds...
        self.accel_time_remaining = self.MOVE_ACCEL_DURATION
        if self.dir_moving == self.DIR_RIGHT:
            self.horiz_veloc = self.INITIAL_MOVE_VELOC
            self.horiz_accel = self.MOVE_ACCEL
        elif self.dir_moving == self.DIR_LEFT:
            self.horiz_veloc = -self.INITIAL_MOVE_VELOC
            self.horiz_accel = -self.MOVE_ACCEL

    def stop_move(self):
        self.horiz_accel = 0
        self.horiz_veloc = 0

    def jump(self):
        if not self.falling:
            self.start_falling()
            self.vert_veloc = -self.INITIAL_JUMP_VELOC

    def start_falling(self):
        self.falling = True
        self.fall_start = pygame.time.get_ticks() / 1000. # in seconds...
        self.vert_accel = self.GRAVITY_ACCEL
        self.vert_veloc = 0

    def stop_falling(self):
        self.falling = False

    def update_position(self):
        t = pygame.time.get_ticks() / 1000. # in seconds...

        x, y = self.pos

        # Are we moving horizontally?
        if self.dir_moving != self.DIR_NOT_MOVING:
            dt = t - self.move_start
            self.move_start = t # for next time

            if self.horiz_accel != 0 and dt > self.accel_time_remaining:
                # accelerate with dt = accel_time_remaining
                dt2 = self.accel_time_remaining
                # dx = v0 dt + (1/2) a dt dt
                # dv = a dt
                x += dt2 * (self.horiz_veloc + self.horiz_accel * dt2 / 2.)
                self.horiz_veloc += dt2 * self.horiz_accel

                # continue calculating without this period
                dt -= dt2

                # no more acceleration
                self.accel_time_remaining = 0
                self.horiz_accel = 0

            # dx = v0 dt + (1/2) a dt dt
            # dv = a dt
            x += dt * (self.horiz_veloc + self.horiz_accel * dt / 2.)
            self.horiz_veloc += dt * self.horiz_accel

            if self.accel_time_remaining > 0:
                self.accel_time_remaining -= dt

        # Are we free-falling? (y acceleration)
        if self.falling:
            dt = t - self.fall_start
            self.fall_start = t # for next time

            # dy = v0 dt + (1/2) a dt dt
            # dv = a dt
            y += dt * (self.vert_veloc + self.vert_accel * dt / 2.)
            self.vert_veloc += dt * self.vert_accel

        self.pos = x, y
        prev_arect = Rect(self.arect)
        self.arect.left, self.arect.top = self.pos

        # Make sure we aren't hitting floor tiles.
        colliding_tiles = [s for s in self.level.walls if self.arect.colliderect(s.arect)]
        while colliding_tiles:
            tile = colliding_tiles[0]
            x, y = self.pos
            dx, dy = 0, 0

            # Did we hit it from the right, left, top, bottom?
            if prev_arect.left >= tile.arect.right and tile.arect.left < self.arect.left < tile.arect.right:
                # From the right
                dx = tile.arect.right - self.arect.left
                self.arect.left = tile.arect.right

            elif prev_arect.right <= tile.arect.left and tile.arect.left < self.arect.right < tile.arect.right:
                # From the left
                dx = tile.arect.left - self.arect.right
                self.arect.right = tile.arect.left

            elif prev_arect.bottom <= tile.arect.top and tile.arect.top < self.arect.bottom < tile.arect.bottom:
                # From the top (fell on this tile)
                self.stop_falling()
                dy = tile.arect.top - self.arect.bottom
                self.arect.bottom = tile.arect.top

            elif prev_arect.top >= tile.arect.bottom and tile.arect.top < self.arect.top < tile.arect.bottom:
                # From the bottom (jumped into this tile)
                self.vert_veloc = 0 # "stop in midair"
                dy = tile.arect.bottom - self.arect.top
                self.arect.top = tile.arect.bottom

            self.pos = x + dx, y + dy
            colliding_tiles = [s for s in self.level.walls if self.arect.colliderect(s.arect)]

        # Check out whether we need to fall. If we have a floor underneath, fall
        if not self.falling:
            collision = [s for s in self.level.walls if self.arect.move(0, 1).colliderect(s.arect)]

            if not collision:
                self.start_falling()