
import copy

from pickups import *
from tiles import *
#from mysticmine import App
import world
from event import Event

class GoldCar:
    """A Car of Gold!

    Public members:
    - pos: TrailPosition
    - speed: the current speed
    - switch: next switch tile
    - switch_dist: distance to switch
    - score
    - amount: amount of gold in car (0,1,2,3)
    - collectible: current collectible on car
    """
    COLLIDE_DISTANCE = 500

    def __init__( self, position, nr ):
        self.pos = position
        self.speed = 70
        self.switch = None
        self.switch_dir = Direction.NORTH
        self.score = 0
        self.collectible = None
        self.modifier = None
        self.nr = nr
        self.amount = 0

        self._key_went_down = 0

    def game_tick( self ):
        if self.pos is None: return
        old_tile = self.pos.tile

        MIN_SPEED = 140
        MAX_SPEED = 230
        SLOPE_ACCEL = 1.2
        SLOPE_SLOWDOWN = 0.8
        SPEEDUP = 1.05
        if isinstance( self.modifier, Oiler ):
            MIN_SPEED = 150
            SLOPE_SLOWDOWN = 0.15
            SPEEDUP = 1.5

        # slow down speed
        self.speed = int(self.speed * 0.99);

        # DESIGN: don't stop the car
        if self.pos.tile.type == Tile.Type.FLAT and self.speed < MIN_SPEED:
            self.speed = int((self.speed+5) * SPEEDUP )

        # when going down/up
        if (self.pos.is_reversed() and not isinstance(self.collectible, Balloon)) or \
                   (not self.pos.is_reversed() and isinstance(self.collectible, Balloon)):
            self.speed = min( [int(self.speed + SLOPE_ACCEL * self.pos.tile.get_angle()), MAX_SPEED] )
        else:
            self.speed = min( [int(self.speed - SLOPE_SLOWDOWN * self.pos.tile.get_angle()), MAX_SPEED] )

        if self.speed < 0:
            self.speed *= -1
            self.pos.reverse_progress()
            self.select_next_switch()

        # special case when ghost
        if isinstance( self.modifier, Ghost ):
            self.speed = (MIN_SPEED + MAX_SPEED) / 2

        # update position
        self.pos += self.speed

        # tile with goldcar on can't be switched
        if self.pos.tile <> old_tile:
            old_tile.trail.may_switch = True
            self.pos.tile.trail.may_switch = False
            self.select_next_switch()

        # Update collectible
        if self.collectible is not None:
            self.collectible.game_tick()

            if isinstance( self.collectible, Lamp ):
                self.score += self.collectible.score()

            # Powerup worked out?
            if isinstance(self.collectible, PowerUp) \
               and self.collectible.is_done():
                self.collectible = None

        # Steal points from mirror
        if isinstance( self.modifier, Mirror ):
            self.score += self.modifier.twin.score
            self.modifier.twin.score = 0

        # Update modifier
        if self.modifier is not None:
            self.modifier.game_tick()
            # Powerup worked out?
            if self.modifier.is_done():

                if isinstance( self.modifier, Ghost ):
                    # playfield handles Ghost modifier
                    pass
                else:
                    self.modifier = None

        if self._key_went_down > 0:
            self._key_went_down -= 1

        # handle leprechaun
        if isinstance( self.collectible, Leprechaun ):
            if old_tile <> self.pos.tile and \
               self.score > 0 and \
               old_tile.pickup is None and \
               random.randint(0, 5) == 0:
                self.score -= 1
                old_tile.pickup = CopperCoin()

        # handle gate
        if isinstance( self.pos.tile, RailGate ) and \
           self.pos.tile.is_down and \
           self.pos.get_distance( TrailPosition(self.pos.tile, self.pos.tile.get_length()/2 )) < GoldCar.COLLIDE_DISTANCE/2:

            self.speed *= -1

    def clear_switch_and_pos( self ):
        if self.switch is not None:
            self.switch.set_selected( False )
            self.switch = None
            self.pos.tile.trail.may_switch = True

    def select_next_switch( self ):
        if self.switch is not None:
            self.switch.set_selected( False )

        it = copy.copy( self.pos )
        #if it.get_tile().is_hill() and it.speed < 0:
        #   it.speed = -it.speed
        out_dir = it.get_out_direction()
        it.to_next_tile()
        self.switch_dist = it.tile.get_length()

        count = 0
        while not it.tile.is_switch() and count < 100:
            #if it.get_tile().is_hill() and it.tile.get_out_direction() == it.in_dir:
            #	self.switch_pos = None
            #	return
            out_dir = it.get_out_direction()
            old_tile = it.tile
            it.to_next_tile()

            if old_tile == it.tile:
                # end of trail when same tile is returned as next
                self.switch = None
                return

            self.switch_dist += it.tile.get_length()

            count += 1

        if count < 100:
            self.switch = it.tile
            self.switch_dir = out_dir.get_opposite()
            self.switch.set_selected( True )
        else:
            self.switch = None

    def align_switch( self ):
        if self.switch is not None:
            self.switch.trail.align( self.switch_dir )

    def key_went_down( self ):
        return self._key_went_down > 0

    def keydown( self ):
        if self.switch is not None:
            self.switch.switch_it( self.switch_dir )
            Event.switch_trail()

        self._key_went_down = 2

        if isinstance( self.modifier, Mirror ):
            self.modifier.twin.keydown()

    def add_pickup( self, pickup ):
        multiplier = 1
        if isinstance( self.collectible, Multiplier ):
            multiplier = 2

        if isinstance( pickup, CopperCoin ):
            self.score += 1 * multiplier
            Event.coin_pickup( 1, self.pos )
        elif isinstance( pickup, GoldBlock ):
            self.score += 1 * multiplier
            Event.pickaxe()
        elif isinstance( pickup, Flag ):
            self.score += 1 * multiplier
            Event.flag_pickup( 1, self.pos )
        elif isinstance( pickup, Collectible ) or isinstance( pickup, PowerUp ):
            self.collectible = pickup
            self.collectible.goldcar = self
            self.collectible.container = self
            if isinstance( pickup, Diamond ):
                Event.diamond()
            elif isinstance( pickup, Axe ):
                Event.pickaxe_pickup()
            elif isinstance( pickup, RockBlock ):
                Event.rock()
            elif isinstance( pickup, Lamp ):
                Event.lamp()
            else:
                Event.pickup()
        elif isinstance( pickup, Modifier ):
            self.modifier = pickup
            self.modifier.set_goldcar( self )
            self.modifier.container = self
            Event.pickup()

    def switch_collectibles( self, car, other ):
        if car.collectible is not None:
            car.collectible.jump()
        if other.collectible is not None:
            other.collectible.jump()
        car.collectible, other.collectible = other.collectible, car.collectible
        if car.collectible is not None:
            car.collectible.container = car
        if other.collectible is not None:
            other.collectible.container = other


    def handle_collision( self, other ):
        if isinstance( self.modifier, Ghost ):
            if isinstance( self.modifier, Ghost ):
                if self.pos.get_distance( other.pos ) < GoldCar.COLLIDE_DISTANCE and \
                           self.collectible is None:
                    self.switch_collectibles( self, other )

        elif isinstance( other.modifier, Ghost ):
            pass
        else:
            SPEEDUP  = 1.05
            SLOWDOWN = 0.95

            # Handle collision
            if self.pos.get_distance( other.pos ) < GoldCar.COLLIDE_DISTANCE:
                Event.carhit()

                self.speed, other.speed = other.speed, self.speed
                if not self.pos.same_direction( other.pos ):
                    self.pos.reverse_progress()
                    other.pos.reverse_progress()
                diff = GoldCar.COLLIDE_DISTANCE - self.pos.get_distance( other.pos )

                proportion1 = float(self.speed) / (self.speed + other.speed)
                proportion2 = float(other.speed) / (self.speed + other.speed)

                if not self.pos.same_direction( other.pos ):
                    self.pos += int(diff * proportion1)
                    other.pos += int(diff * proportion2)
                    self.speed *= SPEEDUP
                    other.speed *= SPEEDUP
                elif self.speed <= other.speed:
                    self.pos -= int(diff * proportion1)
                    other.pos += int(diff * proportion2) + 1 # for rounding
                    self.speed *= SLOWDOWN
                    other.speed *= SPEEDUP
                else:
                    self.pos += int(diff * proportion1) + 1 # for rounding
                    other.pos -= int(diff * proportion2)
                    self.speed *= SPEEDUP
                    other.speed *= SLOWDOWN

                self.switch_collectibles( self, other )

            # Handle too close (in real extreme cases!)
            elif self.pos.get_distance( other.pos ) < GoldCar.COLLIDE_DISTANCE * 3 / 2 \
                 and self.pos.same_direction( other.pos ) \
                 and abs( self.speed - other.speed ) < 10:
                if self.pos + self.pos.get_distance( other.pos ) == other.pos:
                    self.speed *= SLOWDOWN
                    other.speed *= SPEEDUP
                elif other.pos + self.pos.get_distance( other.pos ) == self.pos:
                    self.speed *= SPEEDUP
                    other.speed *= SLOWDOWN
                else:
                    assert False, "Should be one or the other!"
