# pygsear
# Copyright (C) 2003 Lee Harr
#
#
# This file is part of pygsear.
#
# pygsear is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 2 of the License, or
# (at your option) any later version.
#
# pygsear is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with pygsear; if not, write to the Free Software
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA

import random

from twisted.internet.error import AlreadyCalled, ConnectError

from pygame.sprite import Group

from pygsear import Drawable
from pygsear.Drawable import RotatedImage, Circle
from pygsear import conf
from pygsear.locals import *

import Server


class Shot(Circle):
    def __init__(self, position, direction, screen):
        self.hidden = 0
        Circle.__init__(self, radius=2)
        self.set_position(position)
        self.path.set_speed(250)
        self.path.set_direction(direction)
        self.active_screen = screen


    def move(self):
        Circle.move(self)
        if not self.onscreen():
            self.kill()        

class Tank(RotatedImage):
    def __init__(self, filename='tank_blue.png'):
        self.stop()
        self.hidden = 0
        RotatedImage.__init__(self, filename=filename, steps=180, convert=1)
        self.crect.inflate_ip(-19, -19)
        self.set_positionRandom()

        self.max_shots = 7
        self.active_shots = Group()
        self.shots = self.max_shots

        self.repair()

        self.max_speed = 150

    def set_game(self, game):
        self.game = game
        self.set_random_screen()

    def set_active_screen(self, screen):
        self.active_screen = screen
        self.screen_name = screen.name

    def set_random_screen(self):
        self.set_active_screen(random.choice(self.game.world.screens))
        self.set_random_position()

    def set_random_position(self):
        self.set_positionRandom()
        while 1:
            block = self.collidelist(self.game.world.blocks.sprites())
            if not block:
                break
            else:
                self.set_positionRandom()

    def repair(self):
        self.to_kill = 10

    def hit(self):
        print 'hit: %s' % self.to_kill
        self.to_kill -= 1
        if not self.to_kill:
            print 'killed'
            print
            if hasattr(self, 'shot'):
                try:
                    self.shot.cancel()
                except AlreadyCalled:
                    print 'shoot already called'
                    pass
            self.repair()
            self.reload()
            self.set_random_screen()

    def reload(self, ev=None):
        if not self.game.solo:
            self.game.connection.r_reload()
        self.shots = self.max_shots

    def shoot(self):
        if self.shots < 0:
            print 'must reload'
        elif len(self.active_shots.sprites()) > 5:
            print 'wait'
        else:
            self.shots -= 1
            position = self.get_position()
            rotation = self.get_rotation()
            screen = self.active_screen
            shot = Shot(position, rotation, screen)
            self.game.sprites.add(shot)
            self.active_shots.add(shot)

    def stop(self):
        self.turning_right = 0
        self.turning_left = 0
        self.accelerating = 0
        self.baccelerating = 0
        self.speed = 0

    def accelerate(self, ev=None):
        self.accelerating = 1

    def noaccelerate(self, ev=None):
        self.accelerating = 0

    def _accelerate(self):
        s = self.speed
        if s > -5:
            s += 0.20 * (self.max_speed - s)
        else:
            s *= 0.6
        self.speed = s

    def baccelerate(self, ev=None):
        self.baccelerating = 1

    def nobaccelerate(self, ev=None):
        self.baccelerating = 0

    def _baccelerate(self):
        s = self.speed
        if s < 5:
            s -= 0.15 * (self.max_speed + s)
        else:
            s *= 0.7
        self.speed = s

    def decelerate(self):
        s = self.speed
        if s > 1:
            s *= 0.9
            s = max(s, 0)
        elif s < -1:
            s *= 0.9
            s = min(s, 0)
        else:
            s = 0
        self.speed = s

    def right(self, ev=None):
        self.turning_right = 1

    def noright(self, ev=None):
        self.turning_right = 0

    def left(self, ev=None):
        self.turning_left = 1

    def noleft(self, ev=None):
        self.turning_left = 0

    def turn(self):
        if self.turning_right and not self.turning_left:
            self.rotate_right()
        elif self.turning_left and not self.turning_right:
            self.rotate_left()
        else:
            self.rotate_stop()

    def align(self):
        r = self.get_rotation()
        s = self.speed

        if s >= 0:
            self.path.set_direction(r)
        else:
            self.path.set_direction(r + PI)

    def accel(self):
        if self.accelerating and not self.baccelerating:
            self._accelerate()
        elif self.baccelerating and not self.accelerating:
            self._baccelerate()
        else:
            self.decelerate()

        self.path.set_speed(abs(self.speed))

    def check_screen(self):
        if not self.onscreen():
            x, y = self.get_position()
            if x > conf.WINWIDTH:
                screen = self.active_screen.neighbors['right']
                if screen is not None:
                    self.set_active_screen(screen)
                    x = 25
                    self.path.set_position((x, y))

            elif x < 0:
                screen = self.active_screen.neighbors['left']
                if screen is not None:
                    self.set_active_screen(screen)
                    x=conf.WINWIDTH-30
                    self.path.set_position((x, y))

            elif y > conf.WINHEIGHT:
                screen = self.active_screen.neighbors['below']
                if screen is not None:
                    self.set_active_screen(screen)
                    y = 25
                    self.path.set_position((x, y))

            elif y < 0:
                screen = self.active_screen.neighbors['above']
                if screen is not None:
                    self.set_active_screen(screen)
                    y=conf.WINHEIGHT-30
                    self.path.set_position((x, y))

    def move(self):
        self.check_screen()

        self.accel()
        self.turn()
        self.align()

        RotatedImage.move(self)


