module JamRuby
  class MusicSessionUserHistory < ActiveRecord::Base

    self.table_name = "music_sessions_user_history"

    self.primary_key = 'id'

    default_scope { order('user_id ASC') }

    attr_accessible :max_concurrent_connections, :session_removed_at, :rating
    validates_inclusion_of :rating, :in => -1..1, :allow_nil => true

    belongs_to :user, :class_name => "JamRuby::User",  :foreign_key => :user_id,  :inverse_of => :music_session_user_histories
    belongs_to :music_session, :class_name => "MusicSession", :foreign_key => :music_session_id

    def self.latest_history(client_id)
      self.where(:client_id => client_id)
        .order('created_at DESC')
        .limit(1)
        .includes(:user)
        .first
    end

    def name
      user.name
    end

    def range
      # to_time to not use ActiveSupport::TimeWithZone
      session_removed_at_time = session_removed_at.to_time if session_removed_at
      Range.new(created_at.to_time, session_removed_at_time || Time.now.to_time)
    end

    def music_session
      @msh ||= JamRuby::MusicSession.find_by_music_session_id(self.music_session_id)
    end

    def perf_data
      @perfdata ||= JamRuby::MusicSessionPerfData.find_by_client_id(self.client_id)
    end

    def self.save(music_session_id, user_id, client_id, tracks)
      return true if 0 < self.where(:music_session_id => music_session_id,
                                    :user_id => user_id,
                                    :client_id => client_id).where('session_removed_at is NULL').count
      session_user_history = MusicSessionUserHistory.new
      session_user_history.music_session_id = music_session_id
      session_user_history.user_id = user_id
      session_user_history.client_id = client_id
      session_user_history.instruments = tracks.map {|t| t[:instrument_id]}.join("|") if tracks
      session_user_history.save
    end
    
    def user_email
      self.user ? self.user.email : '<user deleted>'
    end

    def duration_minutes
      end_time = self.session_removed_at || Time.now
      (end_time - self.created_at) / 60.0
    end

    def self.join_music_session(user_id, session_id, client_id)
      hist = self
        .where(:user_id => user_id)
        .where(:music_session_id => session_id)
        .where(:client_id => client_id)
        .where('session_removed_at IS NULL')
        .limit(1)
        .first
      hist.start_history if hist
    end

    def start_history
      self.session_removed_at = nil
      self.update_attributes(:session_removed_at => self.session_removed_at, :max_concurrent_connections => determine_max_concurrent)
    end

    def self.removed_music_session(user_id, session_id)
      hist = self
        .where(:user_id => user_id)
        .where(:music_session_id => session_id)
        .order('created_at DESC')
        .limit(1)
        .first
      hist.end_history if hist
    end

    def end_history
      if self.session_removed_at.nil?
        self.session_removed_at = Time.now
        self.max_concurrent_connections = determine_max_concurrent
        self.update_attributes(:session_removed_at => self.session_removed_at, :max_concurrent_connections => self.max_concurrent_connections)
        self.update_remaining_play_time
      end
    end

    # update the users monthly play time
    def update_remaining_play_time
      now = Time.now
      current_month = User.current_month
      remaining_time_so_far = self.user.used_month_play_time.to_i

      if current_month != self.user.used_current_month
        # reset because it's a new month
        remaining_time_so_far = 0
      end

      if self.max_concurrent_connections > 1
        remaining_time_so_far = remaining_time_so_far + duration_minutes * 60
      end

      self.user.update_attributes(:used_current_month => current_month, :used_month_play_time => remaining_time_so_far)
    end

    # figures out what the peak amount of other clients the user saw while playing at any given time.
    # we use the database to get all other connections that occurred while their this user was connected,
    # and then sort through them using custom logic to increase/decrease the count as people come and go
    def determine_max_concurrent
        overlapping_connections = MusicSessionUserHistory.where("music_session_id = ? AND
                                    ((created_at >= ? AND (session_removed_at is NULL OR session_removed_at <= ?)) OR
                                    (created_at <= ? AND (session_removed_at is NULL OR session_removed_at >= ?)))",
                                                              self.music_session_id, self.created_at, self.session_removed_at, self.created_at, self.created_at).select('id, created_at, session_removed_at').order(:created_at)
      in_out_times = []
      overlapping_connections.each do |connection|
        in_out_times.push([connection.created_at.nil? ? Time.at(0) : connection.created_at, true])
        in_out_times.push([connection.session_removed_at.nil? ? Time.new(3000) : connection.session_removed_at, false]) #helpful to get rid of nulls for sorting
      end

      count_concurrent(in_out_times)
    end

    def count_concurrent(in_out_times)
      in_out_times.sort! { |a,b| a[0] <=> b[0] }

      max_concurrent = 0
      concurrent = 0

      in_out_times.each do |hit|
        if hit[1]
          concurrent = concurrent + 1
        else
          concurrent = concurrent - 1
        end

        if concurrent > max_concurrent
          max_concurrent = concurrent
        end
      end
      max_concurrent
    end

    def perf_uri
      self.perf_data.try(:uri)
    end

    def add_rating(rval, comment='', backend_details=nil)
      rval = rval.to_i
      self.rating = rval if 0 != rval 
      self.rating_comment = comment
      if backend_details
        self.backend_details = backend_details.to_json
      end

    end

    def should_rate_session?
      (APP_CONFIG.rating_dialog_min_num <= music_session.unique_users.length &&
          APP_CONFIG.rating_dialog_min_time < (Time.now - music_session.created_at).seconds)
    end

    def good_rating?
      0 < self.rating.to_i
    end

  end
end
