class Resource < ActiveRecord::Base
  #acts_as_taggable
  acts_as_authorizable
  acts_as_versioned

  include Comparable
  
  has_many :flags
  
  belongs_to :resource_type
  belongs_to :stage
  belongs_to :license
  belongs_to :support
  belongs_to :os_support
  belongs_to :size_scale
  belongs_to :superceded_by, :class_name => "Resource", :foreign_key => "superceded_id"
  
  has_and_belongs_to_many :orgs
  has_and_belongs_to_many :similar,
    :class_name => 'Resource',
    :join_table => 'similarity',
    :foreign_key => 'resource_id',
    :association_foreign_key => 'similar_id'
  has_and_belongs_to_many :related,
    :class_name => 'Resource',
    :join_table => 'relatedness',
    :foreign_key => 'resource_id',
    :association_foreign_key => 'related_id'
  has_and_belongs_to_many :tags,
    :join_table => 'taggings'
    
  has_many :ratings, :as => :rateable

  validates_uniqueness_of :name
  validates_presence_of :name, :description, :resource_type, :stage, :orgs, :project_url, :license

  attr_accessor :new_related
  attr_accessor :new_similar
  attr_accessor :new_org
  
  SEARCH_WEIGHTS = {
    :name => 3,
    :byline => 2,
    :description => 1.2,
    :orgs => 0.8,
    :project_url => 0.8,
    :contact_name => 1,
    :contact_email => 1,
    :contact_other => 0.8,
    :mailing_list_url => 1,
    :documentation_url => 1,
    :resource_type_id => 0.5,
    :resource_type => 0.5,
    :stage => 0.6,
    :tags => 1.5,
    :license_id => 1,
    :license => 1,
    :os_support_id => 1,
    :os_support => 1,
    :size_scale => 1,
    :support => 0.5,
    :support_id  => 1,
    :commercial => 0.8,
    :commercial_url => 0.8,
    :cost => 0.8,
    :tech_publications => 0.9,
    :appl_publications => 0.9
  }
  
  def self.tagged_with( tag_string )
    # Find all resources tagged with tag_string
  end
  
  # resources are sortable by alpha name ...
  def <=>(other_resource)
    # all refs say $= depricated, but no explanation of what to do instead FIX
    name.downcase <=> other_resource.name.downcase
  end

  def complex_name
    self.id.to_s + ": " + self.name + " (" + self.contact_name + ")"
  end

  # Adds a resource name to the association "similar" list.
  # Doing it this way allows seamless updates when forms are submitted.
  # Could also fold this into #method_missing since other association handlers are there.
  def new_similar=( other_resource_name )
    other_resource = Resource.find_by_name( other_resource_name )
    self.similar_to( other_resource ) if other_resource
  end

  
  # Return a score that represents how close a resource fits a search term string
  def search_score( search_term )

    score = 0.0
    new_score = 0.0
    hits = 0
    # cycle over each resource field
    SEARCH_WEIGHTS.each do |field, value|

# counts total hits on each resource and decrement wieghts not just by
# filed hit number but but total hits within the resource.  - Credit Mitiguy.
      hits_new = field_hits( field, search_term )      
      hits += hits_new
      if hits_new > 0 and hits > 0
        new_score = value*(2 - (0.5)**(hits-1))
      else
        new_score = 0
      end
      score += new_score
    end
    score
  end

  def human_title( field )
    case field
    when :name
      'Project Name'
    when :byline
      'Byline (long)'
    when :project_url
      'Web address'
    when :description
      'Description'
    when :org
      'Organization(s)'
    when :contact
      'Contact Information'
    when :contact_name
      'Contact name'
    when :contact_email
      'Contact e-mail'
    when :contact_other
      'Contact other'
    when :contact_email_public
      'Public'
    when :contact_name_public
      'Public'
    when :contact_other_public
      'Public'
    when :mailing_list_url
      'Mailing List Url'
    when :documentation_url
      'Documentation Url'
    when :auth_key
      'Authoriztion Key'
    when :hits
      'Page Hits'
    when :outclicks
      'Entry Outclicks'
    when :year_started
      'Year Started'
    when :resource_type_id
      'Resource Type'
    when :resource_type
      'Resource Type'
    when :viewable
      'Viewable'
    when :stage_id
      'Development Stage'
    when :stage
      'Development Stage'
    when :tags
      'Keywords'
    when :userbase
      'Estimated Userbase'
    when :developers
      'Number of Current, <br>Active Developers'
    when :license_id
      'License'
    when :license
      'License'
    when :os_support_id
      'Operating System'
    when :os_support
      'Operating System'
    when :size_scale
      'Size Scale'
    when :support
      'Support'
    when :support_id 
      'Support'
    when :commercial
      'Commercial Information'
    when :commercial_url
      'Commercial URL'
    when :cost
      'Cost Information'
    when :publications
      'Total Number of Pulications'
    when :tech_publications
      'Techical Publication References'
    when :appl_publications
      'Application Publication References'
    when :superceded_id, :superceded_by
      'Entry is superceded by'
    when :update_flags
      'Update Flags'
    when :remove_flags
      'Remove Flags'
    when :similar
      'Resources with Similar Function'
    when :related
      'Related Resources'
    end
  end
    
  # Handle authorization for resources, i.e., whether passed in user has role for this Resource
  def accepts_role?( role, user )
    if role == 'owner'
      return user.email == self.contact_email  # TODO -- Remove this hardwiring in favor of full DB
    end
    super
  end
  
  def average_rating
    ratings = Rating.find_all_by_rateable_type_and_rateable_id( 'Resource', self.id )
    sum_of_ratings, count = 0.0, 0
    ratings.each do |rating|
      if rating.score
        sum_of_ratings = sum_of_ratings + rating.score
        count = count + 1
      end
    end
    count == 0 ? nil : sum_of_ratings / count
  end
  
  def current_rating( user )
    if user
      rating = Rating.find( :first, :conditions => [ "user_id = ? and rateable_type = 'Resource' and rateable_id = ?", user.id, self.id ] )
      rating ? rating.score : nil
    else
      nil
    end
  end
  
  # Only get ratings that have a comment
  def reviews
    unsorted_reviews = self.ratings.select {|rating| not rating.rating_comment.nil? }
    unsorted_reviews.sort {|a,b| b.updated_at <=> a.updated_at}
  end
  
  def review_from_user( user )
    if user.nil?
      nil
    else
      rating = Rating.find( :first, :conditions => [ "user_id = ? and rateable_type = 'Resource' and rateable_id = ?", user.id, self.id ] )
      rating.rating_comment if rating
    end
  end

# protected
  
  # SEARCH: Get the number of hits for a term on a given field of Resource
  # TODO -- Finish the logic here

  def field_hits( field, term )
    if self.send( field ) == nil : return 0 end
    
    target_string = ""

    case field
    when :stage, :license, :support, :resource_type, :os_support, :size_scale
      if self.send( field ).name != nil
        target_string = self.send( field ).name
      end

    when :name, :byline, :project_url, :description, :contact_other, :contact_email, :contact_name, :mailing_list_url, :documentation_url, :commercial_url, :cost, :tech_publications, :appl_publications
      target_string = self.send( field )
    when :orgs
      orgs.each do |org|
# FIX string joins endings on regex match
        target_string += org.name + " "
      end

    when :tags      
      self.tags.each do |t|
        target_string += t.name + " "
      end
    end        
    
    if not target_string : return 0 end
    r1 = Regexp.new(term, Regexp::IGNORECASE)
    # return the number of time the search term (now the r1 regex) matches in the taget string.
    logger.info target_string
    target_string.scan(r1).size 
  end
end

