require 'launchy'
require 'erb'
require 'cairo'
require 'ruby-progressbar'

# An pixel-by-pixel comparison of sample images for visual regression testing
class Sanity

  @@EXPECTED_DIR = "#{File.expand_path(File.dirname(__FILE__))}/expected/"
  @@OUTPUT_DIR   = "#{File.expand_path(File.dirname(__FILE__))}/../../samples/_output/"
  @@DIFF_DIR     = "#{File.expand_path(File.dirname(__FILE__))}/_diffs/"
  @@SANITY_ERB   = "#{File.expand_path(File.dirname(__FILE__))}/sanity.html.erb"
  @@SANITY_HTML  = "#{File.expand_path(File.dirname(__FILE__))}/sanity.html"

  def images
    images   = []
    exp_pngs = Dir[@@EXPECTED_DIR + "/**/*.png"]
    bar      = ProgressBar.create(title: 'Diffing images', total: exp_pngs.size)
    exp_pngs.each do |exp_png|
      row = []
      actual_png = @@OUTPUT_DIR + File.basename(exp_png)
      row << "file:///" + exp_png
      row << "file:///" + actual_png #actual
      row << "file:///" + diff_image(exp_png, actual_png)
      images << row
      bar.increment
    end
    bar.finish
    return images
  end

  def run
    puts "Building sanity test..."
    sanity_template = File.read(@@SANITY_ERB)
    process_erb(sanity_template)
    Launchy.open("file:///" + @@SANITY_HTML)
    puts "Done."
  end

  private

  def diff_image(exp_file, actual_file)
    exp         = Cairo::ImageSurface.from_png(exp_file)
    exp_data    = exp.data.bytes
    actual_data = Cairo::ImageSurface.from_png(actual_file).data.bytes
    new_data    = Array.new(exp_data.size, 255)
    unless exp_data == actual_data # this seems to be the fastest initial comparison
      (0..exp_data.size).each do |i|
        empty = (i % 4 == 3) ? 0 : 255 # alpha channel is empty, others are just black
        new_data[i] = (exp_data[i] == actual_data[i]) ? empty : 128
      end
    end
    out = Cairo::ImageSurface.new(new_data.pack('c*'), exp.format, exp.width, exp.height, exp.stride)
    out_file = @@DIFF_DIR + exp_file[exp_file.rindex("/")..-1]
    out.write_to_png(out_file)
    out_file
  end

  def diff_pixel(exp, actual)
    (exp == actual) ? [0, 0, 0, 0] : [255, 0, 0, 255]
  end

  def process_erb(sanity_template)
    renderer = ERB.new(sanity_template)
    File.open(@@SANITY_HTML, 'w+') do |html|
      html.write(renderer.result(binding))
    end
  end

end