# Schema as of Mon Nov 27 15:17:56 EST 2006 (schema version 35)
#
#  id                  :integer(11)   not null
#  body                :text          
#  created_by          :integer(11)   
#  created_at          :datetime      
#  topic_id            :integer(11)   default(0)
#  root_id             :integer(11)   default(0)
#  parent_id           :integer(11)   default(0)
#  lft                 :integer(11)   default(0)
#  rgt                 :integer(11)   default(0)
#  depth               :integer(11)   default(0)
#  removed             :boolean(1)    
#  word_count          :integer(11)   
#  reading_count       :integer(11)   default(0)
#  avg_rating          :float         
#


# Comments that are on a topic
class Comment < ActiveRecord::Base
  acts_as_threaded
  #acts_as_nested_set
  # TODO strip out any unsafe characters
  # TODO use h() to show body content
  # alias :children :direct_children
  
  belongs_to :topic
  belongs_to :user,    :class_name => 'User', :foreign_key => 'created_by'
  belongs_to :parent,  :class_name => 'Comment', :foreign_key => 'parent_id'
  
  # has_many   :ratings, :class_name =>  'CommentRating', :foreign_key => 'subject_id', :include => :user do
  has_many   :ratings, :class_name =>  'CommentRating', :foreign_key => 'subject_id' do
       def for_user(user)
         # don't do anything if this is this users comment
         return nil unless RatingSpecification::user_not_self_rating(proxy_owner, user)

         # don't do anything for non-participants/guests
         return nil unless RatingSpecification::user_is_a_participant_or_guest(proxy_owner, user)

         # otherwise, grab the existing rating, or build a new one
         # detect{|r|r.participant_id == user.id } || build(:user => user) 
         # find(:first, :conditions => ['participant_id = ?', user.id]) || build(:user => user) 
         user.comment_ratings.detect{|r|r.subject == proxy_owner } || build(:user => user) 
       end
  
       def average_value_excluding_77s
         average(:value, :conditions => 'value != 77')
       end
     end

  has_many   :readings,
             :as => :readable,
             :dependent => :delete_all
             
    
  validates_length_of :body, :within => 2 .. 5000
  validates_presence_of :topic
  validate :topic_is_open_for_comment
  
  before_validation :count_words
  
  def topic_is_open_for_comment
    errors.add('topic', "cannot be commented on") unless topic.commentable?    
  end
  
  def commentable?
    !hidden? && topic.commentable?
  end
  
  # can this comment be rated -- depend on the topic it is attached to and whether this comment is hidden or not
  def rateable?
    commentable?
  end
  
  def subject # TODO write test for this method
    parent_id == 0 ? topic : Comment.find(parent_id)
  end
  
  def hidden? # TODO replace this with a boolean field in the DB
    false
  end
  
  def count_words
    self.word_count = (body || '').split.length
  end
  
  # Recalculate the average rating and update the avg_rating field.
  #
  # Until there is more than 1 rating, the average stays at zero.
  def update_average_rating_cache
    update_attribute(:avg_rating, ratings.count > 1 ? ratings.average_value : 0)
  end

  # Returns a set of only this entry's immediate children  
  def children
    return [] unless has_children?
    self.class.find(:all, :conditions => "#{scope_condition} AND #{parent_column} = #{self.id}")
    # self.class.find(:all, 
    #   :conditions => "#{scope_condition} AND #{parent_column} = #{self.id}", 
    #   :include => [:user, [:ratings => [:user]]])
  end
  
  def children_ids
    return [] unless has_children?
    self.class.connection.select_all("SELECT id FROM #{self.class.table_name} WHERE #{scope_condition} AND #{parent_column} = #{self.id}")
  end
  
  def has_children?
    if self.rgt - self.lft == 1
      logger.info("this comment has no children -- left: #{self.lft}, right: #{self.rgt}")
      false
    else
      return true
    end
  end
  
  class << self
    # TODO hook up these stats gatherers to a slider (or even the measuremap dateslider)
    # to get better parsing of data over time    
    
    # Find the comment that has drawn the most ratings
    #
    # Returns a Comment or Nil if none found
    def find_most_rated
      find(rating_klass.most_rated_subject_id) rescue nil
    end
  
    # Find the comment that has the highest mean rating.
    #
    # Returns a Comment or Nil if none found
    def find_best_mean_rated
      find(rating_klass.best_average_rated_subject_id) rescue nil
    end
  
    # Find the comment that has the most direct replies.
    #
    # Returns a Comment or Nil if none found
    def find_most_replied
      find(self.count(:group => :parent_id, 
         :conditions => 'root_id != parent_id AND parent_id != 0',
         :limit => 1, :order => 'count_all DESC'
         ).first.first) rescue nil
    end
    
    # returns the number of comments that have not been removed
    def count_visible
      count(:conditions => 'removed != 1')
    end
    
    def find_three_most_read  
      find(:all, :limit => 3, :order => 'reading_count DESC', :conditions => 'reading_count > 0', :include => :user)
    end
    
    # Highest rated is a misnomer, as the rating system starts at 1 with Agree Strongly,
    # and ends at 4 with Disagree Strongly
    def find_three_highest_avg_rating
      find(:all, :limit => 3, :order => "#{Comment.table_name}.avg_rating ASC", :conditions => "#{Comment.table_name}.avg_rating > 0", :include => :user)
    end
    
    def find_three_newest
      self.find(:all, :limit => 3, :order => "#{Comment.table_name}.created_at DESC", :include => :user)
    end
    
    def find_most_read
      find(:first, 
        :select => "#{table_name}.*, count(*) as 'fresh_reading_count'",
        :joins  => "LEFT JOIN readings ON #{table_name}.id = #{Reading.table_name}.readable_id AND #{Reading.table_name}.readable_type = 'Comment'",
        :group  => "#{table_name}.id",
        :order  => 'fresh_reading_count DESC'
      )
    end
    
    def rating_klass
      reflect_on_association(:ratings).klass
    end
    
    def all_parent_children_ids(comment_ids = [])
      return [] if comment_ids.empty?
      parent_children_ids = {}
      self.connection.select_all("SELECT id, parent_id FROM #{self.table_name} WHERE id IN (#{comment_ids * ', '})").each do |row|
        parent_children_ids[row['parent_id'].to_i] ||= []
        parent_children_ids[row['parent_id'].to_i] << row['id'].to_i
      end
      return parent_children_ids
    end
  end
end
