# == Synopsis
# Transfer media meta data from DvdProfiler to the format that XBMC needs it (.tbn and .nfo files)
#
# usage:
#  app = DvdProfiler2Xbmc.new
#  app.execute
#  app.report.each {|line| puts line}
class DvdProfiler2Xbmc
  include Singleton

  protected

  # == Synopsis
  # protected initializer because it is a Singleton class
  def initialize
    @media_files = nil
    @duplicate_titles = []
  end

  public

  @interrupted = false
  @interrupt_message = "control-C detected, finishing current task"
  @multiple_profiles = []

  class << self
    # == Synopsis
    # When ^C is pressed, this message is sent to stdout
    attr_accessor :interrupt_message

    # == Synopsis
    # An Array of Strings that the external processing my write to to
    # indicate that a given title has multiple ISBNs.
    # HACK, this is a hack because I didn't see a way to cleanly pass
    # the data up from the processing.
    attr_accessor :multiple_profiles
  end

  # == Synopsis
  # A trap("INT") in the Runner calls this to indicate that a ^C has been detected.
  # Note, once set, it is never cleared
  def self.interrupt
    AppConfig[:logger].error { @interrupt_message }
    @interrupted = true
  end

  # == Synopsis
  # Long loops should poll this method to see if they should abort
  # Returns:: true if the application has trapped an "INT", false otherwise
  def self.interrupted?
    @interrupted
  end

  # == Synopsis
  # the application's main execution loop that processes all of the media
  def execute
    AppConfig[:logger].info { "Media Directories:\n  #{AppConfig[:directories].join("\n  ")}" }

    DvdprofilerProfile.collection_filespec = AppConfig[:collection_filespec]

    @media_files = MediaFiles.new(AppConfig[:directories])
    if AppConfig[:do_update]
      @media_files.titles.each do |title, medias|
        break if DvdProfiler2Xbmc.interrupted?
        medias.each do |media|
          # note, NfoController update must be first as it sets isbn and imdb_id for media
          NfoController.update(media)
          ThumbnailController.update(media)
          FanartController.update(media)
        end
      end
    end
    @duplicate_titles = @media_files.duplicate_titles

    # set file and directory permissions
    AppConfig[:directories].each { |dir| set_permissions(dir) }
  end

  # == Synopsis
  # generate the report.
  # Note, must be ran after execute()
  # returns an array of lines
  def report
    buf = []
    unless DvdProfiler2Xbmc.interrupted?
      unless @media_files.nil?
        buf += gen_report('duplicates', 'Duplicates')
        buf += gen_report('missing_isbns', 'Missing ISBNs')
        buf += gen_report('missing_imdb_ids', 'Missing IMDB IDs')
        buf += gen_report('missing_thumbnails', 'Missing Thumbnails')
        buf += gen_report('multiple_profiles', 'Multiple Profiles Found For Single Titles')
      end
    end
    buf
  end

  # == Synopsis
  # utility method that saves the given data to the filespec safely by:
  # 1) writes the data to a new file,
  # 2) deletes any previous backup file,
  # 3) renames the old file to a backup,
  # 4) renames the new file to the original filename.
  def self.save_to_file(filespec, data)
    new_filespec = filespec + AppConfig[:extensions][:new]
    File.open(new_filespec, "w") do |file|
      file.puts(data)
    end
    backup_filespec = filespec + AppConfig[:extensions][:backup]
    File.delete(backup_filespec) if File.exist?(backup_filespec)
    File.rename(filespec, backup_filespec) if File.exist?(filespec)
    File.rename(new_filespec, filespec)
    File.delete(new_filespec) if File.exist?(new_filespec)
  end

  # == Synopsis
  # options hash may have the following:
  #  :extension  - an extension to append to the generated filespec
  #  :year       - the production year
  #  :resolution - the video resolution
  def self.generate_filespec(media_pathspec, type, options={})
    filespec = nil
    begin
      basespec = File.basename(media_pathspec, ".*").gsub(AppConfig[:part_regex], '')
      dirname = File.dirname(media_pathspec)
      part = :no_part
      if media_pathspec =~ AppConfig[:part_regex]
        part = :part
      end

      extension = AppConfig[:extensions][type]
      year = options[:year] || ''
      resolution = options[:resolution] || ''

      if AppConfig[:naming][type].nil?
        filespec = File.join(dirname, basespec)
        unless extension.blank?
          filespec += extension
        end
      else
        format_str = AppConfig[:naming][type][part]
        unless format_str.blank?
          unless extension.blank?
            filespec = File.join(dirname, format_str.gsub(/%t/, basespec).gsub(/%e/, extension).gsub(/%r/, resolution).gsub(/%y/, year))
          end
        end
      end
      unless options[:extension].blank?
        filespec += options[:extension]
      end
    rescue Exception => e
      AppConfig[:logger].error { "Error in generate_filespec(#{media_pathspec}, #{type}, #{options.inspect}) - #{e.to_s}" }
    end
    filespec
  end

  protected

  # == Synopsis
  def gen_report(name, heading='')
    buf = []
    begin
      lines = send("#{name}_report")
      unless lines.empty?
        buf << ''
        buf << heading
        buf += lines
      end
    rescue Exception => e
      AppConfig[:logger].error { "Error generating #{name} report - #{e.to_s}" }
    end
    buf
  end

  # == Synopsis
  # set the directory and file permissions for all files and directories under
  # the given directory
  def set_permissions(dir)
    Dir.glob(File.join(dir, '**/*')).each do |f|
      begin
        if File.directory?(f)
          File.chmod(AppConfig[:dir_permissions].to_i(8), f) unless AppConfig[:dir_permissions].nil?
        else
          File.chmod(AppConfig[:file_permissions].to_i(8), f) unless AppConfig[:file_permissions].nil?
        end
      rescue Exception => e
        AppConfig[:logger].error {e.to_s}
      end
    end
  end

  # == Synopsis
  # duplicate media file report
  def duplicates_report
    buf = []
    unless @duplicate_titles.empty?
      @duplicate_titles.each do |title, medias|
        if medias.length > 1
          buf << title
          medias.each {|media| buf << "  #{media.media_path}"}
        end
      end
    end
    buf
  end

  # == Synopsis
  # unable to find ISBN for these titles report
  def missing_isbns_report
    buf = []
    @media_files.titles.each do |title, medias|
      if medias.nil?
        buf << "No media for #{title}"
      else
        if medias[0].isbn.nil?
          paths = []
          medias.each do |media|
            unless File.exist? media.path_to(:no_isbn)
              paths << "  #{media.media_path}"
            end
          end
          unless paths.empty?
            buf += paths
          end
        end
      end
    end
    buf
  end

  # == Synopsis
  def missing_imdb_ids_report
    buf = []
    @media_files.titles.each do |title, medias|
      if medias.nil?
        buf << "No media for #{title}"
      else
        medias.each do |media|
          if media.imdb_id.blank?
            buf << "  #{title}"
            break
          end
        end
      end
    end
    buf
  end

  # == Synopsis
  def missing_thumbnails_report
    buf = []
    @media_files.titles.each do |title, medias|
      if medias.nil?
        buf << "No media for #{title}"
      else
        medias.each do |media|
          thumbnail = media.path_to(:thumbnail)
          unless File.exist?(thumbnail)
            buf << "  #{thumbnail} #{media.imdb_id.nil? ? '' : media.imdb_id}"
          end
        end
      end
    end
    buf
  end

  # == Synopsis
  def multiple_profiles_report
    @multiple_profiles
  end

end