#! /usr/bin/env ruby

require 'open3'
require 'colorize'

class Prefect
  # This class tests per file.
  IN_PREFIX = '!>> ?'
  OUT_PREFIX = '!<< ?'

  PAT = /.*".*---\n(.*)\n#{IN_PREFIX}([^"]*)\n#{OUT_PREFIX}([^"]*)"/m
  # This means that a testcase description consists of three parts:
  # (1) the text description part that tells what this testcase is about
  # (2) the input part, led by !>>\space
  # (3) the expected output part, led by !<<\space

  def initialize(fp, **ctx)
    @ctx = ctx
    @fp = fp.chop.chop

    open(fp) {|f|
      @content = f.read
    }

    extract_info
  end

  def extract_info
    matches = PAT.match(@content)
    unless matches.nil?
      @description, @input, @expected_output = matches.captures
      @description.strip!
    end
  end

  def execute_ok(idx)
    pretty_input = if @input == ''
                     'None'.light_cyan
                   else
                     @input.white
                   end
    pretty_output = if @expected_output == ''
                      'None'.light_cyan
                    else
                      @expected_output.white
                    end

    puts "Testcase #{('#' + idx.to_s).blue}: #{@description}
---
Input: #{pretty_input}
Output: #{pretty_output}"

    out, err, status = Open3.capture3(@ctx[:weave_path],
                                    "#{@fp}.t",
                                    '-o',
                                    "temp/#{@fp}.w")

    if status.exitstatus != 0
      [false, out]
      return
    end

    if ARGV[0] == '-t'
      puts "Tracing #{@fp}.w =>"
      output, _ = Open3.capture2(
          @ctx[:tvm_path], "temp/#{@fp}.w", '-t', :stdin_data=>@input
      )
    else
      output, err, status = Open3.capture3(
          @ctx[:tvm_path], "temp/#{@fp}.w", :stdin_data=>@input
      )
    end

    if status.exitstatus != 0
      [false, err]
      return
    end

    if output == @expected_output
      [true, output]
    else
      [false, output]
    end
  end
end

if __FILE__ == $0
  require 'fileutils'
  FileUtils.remove_entry_secure 'temp'
  FileUtils.mkdir 'temp'

  SUFFIX = 't'
  WEAVE_PATH = '../build/src/compiler/weave'
  TVM_PATH = '../build/src/vm/tvm'
  make_prefect = lambda {|fp| Prefect.new(
    fp,
    :weave_path=>WEAVE_PATH,
    :tvm_path=>TVM_PATH
  )}

  cnt = 0
  results = {}
  Dir.foreach '.' do |entry|
    if entry =~ /[^.]*\.#{SUFFIX}/
      if entry.start_with? '__'
        next
      end

      cnt += 1
      ok, out = make_prefect.call(entry).execute_ok cnt

      puts "Real output: #{out}\n"
      if ok
        results[entry] = :ok
        puts "#{entry} =====> #{'[OK]'.green}"
      else
        results[entry] = :failed
        puts "#{entry} =====> #{'[FAILED]'.red}"
      end

      puts '-' * 42
    end
  end

  count = results.inject({:ok => 0, :failed => 0}) do |acc, kv|
    _, v = kv
    acc[v] += 1
    acc
  end

  if count != {}
    if count[:ok] > 0
      ok = count[:ok].to_s.green
    else
      ok = 0.to_s
    end

    if count[:failed] > 0
      failed = count[:failed].to_s.red
    else
      failed = 0.to_s
    end
    puts "Summary: #{ok} passed, #{failed} failed."
  end
end
