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

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

class SortingRules(object):
    def __init__(self, allow_pickup=True, allow_drop=True, starting_weight=5,
            match_color=None, match_figure=None):
        # This is whether the station sorter is allowed to pick items up off
        # of the platform or drop items off the platform.  For example,
        # a platform used to load items on the gondola should not allow the
        # sorter to pickup, and a platform for receiving items should not
        # allow dropping.  (But a platform designed to help with overflow
        # could allow both.)
        self.allow_pickup = allow_pickup
        self.allow_drop = allow_drop
        self.starting_weight = starting_weight
        self.match_color = match_color
        self.match_figure = match_figure

    def get_weight(self, item):
        if not self.allow_drop:
            return 0

        weight = self.starting_weight

        if self.match_figure is not None:
            if self.match_figure == item.figure:
                weight += 10
            else:
                return 0
        if self.match_color is not None:
            if self.match_color == item.color:
                weight += 10
            else:
                return 0
        return weight

class Platform(rabbyt.Sprite):
    """
    This is a part of a station that can store *one* item.
    """
    min_move_weight = rabbyt.anim_slot()
    def __init__(self, world, xy=(0,0), rot=0, rules=None,
            allow_drop=None, allow_pickup=None,
            smooth_entry=True, render_layer="platforms"):
        rabbyt.Sprite.__init__(self, "gondola.png", xy=xy, rot=rot)
        self.world = world
        if render_layer:
            self.world.renderables[render_layer].append(self)
        self._render_layer = render_layer

        if rules is not None:
            self.rules = rules
        else:
            self.rules = SortingRules()
        if allow_drop is not None:
            self.rules.allow_drop = allow_drop
        if allow_pickup is not None:
            self.rules.allow_pickup = allow_pickup
        # Rules for specific sorters:
        self.sorter_rules = {}

        self.smooth_entry = smooth_entry

        # When choosing a platform to move an item to, the sorter should
        # first try to move it to a platform that is *not* in this set.
        # (This can be used to prevent an item from being sent right back
        # along the same cable, if it can be avoided.)
        self.discouraged = set()
        # More strict than discouraged:
        self.forbidden = set()

        self.contents = None

        self.match_figure = None
        self.match_color = None

        self.fading_out_sprite = None

        # An item will not be removed from this platform unless if the 
        # target platform's weight is >= this value:
        self.min_move_weight = 1

        self.blocked = False

    def remove(self):
        if self._render_layer:
            self.world.renderables[self._render_layer].remove(self)

    def get_rules(self, sorter):
        return self.sorter_rules.get(sorter, self.rules)

    def get_weight(self, item, sorter, other_platform):
        if self.blocked and self.blocked != sorter:
            return 0

        if self in other_platform.forbidden:
            return 0

        rules = self.get_rules(sorter)
        weight = rules.get_weight(item)


        if self in other_platform.discouraged:
            weight -= 2

        return weight

    def get_allow_pickup(self, sorter):
        """
        Returns true if the sorter is allowed to move the contents to the other
        platform.
        """
        if self.blocked and self.blocked != sorter:
            return False
        if not self.get_rules(sorter).allow_pickup:
            return False
        return True

    def render(self):
        rabbyt.Sprite.render(self)
        if self.fading_out_sprite:
            self.fading_out_sprite.render()
            if self.fading_out_sprite.alpha <= 0.001:
                self.fading_out_sprite = None
        if self._contents:
            self._contents.render()

    def _set_contents(self, item):
        if getattr(self, "_contents", None) and self._contents.platform is self:
            self._contents.platform = None
        self._contents = item
        if item:
            item.platform = self
    contents = property(lambda self:self._contents, _set_contents)

    def temporarily_block(self, dt):
        self.blocked = True
        def finish(_):
            self.blocked = False
        self.world.clock.schedule_once(finish, dt)

    def remove_fade(self, dt=.5):
        """
        Immediately removes the contained item, but gives the appearance of
        it fading out.
        """
        if not self.contents: return
        i = self.contents
        self.fading_out_sprite = rabbyt.Sprite(i.texture, rgb=i.rgb,
                xy=self.attrgetter("xy"), rot=self.attrgetter("rot"),
                alpha=rabbyt.lerp(1,0,dt=dt))
        self._contents = None

    def add_fade(self, item, dt=.5):
        """
        Adds an item to the platform, fading it in.
        
        If allow_pickup is True, it will be set to False while the item is
        fading in.
        """
        self.contents = item
        item.alpha = rabbyt.lerp(0, 1, dt=dt)
        self.temporarily_block(dt)

    def clear_block(self, blocker=None):
        """
        Clears the block, but only if the passed blocker was the one blocking.
        """
        if blocker:
            if blocker is self.blocked:
                self.blocked = False
                return True
            else:
                return False
        else:
            self.blocked = False
            return True

    def acquire_block(self, blocker):
        if not self.blocked or self.blocked is blocker:
            self.blocked = blocker
            return True
        else:
            return False

