require "zip"
require "archive"

# A unified class to handle all supported archive formats. It uses the ::Zip
#   module in crystal standard library if the target file is a zip archive.
#   Otherwise it uses `archive.cr`.
class ArchiveFile
  def initialize(@filename : String)
    if [".cbz", ".zip"].includes? File.extname filename
      @archive_file = Zip::File.new filename
    else
      @archive_file = Archive::File.new filename
    end
  end

  def self.open(filename : String, &)
    s = self.new filename
    yield s
    s.close
  end

  def close
    if @archive_file.is_a? Zip::File
      @archive_file.as(Zip::File).close
    end
  end

  # Lists all file entries
  def entries
    ary = [] of Zip::File::Entry | Archive::Entry
    @archive_file.entries.map do |e|
      if (e.is_a? Zip::File::Entry && e.file?) ||
         (e.is_a? Archive::Entry && e.info.file?)
        ary.push e
      end
    end
    ary
  end

  def read_entry(e : Zip::File::Entry | Archive::Entry) : Bytes?
    if e.is_a? Zip::File::Entry
      data = nil
      e.open do |io|
        slice = Bytes.new e.uncompressed_size
        bytes_read = io.read_fully? slice
        data = slice if bytes_read
      end
      data
    else
      e.read
    end
  end

  def check
    if @archive_file.is_a? Archive::File
      @archive_file.as(Archive::File).check
    end
  end
end
