desc 'png-optimize PATH', 'Optimize .png files'
long_desc <<-LONGDESC
Example: `geordi png-optimize some/directory`

- Removes color profiles: cHRM, sRGB, gAMA, ICC, etc.
- Eliminates unused colors and reduces bit-depth (if possible)
- May reduce PNG file size lossless
LONGDESC

def png_optimize(path)
  require 'fileutils'

  Interaction.announce 'Optimizing .png files'

  if `which pngcrush`.strip.empty?
    Interaction.fail 'Please install pngcrush first (sudo apt-get install pngcrush)'
  end

  po = PngOptimizer.new
  if File.directory?(path)
    po.batch_optimize_inplace(path)
  elsif File.file?(path)
    po.optimize_inplace(path)
  else
    Interaction.fail 'Neither directory nor file: ' + path
  end

  Interaction.success 'PNG optimization completed.'
end

class PngOptimizer

  def ends_with?(string, suffix)
    string[-suffix.length, suffix.length] == suffix
  end

  def optimization_default_args
    args = ''
    args << '-rem alla ' # remove everything except transparency
    args << '-rem text ' # remove text chunks
    args << '-reduce ' # eliminate unused colors and reduce bit-depth (if possible)
    args
  end

  def optimize_file(input_file, output_file)
    system "pngcrush #{optimization_default_args} '#{input_file}' '#{output_file}'"
  end

  def unused_tempfile_path(original)
    dirname = File.dirname(original)
    basename = File.basename(original)
    count = 0

    loop do
      tmp_name = "#{dirname}/#{basename}_temp_#{count += 1}.png"
      break tmp_name unless File.exist?(tmp_name)
    end
  end

  def optimize_inplace(input_file)
    temp_file = unused_tempfile_path(input_file)
    result = optimize_file(input_file, temp_file)
    if result
      FileUtils.rm(input_file)
      FileUtils.mv(temp_file.to_s, input_file.to_s)
    else
      Interaction.fail 'Error:' + $?
    end
  end

  def batch_optimize_inplace(path)
    # Dir[".png"] works case sensitive, so to catch all funky .png extensions we have to go the following way:
    png_relative_paths = []
    Dir["#{path}/*.*"].each do |file_name|
      png_relative_paths << file_name if ends_with?(File.basename(file_name.downcase), '.png')
    end
    png_relative_paths.each do |png_relative_path|
      optimize_inplace(png_relative_path)
    end
  end

end
