#  Copyright (c) 2007, Enthought, Inc.
#  License: BSD Style.

#--(Extended Property depends_on References)-----------------------------------
"""
Extended Property *depends_on* References
=========================================

In Traits 3.0, the **Property** *depends_on* metadata has been extended to
take advantage of the new extended trait name support offered by the
*on_trait_change* method.

Previously, the *depends_on* metadata for a *Property* was restricted to
referencing traits defined either on the same object as the **Property**, or
on an object immediately reachable from the object. For example::

    class Wheel(Part):

        axel     = Instance(Axel)
        position = Property(depends_on = 'axel.position')

        ...

Starting with Traits 3.0, the *depends_on* metadata may now include any
extended trait reference that is allowed by the enhanced *on_trait_change*
method. So, for example it is now legal to write things like::

    class Wheel(Part):

        axel     = Instance(Axel)
        position = Property(depends_on = 'axel.chassis.position')

or::

    class Child(Person):

        mother = Instance(Person)
        father = Instance(Person)
        mood   = Property(depends_on = ['mother.+mood_affecting',
                                        'father.+mood_affecting'])

In particular, in the last example we are declaring that the **Child** class's
*mood* property depends upon the values of any of either its mother or
father object's traits that have *mood_affecting* metadata defined.

Thus, a **Child** object's *mood* property will fire a trait change
notification whenever any of the its mother's or father's mood affecting
traits change.

Refer also to the code tabs for this lesson for a complete example using a
**Property** definition using *depends_on* metadata containing an extended
trait reference. In particular, take a look at the **LeagueModelView Class**
tab's *total_hits* trait definition.
"""

# FIXME redo example without traitsui

from traits.api import *

from traitsui.api import *

from traitsui.table_column import *


#--[Player Class]--------------------------------------------------------------
# Define a baseball player:
class Player(HasTraits):

    # The name of the player:
    name = Str('<new player>')

    # The number of hits the player made this season:
    hits = Int


#--[Team Class]----------------------------------------------------------------
# Define a baseball team:
class Team(HasTraits):

    # The name of the team:
    name = Str('<new team>')

    # The players on the team:
    players = List(Player)

    # The number of players on the team:
    num_players = Property(depends_on='players')

    def _get_num_players(self):
        """ Implementation of the 'num_players' property.
        """
        return len(self.players)


#--[League Class]--------------------------------------------------------------
# Define a baseball league model:
class League(HasTraits):

    # The name of the league:
    name = Str('<new league>')

    # The teams in the league:
    teams = List(Team)


#--[LeagueModelView Class]-----------------------------------------------------
# Define a ModelView for a League model:
class LeagueModelView(ModelView):

    # The currently selected team:
    team = Instance(Team)

    # The currently selected player:
    player = Instance(Player)

    # Button to add a hit to the current player:
    got_hit = Button('Got a Hit')

    # The total number of hits (note the 'depends_on' extended trait
    # reference):
    total_hits = Property(depends_on='model.teams.players.hits')

    @cached_property
    def _get_total_hits(self):
        """ Returns the total number of hits across all teams and players.
        """
        return reduce(add, [reduce(add, [p.hits for p in t.players], 0)
                            for t in self.model.teams], 0)

    view = View(
        VGroup(
            HGroup(
                Item('total_hits', style='readonly'),
                label='League Statistics',
                show_border=True,
                ),
            VGroup(
                Item('model.teams',
                     show_label=False,
                     editor=TableEditor(
                         columns=[ObjectColumn(name='name',
                                               width=0.70),
                                  ObjectColumn(name='num_players',
                                               label='# Players',
                                               editable=False,
                                               width=0.29)],
                         selected='object.team',
                         auto_add=True,
                         row_factory=Team,
                         configurable=False,
                         sortable=False)
                     ),
                label='League Teams',
                show_border=True,
                ),
            VGroup(
                Item('object.team.players',
                     show_label=False,
                     editor=TableEditor(
                         columns=[ObjectColumn(name='name',
                                               width=0.70),
                                  ObjectColumn(name='hits',
                                               editable=False,
                                               width=0.29)],
                         selected='object.player',
                         auto_add=True,
                         row_factory=Player,
                         configurable=False,
                         sortable=False),
                     ),
                '_',
                HGroup(
                    Item('got_hit',
                         show_label=False,
                         enabled_when='player is not None',
                         ),
                    ),
                label='Team Players',
                show_labels=False,
                show_border=True,
                ),
            ),
        resizable=True,
        )

    def _model_changed(self, model):
        """ Handles the 'league' model being initialized.
        """
        if len(model.teams) > 0:
            self.team = model.teams[0]

    def _got_hit_changed(self):
        """ Handles the currently selected player making a hit.
        """
        self.player.hits += 1

    def _team_changed(self, team):
        """ Handles a new team being selected.
        """
        if len(team.players) > 0:
            self.player = team.players[0]
        else:
            self.player = None

# Function to add two numbers (used with 'reduce'):
add = lambda a, b: a + b

#--[Example*]------------------------------------------------------------------

# Define some sample teams and players:
blue_birds = Team(name='Blue Birds', players=[
    Player(name='Mike Scott', hits=25),
    Player(name='Willy Shofield', hits=37),
    Player(name='Tony Barucci', hits=19)])

chicken_hawks = Team(name='Chicken Hawks', players=[
    Player(name='Jimmy Domore', hits=34),
    Player(name='Bill Janks', hits=16),
    Player(name='Tim Saunders', hits=27)])

eagles = Team(name='Eagles', players=[
    Player(name='Joe Peppers', hits=33),
    Player(name='Sam Alone', hits=12),
    Player(name='Roger Clemson', hits=23)])

# Create a league and its corresponding model view:
demo = LeagueModelView(
    League(name='National Baseball Conference',
           teams=[blue_birds, chicken_hawks, eagles]))
