"""
Copyright (C) 2008  Matthew and Joey Marshall <joey@arcticpaint.com>

See main.py for full notice.
"""
from __future__ import division
import rabbyt
import random
import math

from gamelib.platform import Platform, SortingRules
from gamelib.station  import COLORS, _COLORS_LOOKUP

class _ConnectionPlatform(Platform):
    def __init__(self, connection, offset, **kwargs):
        Platform.__init__(self, connection.world, **kwargs)
        self.connection = connection
        self.offset = offset

        self.schedule_next_gondola()

    def schedule_next_gondola(self, ignores=(), dt=None):
        """
        Finds which gondola will be the next one to pass us, and schedule
        to handle it when it comes.  (This assumes that the gondolas are
        always moving at a constant rate.
        """
        clock = self.connection.world.clock
        self.next_gondola, dt = self.get_next_gondola(ignores)
        clock.schedule_once(self._handle_next_gondola, dt)

    def get_next_gondola(self, ignores=(), only_non_empty=False):
        cable = self.connection.cable

        closest = None, 0
        highest = None, 0
        for g in cable.gondolas:
            if g in ignores or (only_non_empty and not g.contents):
                continue
            o = g.offset
            if o > closest[1] and o < self.offset:
                closest = g, o
            if o > highest[1]:
                highest = g, o
        if closest[0]:
            return closest[0], (self.offset-closest[1])/cable.speed
        elif highest[0]:
            # If there was no gondola with an offset lower than ours, we must
            # be at the begining of the cable.  So we use the highest gondola.
            return highest[0], (self.offset+cable.cable_length-highest[1])/cable.speed
        else:
            # No gondola found!
            return None, 0


    def _handle_next_gondola(self, dt=None):
        self.handle_gondola(self.next_gondola)
        self.schedule_next_gondola(ignores=(self.next_gondola,))

class LoadingPlatform(_ConnectionPlatform):
    """
    Loads items onto a gondola.
    """
    def handle_gondola(self, gondola):
        if gondola.contents or not self.contents:
            return
        item = self.contents
        self.contents = None
        gondola.contents = item
        item.gondola = gondola


class UnloadingPlatform(_ConnectionPlatform):
    """
    Unloads items from a gondola.
    """
    def handle_gondola(self, gondola):
        if not gondola.contents:
            return
        if self.contents:
            self.contents.drop_in_sea()
        self.contents = gondola.contents
        gondola.contents = None

        next_gondola, dt = self.get_next_gondola(only_non_empty=True)
        if not dt:
            dt = self.connection.cable.length/self.connection.cable.speed
        self.min_move_weight = rabbyt.lerp(4,1,dt=dt)

