# A relation defines a type of {Tie tie}. Relations are affective (friendship, liking, 
# respect), formal or biological (authority, kinship), transfer of material 
# resources (transactions, lending and borrowing), messages or conversations, 
# physical connection and affiliation to same organizations.
#
# = Strength hierarchies
#
# Relations are arranged in strength hierarchies, denoting that some ties between
# two actors are stronger than others. For example, a "friend" relation is stronger than 
# an "acquaintance" relation.
#
# When a strong tie is established, ties with weaker relations are establised as well
#
# = Permissions
#
# {SocialStream::Models::Subject Subjects} assign {Permission permissions} to relations.
# This way, when establishing {Tie ties}, they are granting permissions to their contacts.
#
# See the documentation of {Permission} for more details on permission definition.
#
# = {Activity Activities} and {Relation relations}
# Each {Activity} can be attached to one or more {Relation relations}.
# The {Relation} sets up the mode in which the {Activity} is shared.
# It sets the {Audience} that has access to it, and the {Permission Permissions} that rule that access.
#
class Relation < ActiveRecord::Base
  belongs_to :actor

  has_many :relation_permissions, :dependent => :destroy
  has_many :permissions, :through => :relation_permissions

  has_many :ties, :dependent => :destroy
  has_many :contacts, :through => :ties

  has_many :audiences, :dependent => :destroy
  has_many :activities, :through => :audiences

  scope :mode, lambda { |st, rt|
    where(:sender_type => st, :receiver_type => rt)
  }

  before_create :initialize_sender_type

  class << self
    # Get relation from object, if possible
    #
    # Options::
    # sender:: The sender of the tie
    def normalize(r, options = {})
      case r
      when Relation
        r
      when String
        if options[:sender]
          options[:sender].relation_custom(r)
        else
          raise "Must provide a sender when looking up relations from name: #{ options[:sender] }"
        end
      when Integer
        Relation.find r
      when Array
        r.map{ |e| Relation.normalize(e, options) }
      else
        raise "Unable to normalize relation #{ r.class }: #{ r.inspect }"
      end
    end

    def normalize_id(r, options = {})
      case r
      when Integer
        r
      when Array
        r.map{ |e| Relation.normalize_id(e, options) }
      else
        normalize(r, options).id
      end
    end
 
    # All the relations that allow subject to perform action
    #
    # Options:
    #   in:: Limit possible relations to a set
    #   public_relations:: include also {Relation::Public} whose activities can always be read
    def allow(subject, action, object, options = {})
      q = 
        select("DISTINCT relations.*").
        joins(:contacts).
        joins(:permissions)

      conds =
        Permission.arel_table[:action].eq(action).and(Permission.arel_table[:object].eq(object))

      # Relation::Public permissions cannot be customized yet
      if action == 'read' && object == 'activity' && (options[:public].nil? || options[:public])
        conds = conds.or(Relation.arel_table[:type].eq('Relation::Public'))
      end

      # Add in condition
      if ! options[:in].nil?
        conds = conds.and(Relation.arel_table[:id].in(Relation.normalize_id(Array(options[:in]))))
      end

      # subject conditions
      conds = conds.and(Contact.arel_table[:receiver_id].eq(Actor.normalize_id(subject)))

      q.where(conds)
    end

    def allow?(*args)
      allow(*args).to_a.any?
    end
  end

  # Relation class scoped in the same mode that this relation
  def mode
    Relation.mode(sender_type, receiver_type)
  end

  private

  # Before create callback
  def initialize_sender_type
    return if actor.blank?

    self.sender_type = actor.subject_type
  end
end