class LocalTank(Tank):
    def shoot(self, ev=None):
        if not self.game.solo:
            self.game.connection.r_shoot()
        Tank.shoot(self)

    def set_active_screen(self, screen):
        Tank.set_active_screen(self, screen)
        self.game.world.set_active_screen(screen)
        if not self.game.solo:
            self.game.connection.r_set_active_screen(self.screen_name)

    def move(self):
        Tank.move(self)
        if self.active_screen != self.game.active_screen:
            self.game.set_active_screen(self.active_screen)


class RemoteTank(Tank):
    def __init__(self):
        Tank.__init__(self, filename='tank_red.png')
        self.is_drone = 0
    
    def r_update(self, pos, rot, vel, acc):
        self.set_position(pos)
        self.set_rotation(rot)
        vx, vy = vel
        self.path.set_velocity(vx=vx, vy=vy)
        ax, ay = acc
        self.path.set_acceleration(ax=ax, ay=ay)

    def r_set_active_screen(self, screen_name):
        screen = self.game.world.screens[screen_name]
        self.set_active_screen(screen)


class Drone(Tank):
    def __init__(self):
        Tank.__init__(self, filename='tank_red.png')
        self.maneuver = self.patrol
        self.is_drone = 1
        self.avoiding = 0

    def patrol(self):
        if not self.onscreen(slack=30):
            self.rotate_towards((400, 300))
            if self.rotationRate > 0:
                self.noright()
                self.left()
            elif self.rotationRate < 0:
                self.noleft()
                self.right()
        else:
            self.noleft()
            self.noright()
        self.noleft()
        self.noright()
        self.nobaccelerate()
        self.accelerate()

    def avoid(self):
        self.noaccelerate()
        self.baccelerate()
        self.noleft()
        self.right()
        self.until -= conf.ticks
        if self.until < 0:
            self.switch()

    def switch(self):
        if self.avoiding:
            self.avoiding = 0
            self.maneuver = self.patrol
        else:
            self.avoiding = 1
            self.maneuver = self.avoid
            self.until = conf.ticks + random.randrange(200, 700)

    def shoot(self):
        Tank.shoot(self)
        if not self.shots:
            self.game.reactor.callLater(5, self.reload)
        else:
            self.shot = self.game.reactor.callLater(2, self.shoot)

    def reload(self):
        Tank.reload(self)
        if hasattr(self, 'game'):
            self.shot = self.game.reactor.callLater(2, self.shoot)

    def move(self):
        self.maneuver()
        Tank.move(self)


def main():
    print 'testing Tank'
    t = Tank()
    print 'test complete'

if __name__ == '__main__':
    main()