class Connection(object):
    """
    This is a connection between a station and a cable.

    Each cable will have two Connections.  Each station will have one
    Connection for each station/cable to which it is connected.
    """
    def __init__(self, station, cable, opposite_station, end):
        self.station = station
        self.cable = cable
        self.world = station.world
        self.opposite_station = opposite_station
        self.end = end # Which end of the cable this connection is on ("a" or "b")

        d = 35
        if self.end == "a":
            self._unload_offset = self.cable.cable_length - self.station.radius*math.pi - d
            self._load_offset = d
            self.angle_d = self.cable.angle_d % 360
        elif self.end == "b":
            self._unload_offset = self.cable.length - d
            self._load_offset = self.cable.length + self.station.radius*math.pi + d
            self.angle_d = (self.cable.angle_d + 180) % 360

        self.entry_rules = SortingRules(allow_drop=False)
        self.exit_rules = SortingRules(allow_pickup=False)

        # Items entering the Station from the Cable:
        self.entry_platforms = [UnloadingPlatform(self, self._unload_offset, 
            rules=self.entry_rules)]
        # Items entering leaving the Station to the Cable:
        self.exit_platforms = [LoadingPlatform(self, self._load_offset, 
            rules=self.exit_rules)]

        self.set_platform_positions()

        for p in self.entry_platforms:
            p.discouraged = set(self.exit_platforms)



        angle_r = self.cable.angle_r
        if self.end == "b":
            angle_r += math.pi
        dist = 80
        if cable.length < 240:
            dist = cable.length/2 - 40
        self.filter_sprite = rabbyt.Sprite(xy=self.station.xy)
        self.filter_sprite.x += dist*math.cos(angle_r)
        self.filter_sprite.y += dist*math.sin(angle_r)
        self.filter_sprite.rot = math.degrees(angle_r)

        self.filter_sprite_arrow = rabbyt.Sprite("arrow.png",
                xy=self.station.xy)
        self.filter_sprite_arrow.x += (dist+20)*math.cos(angle_r)
        self.filter_sprite_arrow.y += (dist+20)*math.sin(angle_r)
        self.filter_sprite_arrow.rot = math.degrees(angle_r)
        self.filter_sprite_arrow.scale = rabbyt.lerp(start=.7, end=1, dt=1,
                extend="reverse")

        self.filter_sprite_bg = rabbyt.Sprite("arrow_bg.png",
                xy=self.station.xy)
        self.filter_sprite_bg.x += (dist+10)*math.cos(angle_r)
        self.filter_sprite_bg.y += (dist+10)*math.sin(angle_r)
        self.filter_sprite_bg.rot = math.degrees(angle_r)
        self.filter_sprite_bg.connection = self
        self.world.filters.append(self.filter_sprite_bg)

        self.station.world.renderables["hud"].append(self.filter_sprite_bg)
        self.station.world.renderables["hud"].append(self.filter_sprite)
        self.station.world.renderables["hud"].append(self.filter_sprite_arrow)

        self.match_color = None
        self.match_figure = "all"


    def set_platform_positions(self):
        entry_offsets = [o*.5 for o in self.cable._direction_offsets[1]]
        exit_offsets = [o*.5 for o in self.cable._direction_offsets[0]]
        if self.end == "a":
            angle_r = self.cable.angle_r
        else:
            entry_offsets, exit_offsets = exit_offsets, entry_offsets
            angle_r = self.cable.angle_r + math.pi

        distance = 35
        for p in self.entry_platforms:
            p.rot = math.degrees(angle_r)
            p.x = self.station.x + entry_offsets[0] + distance*math.cos(angle_r)
            p.y = self.station.y + entry_offsets[1] + distance*math.sin(angle_r)
            distance += 16

        distance = 35
        for p in self.exit_platforms:
            p.rot = math.degrees(angle_r)
            p.x = self.station.x + exit_offsets[0] + distance*math.cos(angle_r)
            p.y = self.station.y + exit_offsets[1] + distance*math.sin(angle_r)
            distance += 16

    def unload_item(self, gondola):
        """
        Unloads the item from the given gondola.
        """
        item = gondola.contents
        item.gondola = None
        gondola.contents = None

        for p in self.entry_platforms:
            if not p.contents:
                p.contents = item
                break
        else:
            p = random.choice(self.entry_platforms)
            p.contents.drop_in_sea()
            p.contents = item

    def _set_match_figure(self, value):
        if value == "all":
            value = None
        if not value:
            self.filter_sprite.texture = "all.png"
            self.filter_sprite.alpha = 0
        else:
            self.filter_sprite.texture = value+".png"
            self.filter_sprite.alpha = 1
        self.exit_rules.match_figure = value
        self._match_figure = value
    match_figure = property((lambda self:self._match_figure), _set_match_figure)

    def _set_match_color(self, value):
        self.exit_rules.match_color = value
        self._match_color = value
        if value:
            self.filter_sprite.rgb = _COLORS_LOOKUP[value]
            self.filter_sprite_arrow.rgb = _COLORS_LOOKUP[value]
        else:
            self.filter_sprite.rgb = 1,1,1
            self.filter_sprite_arrow.rgb = 1,1,1
    match_color = property((lambda self:self._match_color), _set_match_color)

