module Integration

  class Test

    BASIC_EXTENSION = '.bas'

    def initialize(basic_path)
      @basic_path = basic_path
      create_input
      create_expected_output
      @expected_output = read_expected_output
    end

    def matches_patterns?(patterns)
      return true if patterns.empty?
      path = Pathname.new(@basic_path)
      path = path.relative_path_from(base_path)
      path.to_s =~ Regexp.union(patterns)
    end

    def run
      output_file = OutputFile.new
      output_file.max_lines = options['max_output_lines']
      File.open(input_path, 'r') do |input_file|
        begin
          program = Basic101::Program.load_file(@basic_path)
          runtime = Basic101::Runtime.new(:input_file => input_file,
                                          :output_file => output_file,
                                          :program => program)
          runtime.run
        rescue Error => e
          output_file.puts_unchecked e
        rescue Parslet::ParseFailed => e
          output_file.puts e
        rescue Basic101::Error => e
          output_file.puts e
        rescue Exception => e
          output_file.puts e, e.backtrace
        end
      end
      @output = output_file.string
    end

    def print_progress
      print progress_character
    end

    def print_failure
      return if passed?
      puts
      puts "Failed: #{File.basename(@basic_path)}"
      actual_output_file = Tempfile.new(expected_output_filename)
      actual_output_file.write @output
      actual_output_file.close
      command = [
        'diff',
        '-u',
        expected_output_path,
        actual_output_file.path,
      ].join(' ')
      system(command)
      actual_output_file.unlink
    end

    def train
      File.open(expected_output_path, 'w') do |file|
        file.write @output
      end
    end
    
    def passed?
      @output == @expected_output
    end

    private

    DEFAULT_OPTIONS = {
      'max_output_lines' => 10_000
    }

    def base_path
      Pathname.new(__dir__) + 'tests'
    end

    def progress_character
      if passed?
        '.'
      else
        'F'
      end
    end

    def create_input
      FileUtils.touch(input_path)
    end

    def create_expected_output
      FileUtils.touch(expected_output_path)
    end

    def read_expected_output
      File.read(expected_output_path)
    end

    def expected_output_filename
      File.basename(expected_output_path)
    end

    def input_path
      path_without_extension + '.input'
    end

    def expected_output_path
      path_without_extension + '.output'
    end

    def options_path
      path_without_extension + '.options'
    end

    def options
      if File.exists?(options_path)
        YAML.load_file(options_path)
      else
        DEFAULT_OPTIONS
      end
    end

    def path_without_extension
      @basic_path.chomp('.bas')
    end

  end

end