namespace :avalon do

    namespace :ualbertalib do

      desc "Given a set of MediaObject IDs, in a file, one-per-line, update visibility to 'restricted'"
      task :visibility_restricted => :environment do

        options = {:commit => false}
        option_parser = OptionParser.new do |opts|
          opts.banner = "Usage: rake avalon:ualbertalib:visibility_restricted [options]"
          opts.on("-c", "--commit", "Commit change; override default dry-run") { 
            options[:commit] = true 
          }
          opts.on("-m ARG", "--media_objects", "File containing list of MediaObject IDs, one per line") { 
            |filename| options[:filename] = filename 
          }
          opts.on("-h", "--help") do 
             puts opts
             exit 
          end
        end

        # return `ARGV` with the intended arguments
        args = option_parser.order!(ARGV) {}
        option_parser.parse!(args)

        if options[:commit] 
          puts "Committing changes"
        else
          puts "Executing a 'dry-run' (no changes commited); use '-c' to save the change."
        end

        if options[:filename].nil?
          puts "Manditory argument: -m filename"
          puts option_parser.help
          exit 1
        elsif !File.exist?(options[:filename])
          puts "Filename not found: #{options[:filename]}"
          exit 1
        end

        mediaobject_ids = File.read(options[:filename]).split

        count = 0
        count_success = 0
        mediaobject_ids.each do |id|
          begin  
            count += 1
            obj = MediaObject.find(id)  
            old_visibility = obj.visibility
            obj.visibility = 'restricted' 
            if obj.valid?
              obj.save! if options[:commit] # default to dry-run unless commit specified 
              puts "[success] old:[#{old_visibility}] new:[#{obj.visibility}] id:[#{obj.id}]"
              count_success += 1
            else
              puts "[fail] old:[#{old_visibility}] new:[#{obj.visibility}] id:[#{obj.id}]"
              raise StandardError, "validation failure on id: #{id}, visibility change not saved"
            end
          rescue StandardError => e
            puts "ERROR: id:#{id}, class:#{e.class.name}, message:#{e.message}"
          end
        end
        puts "Number processed: [#{count}]; success: [#{count_success}]"
        if count == 0
          puts "You must specify an set of MediaObject IDs."
          puts option_parser.help
          exit 1
        end
      end

      desc "Return a list of collections: id's and labels"
      task :list_collections => :environment do

        Admin::Collection.find_each do |collection|
          puts "[#{collection.id}] #{collection.name}" 
        end

      end

      desc "Given a single collection ID, return a list of collection members"
      task :list_collection_members => :environment do

        options = {}
        option_parser = OptionParser.new do |opts|
          opts.on("-c=ARG", "--collection_id=ARG", "collection id") { 
            |id| options[:id] = id 
          }
        end
        
        args = option_parser.order!(ARGV) {}
        option_parser.parse!(args)

        if options[:id].nil?
          puts "Manditory argument: -c id"
          puts option_parser.help
          exit 1
        end
        
        obj = Admin::Collection.find(options[:id])
        obj.media_objects.each do |m| 
            puts m.id
        end
      rescue ActiveFedora::ModelMismatch
        puts "ERROR: not a Collection obj: #{options[:id]}" 
      rescue ActiveFedora::ObjectNotFoundError
        puts "ERROR: Object not found: #{options[:id]}" 
      end
 
      desc "Given a set of IDs, in a file, one-per-line, export contents"
      task :export_contents => :environment do

        options = {}
        option_parser = OptionParser.new do |opts|
          opts.on("-m=ARG", "--objects=ARG", "File containing list of object IDs, one per line") { 
            |filename| options[:filename] = filename 
          }
          opts.on("-d=ARG", "--derivatives=ARG", "Base directory of the derivatives") { 
            |stream_base_dir| options[:stream_base_dir] = stream_base_dir 
          }
          opts.on("-b=ARG", "--dest_base=ARG", "Output directory") { 
            |dest_base_dir| options[:dest_base_dir] = dest_base_dir 
          }
          opts.on("-s=ARG", "--delay=ARG", "Delay between exports") { 
            |delay| options[:delay] = delay ? delay.to_i : 0
          }
          opts.on("-h", "--help") do 
             puts opts
             exit 
          end
        end
        
        args = option_parser.order!(ARGV) {}
        option_parser.parse!(args)

        if options[:filename].nil?
          puts "Manditory argument: -m filename"
          puts option_parser.help
          exit 1
        elsif !File.exist?(options[:filename])
          puts "Filename not found: #{options[:filename]}"
          exit 1
        end

        ids = File.read(options[:filename]).split

        count_success = 0
        ids.each do |id|
          begin  
            obj = ActiveFedora::Base.find(id)
            if (obj.is_a?(MediaObject))
              ualberta_export_mediaObject(obj, options)
            elsif (obj.is_a?(Admin::Collection))
              ualberta_export_collectionObject(obj, options)
            end
            count_success += 1
            sleep(options[:delay]) if (options[:delay])
          rescue StandardError => e
            puts "ERROR: id:#{id}, class:#{e.class.name}, message:#{e.message}"
            puts e.backtrace
          end
        end
        puts "Number processed: [#{ids.count}]; success: [#{count_success}]"
        if ids.count == 0
          puts "You must specify an set of MediaObject IDs."
          puts option_parser.help
          exit 1
        end
      end

      def ualberta_export_collectionObject( obj, options )
        base_dir = File.join(options[:dest_base_dir], obj.id)
        FileUtils.mkdir_p(base_dir)
        File.open("#{base_dir}/collection_object.json", 'w') { |file| file.write(obj.as_json.to_json(except: :captions)) }
      end

      def ualberta_export_mediaObject( obj, options )
        base_dir = File.join(options[:dest_base_dir], obj.collection_id, "MediaObjects", obj.id)
        FileUtils.mkdir_p(base_dir)
        File.open("#{base_dir}/media_object.json", 'w') { |file| file.write(obj.as_json.to_json(except: :captions)) }
        File.open("#{base_dir}/mods.xml", 'w:BINARY') { |file| file.write(obj.descMetadata.content) }
        obj.ordered_master_files.to_a.collect.each do |mf|
          ualberta_export_masterFile(mf, base_dir, options)
        end
      end

      def ualberta_export_masterFile ( mf, base_dir, options )
        mf_base_dir = File.join(base_dir, "media_files", mf.id)
        FileUtils.mkdir_p(mf_base_dir)
        File.open("#{mf_base_dir}/media_file.json", 'w') { |file| file.write(mf.to_json) }
        File.open("#{mf_base_dir}/structural_metadata.xml", 'w') { |file| file.write(mf.structuralMetadata.content) }
        original_base_dir = File.join(mf_base_dir, "original_media_file")
        if File.file?(mf.file_location)
          FileUtils.mkdir_p(original_base_dir)
          FileUtils.cp(mf.file_location, "#{original_base_dir}/")
        else
          # mf.file_location may be outdated (i.e., moved outside Avalon)
          # search for file in other directories
          # log: if media doesn't exist or has been moved
          glob = Dir.glob("#{ENV['SETTINGS__DROPBOX__PATH']}/**/#{File.basename(mf.file_location)}")
          puts "ERROR: [#{mf.id}] media file not found: [#{mf.file_location}] for Object [#{mf.media_object_id}]" if glob.empty?
          # assumption: every media file should have a unique name otherwise this fails and fails by reporting 2+ items
          # log: if multiple files of the same name found but don't change execution path as there could be a legitemate reason
          puts "ERROR: [#{mf.id}] stored media file path outdated: multiple media files found with name [#{File.basename(mf.file_location)}]" if glob.count > 1
          glob.each do |file|
            FileUtils.mkdir_p(original_base_dir)
            FileUtils.cp(file, "#{original_base_dir}/")
            puts "INFO: [#{mf.id}] stored media file path outdated [#{mf.file_location}] -- using alternative [#{file}]"
          end
        end
        mf.derivatives.each do |derivative|
          derivative_base_dir = File.join(mf_base_dir,"derivatives","#{derivative.quality}_#{derivative.id}")
          FileUtils.mkdir_p(derivative_base_dir)
          File.open("#{derivative_base_dir}/derivative.json", 'w') { |file| file.write(derivative.to_json) }
          # locate the derivative file and export
          # derivative files are stored on the streaming server; UAL mounts this filesystem on the app server as well
          # parse streaming server path to translate into a valid app server path
          # start with the derivativeFile path and parse the last two subdirectories along with the filename 
          if (File.exists?(tmp_path = File.join(options[:stream_base_dir], derivative.derivativeFile.split(File::SEPARATOR)[4..-1])))
            FileUtils.cp(tmp_path, derivative_base_dir)
          end
        end
        if mf.captions.has_content?
          caption_base_dir = File.join(mf_base_dir, "captions")
          FileUtils.mkdir_p(caption_base_dir)
          File.open("#{caption_base_dir}/#{mf.captions.original_name}", 'w:BINARY') { |file| file.write(mf.captions.content) }
        end
        if mf.poster.has_content?
          poster_base_dir = File.join(mf_base_dir,"poster")
          FileUtils.mkdir_p(poster_base_dir)
          File.open("#{poster_base_dir}/#{mf.poster.original_name}", 'w:BINARY') { |file| file.write(mf.poster.content) }
        end
        if mf.thumbnail.has_content?
          thumbnail_base_dir = File.join(mf_base_dir,"thumbnail")
          FileUtils.mkdir_p(thumbnail_base_dir)
          File.open("#{thumbnail_base_dir}/#{mf.thumbnail.original_name}", 'w:BINARY') { |file| file.write(mf.thumbnail.content) }
        end
      end

    end
  end
