class Beatle
  include Walruz::Actor
  include Walruz::Subject
  
  attr_reader :name
  attr_accessor :songs
  attr_accessor :colaborations
  
  def initialize(name)
    @name = name
    @songs = []
    @colaborations = []
  end
  
  def sing_the_song(song)
    response = authorize(:sing, song)
    case response[:owner]
    when Colaboration
      authors = response[:owner].authors.dup
      authors.delete(self)
      authors.map! { |author| author.name }
      "I need %s to play this song properly" % authors.join(', ')
    when Beatle
      "I just need myself, Let's Rock! \\m/"
    end
  end
  
  def sing_with_john(song)
    authorize(:sing_with_john, song)
    "Ok John, Let's Play '%s'" % song.name
  end


  JOHN   = self.new("John")
  PAUL   = self.new("Paul")
  RINGO  = self.new("Ringo")
  GEORGE = self.new("George")
end

class Colaboration
  
  attr_accessor :authors
  attr_accessor :songs
  
  def initialize(*authors)
    authors.each do |author|
      author.colaborations << self
    end
    @authors = authors
    @songs = []
  end
  
  JOHN_PAUL = self.new(Beatle::JOHN, Beatle::PAUL)
  JOHN_PAUL_GEORGE = self.new(Beatle::JOHN, Beatle::PAUL, Beatle::GEORGE)
  JOHN_GEORGE = self.new(Beatle::JOHN, Beatle::GEORGE)
end

class SubjectIsActorPolicy < Walruz::Policy
  
  def authorized?(actor, subject)
    actor == subject
  end
  
end

# class AuthorPolicy < Walruz::Policy
#   
#   def authorized?(beatle, song)
#     if song.author == beatle
#       [true, { :owner => beatle }]
#     else
#       false
#     end
#   end
#   
# end

AuthorPolicy = SubjectIsActorPolicy.for_subject(:author) do |authorized, params, actor, subject|
  params.merge!(:owner => actor) if authorized
end

class AuthorInColaborationPolicy < Walruz::Policy
  
  def authorized?(beatle, song)
    return false unless song.colaboration
    if song.colaboration.authors.include?(beatle)
      [true, { :owner => song.colaboration }]
    else
      false
    end
  end
  
end

class ColaboratingWithJohnPolicy < Walruz::Policy
  depends_on AuthorInColaborationPolicy
  
  def authorized?(beatle, song)
    params[:owner].authors.include?(Beatle::JOHN)
  end
  
end

class Song
  include Walruz::Subject
  extend Walruz::Utils

  check_authorizations :sing => any(AuthorPolicy, AuthorInColaborationPolicy),
                       :sell => all(AuthorPolicy, negate(AuthorInColaborationPolicy)),
                       :sing_with_john => ColaboratingWithJohnPolicy
  attr_accessor :name
  attr_accessor :colaboration
  attr_accessor :author
  
  def initialize(name, owner)
    @name = name
    case owner
    when Colaboration
      @colaboration = owner
    when Beatle
      @author = owner
    end
    owner.songs << self
  end
  
  A_DAY_IN_LIFE        = self.new("A Day In Life", Colaboration::JOHN_PAUL)
  YELLOW_SUBMARINE     = self.new("Yellow Submarine", Colaboration::JOHN_PAUL)
  TAXMAN               = self.new("Taxman", Colaboration::JOHN_GEORGE)
  YESTERDAY            = self.new("Yesterday", Beatle::PAUL)
  ALL_YOU_NEED_IS_LOVE = self.new("All You Need Is Love", Beatle::JOHN)
  BLUE_JAY_WAY         = self.new("Blue Jay Way", Beatle::GEORGE)
end