module JamRuby
  class Track < ActiveRecord::Base

    self.table_name = "tracks"

    self.primary_key = 'id'

    default_scope { order('created_at ASC') }

    attr_accessor :musician, :instrument_ids

    SOUND = %w(mono stereo)
  
    belongs_to :connection, :class_name => "JamRuby::Connection", :inverse_of => :tracks, :foreign_key => 'connection_id'
    belongs_to :instrument, :class_name => "JamRuby::Instrument", :inverse_of => :tracks

    validates :sound, :inclusion => {:in => SOUND}
    validates :connection, presence: true
    validates :client_track_id, presence: true
    #validates :client_resource_id, presence: true

    def user
      self.connection.user
    end

    def musician
      @musician
    end

    def musician=(user)
      @musician = user
    end

    def self.index(current_user, music_session_id)
      query = Track
        .joins(
          %Q{
            INNER JOIN
              connections
            ON
              connections.music_session_id = '#{music_session_id}'
            AND
              connections.id = connection_id
            AND
              connections.user_id = '#{current_user.id}'
            INNER JOIN
              music_sessions
            ON
              music_sessions.id = '#{music_session_id}'
            AND
              music_sessions.user_id = '#{current_user.id}'
          }
        )

      return query
    end

    def self.diff_track(track_class, existing_tracks, new_tracks, &blk)
      result = []
      if new_tracks.length == 0
        existing_tracks.delete_all
      else

        # we will prune from this as we find matching tracks
        to_delete = Set.new(existing_tracks)
        to_add = Array.new(new_tracks)

        existing_tracks.each do |existing_track|
          new_tracks.each do |new_track|

            if new_track[:id] == existing_track.id || new_track[:client_track_id] == existing_track.client_track_id
              to_delete.delete(existing_track)
              to_add.delete(new_track)

              blk.call(existing_track, new_track)

              result.push(existing_track)

              if existing_track.save
                next
              else
                result = existing_track
                raise ActiveRecord::Rollback
              end
            end
          end
        end


        to_add.each do |new_track|
          existing_track = track_class.new

          blk.call(existing_track, new_track)

          if existing_track.save
            result.push(existing_track)
          else
            result = existing_track
            raise ActiveRecord::Rollback
          end
        end

        to_delete.each do |delete_me|
          delete_me.delete
        end
      end
      result
    end

    # this is a bit different from a normal track synchronization in that the client just sends up all tracks,
    # ... some may already exist
    def self.sync(clientId, tracks, backing_tracks = [], metronome_open = false)
      result = {}

      backing_tracks = [] unless backing_tracks
      tracks = [] unless tracks

      Track.transaction do
        connection = Connection.find_by_client_id!(clientId)

        # synchronize metronome_open on connection
        if connection.metronome_open != metronome_open
          Connection.where(:id => connection.id).update_all(:metronome_open => metronome_open)
        end

        # each time tracks are synced we have to update the entry in music_sessions_user_history
        msh = MusicSessionUserHistory.find_by_client_id!(clientId)
        instruments = []

        tracks.each do |track|
          instruments << track[:instrument_id]
        end

        result[:tracks] = diff_track(Track, connection.tracks, tracks) do |track_record, track_info|
          track_record.connection = connection
          track_record.client_track_id = track_info[:client_track_id]
          track_record.client_resource_id = track_info[:client_resource_id]
          track_record.instrument_id = track_info[:instrument_id]
          track_record.sound = track_info[:sound]
        end

        result[:backing_tracks] = diff_track(BackingTrack, connection.backing_tracks, backing_tracks)  do |track_record, track_info|
          track_record.connection = connection
          track_record.client_track_id = track_info[:client_track_id]
          track_record.client_resource_id = track_info[:client_resource_id]
          track_record.filename = track_info[:filename]
        end

        msh.instruments = instruments.join("|")
        if !msh.save
          raise ActiveRecord::Rollback
        end
      end

      result
    end

    def self.save(id, connection_id, instrument_id, sound, client_track_id, client_resource_id)
      if id.nil?
        track = Track.new
        track.connection_id = connection_id
      else
        track = Track.find(id)
      end

      unless instrument_id.nil?
        track.instrument_id = instrument_id
      end

      unless sound.nil?
        track.sound = sound
      end

      unless client_track_id.nil?
        track.client_track_id = client_track_id
      end

      unless client_resource_id.nil?
        track.client_resource_id = resource_id
      end

      track.updated_at = Time.now
      track.save
      return track
    end
  end
end