module JamRuby

  # describes an audio track (like the drums, or guitar) that comprises a JamTrack
  class JamTrackTrack < ActiveRecord::Base
    include JamRuby::S3ManagerMixin
    include JamRuby::S3PublicManagerMixin

    # there should only be one Master per JamTrack, but there can be N Track per JamTrack
    TRACK_TYPE = %w{Track Master Click}

    @@log = Logging.logger[JamTrackTrack]

    before_destroy :delete_s3_files

    # Because JamTrackImporter imports audio files now, and because also the mere presence of this causes serious issues when updating the model (because reset of url_44 to something bogus), I've removed these
    #mount_uploader :url_48, JamTrackTrackUploader
    #mount_uploader :url_44, JamTrackTrackUploader

    attr_accessible :jam_track_id, :track_type, :instrument, :instrument_id, :position, :part, as: :admin
    attr_accessible :url_44, :url_48, :md5_44, :md5_48, :length_44, :length_48, :preview_start_time_raw, as: :admin

    attr_accessor :original_audio_s3_path, :skip_uploader, :preview_generate_error, :wav_file, :tmp_duration, :skip_inst_part_uniq

    before_destroy :delete_s3_files

    validates :position,        presence: true, numericality: {only_integer: true}, length: {in: 1..1000}
    validates :part,            length: {maximum: 35}
    validates :track_type,      inclusion: {in: TRACK_TYPE }
    validates :preview_start_time,  numericality: {only_integer: true}, length: {in: 1..1000},  :allow_nil => true
    validates_uniqueness_of     :part, scope: [:jam_track_id, :instrument_id], unless: :skip_inst_part_uniq
    # validates :jam_track,       presence: true

    belongs_to :instrument, class_name: "JamRuby::Instrument"
    belongs_to :jam_track, class_name: "JamRuby::JamTrack"

    has_many :recorded_jam_track_tracks, :class_name => "JamRuby::RecordedJamTrackTrack", :foreign_key => :jam_track_track_id, :dependent => :destroy
    has_one :jam_track_right, class_name: 'JamRuby::JamTrackRight', foreign_key: 'last_stem_id', inverse_of: :last_stem

    # create storage directory that will house this jam_track, as well as
    def store_dir
      "jam_track_tracks"
    end


    def licensor_suffix
      suffix = ''
      if jam_track.licensor
        raise "no licensor name" if jam_track.licensor.name.nil?
        suffix = " - #{jam_track.licensor.name}"
      end
      suffix
    end

    # create name of the file
    def filename(original_name)
      "#{store_dir}/#{jam_track.original_artist}/#{jam_track.name}#{licensor_suffix}/#{original_name}"
    end

    # create name of the preview file.
    # md5-'ed because we cache forever
    def preview_filename(md5, ext='ogg')
      original_name = "#{File.basename(self["url_44"], ".ogg")}-preview-#{md5}.#{ext}"
      "#{preview_directory}/#{original_name}"
    end

    def preview_directory
      "jam_track_previews/#{jam_track.original_artist}/#{jam_track.name}#{licensor_suffix}"
    end

    def has_preview?
      !self["preview_url"].nil? && !self['preview_mp3_url'].nil?
    end

    # generates a URL that points to a public version of the preview
    def preview_public_url(media_type='ogg')
      case media_type
        when 'ogg'
          url = self[:preview_url]
        when 'mp3'
          url = self[:preview_mp3_url]
        when 'aac'
          url = self[:preview_aac_url]
        else
          raise "unknown media_type #{media_type}"
      end
      if url
        s3_public_manager.public_url(url,{ :secure => true})
      else
        nil
      end
    end

    def display_name
      if track_type == 'Master'
        'Master Mix'
      else
        display_part = ''
        if part
          display_part = "-(#{part})"
        end
        "#{instrument.description}#{display_part}"
      end
    end

    def manually_uploaded_filename(mounted_as)
      if track_type == 'Master'
        filename("Master Mix-#{mounted_as == :url_48 ? '48000' : '44100'}.ogg")
      else
        filename("#{jam_track.name} Stem - #{instrument.description}-#{part}-#{mounted_as == :url_48 ? '48000' : '44100'}.ogg")
      end
    end

    def master?
      track_type == 'Master'
    end

    def url_by_sample_rate(sample_rate=48)
      field_name = (sample_rate==48) ? "url_48" : "url_44"
      self[field_name]
    end
    # creates a short-lived URL that has access to the object.
    # the idea is that this is used when a user who has the rights to this tries to download this JamTrack
    # we would verify their rights (can_download?), and generates a URL in response to the click so that they can download
    # but the url is short lived enough so that it wouldn't be easily shared
    def sign_url(expiration_time = 120, sample_rate=48)
      s3_manager.sign_url(url_by_sample_rate(sample_rate), {:expires => expiration_time, :response_content_type => 'audio/ogg', :secure => true})
    end

    def web_download_sign_url(expiration_time = 120, type='mp3', content_type = nil, response_content_disposition = nil)
      options = {:expires => expiration_time, :secure => true}
      options[:response_content_type] = content_type if content_type
      options[:response_content_disposition] = response_content_disposition if response_content_disposition

      url_field = self['url_' + type + '_48']
      url_field = self['url_48'] if type == 'ogg' # ogg has different column format in database


      s3_manager.sign_url(url_field, options)
    end
  
    def can_download?(user)
      # I think we have to make a special case for 'previews', but maybe that's just up to the controller to not check can_download?
      jam_track.owners.include?(user)
    end

    def move_up
      #normalize_position
      if self.position > 1
        # Switch with previous
        previous_track = self.jam_track.jam_track_tracks.where("position=?", self.position-1).first
        if previous_track
          JamTrack.transaction do
            previous_track.position,self.position = self.position,previous_track.position
            previous_track.save(validate:false)
            self.save(validate:false)
          end
        end
      end
    end

    def move_down
      count=normalize_position
      if self.position < count
        # Switch with next:
        next_track = self.jam_track.jam_track_tracks.where("position=?", self.position+1).first
        if next_track
          next_track.position,self.position = self.position,next_track.position
          next_track.save(validate:false)
          self.save(validate:false)
        end
      end
    end

    def delete_s3_files
      s3_manager.delete(self[:url_44]) if self[:url_44] && s3_manager.exists?(self[:url_44])
      s3_manager.delete(self[:url_48]) if self[:url_48] && s3_manager.exists?(self[:url_48])
      s3_public_manager.delete(self[:preview_url]) if self[:preview_url] && s3_public_manager.exists?(self[:preview_url])
      s3_public_manager.delete(self[:preview_mp3_url]) if self[:preview_mp3_url] && s3_public_manager.exists?(self[:preview_mp3_url])
    end



    def generate_preview

      begin
        Dir.mktmpdir do |tmp_dir|

          input = File.join(tmp_dir, 'in.ogg')

          raise 'no track' unless self["url_44"]

          s3_manager.download(self.url_by_sample_rate(44), input)

          process_preview(input, tmp_dir)
        end
      rescue Exception => e
        @@log.error("error in sox command #{e.to_s}")
        @preview_generate_error = e.to_s
      end

    end

    # input is the original ogg file for the track. tmp_dir is where this code can safely generate output stuff and have it cleaned up later
    def process_preview(input, tmp_dir)
      uuid = SecureRandom.uuid
      output = File.join(tmp_dir, "#{uuid}.ogg")
      output_mp3 = File.join(tmp_dir, "#{uuid}.mp3")
      output_aac = File.join(tmp_dir, "#{uuid}.aac")

      start = self.preview_start_time.to_f / 1000
      stop = start + 20

      command = "sox \"#{input}\" \"#{output}\" trim #{sprintf("%.3f", start)} =#{sprintf("%.3f", stop)}"

      @@log.debug("trimming using: " + command)

      sox_output = `#{command}`

      result_code = $?.to_i

      if result_code != 0
        @@log.debug("fail #{result_code}")
        @preview_generate_error = "unable to execute cut command #{sox_output}"
      else
        # now create mp3 off of ogg preview

        convert_mp3_cmd = "#{APP_CONFIG.ffmpeg_path} -i \"#{output}\" -ab 192k \"#{output_mp3}\""
        @@log.debug("converting to mp3 using: " + convert_mp3_cmd)

        convert_output = `#{convert_mp3_cmd}`

        result_code = $?.to_i

        if result_code != 0
          @@log.debug("fail #{result_code}")
          @preview_generate_error = "unable to execute mp3 convert command #{convert_output}"
        else

          convert_aac_cmd = "#{APP_CONFIG.ffmpeg_path} -i \"#{output}\" -c:a libfdk_aac -b:a 192k \"#{output_aac}\""
          @@log.debug("converting to aac using: " + convert_aac_cmd)

          convert_output = `#{convert_aac_cmd}`

          result_code = $?.to_i

          if result_code != 0
            @@log.debug("fail #{result_code}")
            @preview_generate_error = "unable to execute aac convert command #{convert_output}"
          else

            ogg_digest = ::Digest::MD5.file(output)
            mp3_digest = ::Digest::MD5.file(output_mp3)
            aac_digest = ::Digest::MD5.file(output_aac)
            self["preview_md5"] = ogg_md5 = ogg_digest.hexdigest
            self["preview_mp3_md5"] = mp3_md5 = mp3_digest.hexdigest
            self["preview_aac_md5"] = aac_md5 = mp3_digest.hexdigest

            @@log.debug("uploading ogg preview to #{self.preview_filename('ogg')}")
            s3_public_manager.upload(self.preview_filename(ogg_md5, 'ogg'), output, content_type: 'audio/ogg', content_md5: ogg_digest.base64digest)
            @@log.debug("uploading mp3 preview to #{self.preview_filename('mp3')}")
            s3_public_manager.upload(self.preview_filename(mp3_md5, 'mp3'), output_mp3, content_type: 'audio/mpeg', content_md5: mp3_digest.base64digest)
            @@log.debug("uploading aac preview to #{self.preview_filename('aac')}")
            s3_public_manager.upload(self.preview_filename(aac_md5, 'aac'), output_aac, content_type: 'audio/aac', content_md5: aac_digest.base64digest)

            self.skip_uploader = true

            original_ogg_preview_url = self["preview_url"]
            original_mp3_preview_url = self["preview_mp3_url"]
            original_aac_preview_url = self["preview_aac_url"]

            self["preview_url"] = self.preview_filename(ogg_md5, 'ogg')
            self["preview_length"] = File.new(output).size
            self["preview_mp3_url"] = self.preview_filename(mp3_md5, 'mp3')
            self["preview_mp3_length"] = File.new(output_mp3).size
            self["preview_aac_url"] = self.preview_filename(aac_md5, 'aac')
            self["preview_aac_length"] = File.new(output_aac).size
            self.save!

            # if all that worked, now delete old previews, if present
            begin
              s3_public_manager.delete(original_ogg_preview_url) if original_ogg_preview_url && original_ogg_preview_url != self["preview_url"]
              s3_public_manager.delete(original_mp3_preview_url) if original_mp3_preview_url && original_mp3_preview_url != track["preview_mp3_url"]
              s3_public_manager.delete(original_aac_preview_url) if original_aac_preview_url && original_aac_preview_url != track["preview_aac_url"]
            rescue
              puts "UNABLE TO CLEANUP OLD PREVIEW URL"
            end
          end

        end
      end
    end


    private
    def normalize_position
      parent = self.jam_track
      position = 0        
      if parent
        JamTrack.transaction do
          parent.jam_track_tracks.each do |jtt|
            position += 1
            if jtt.position != position
              jtt.position = position
              jtt.save(validate:false)                      
            end
          end
        end
      end
      position
    end # normalize_position

  end # class
end # module
