#!/usr/bin/perl

use strict;
use 5.6.0;
use warnings;

use File::Spec;
use POSIX;
use Text::Diff;
use File::Find;
use IO::Handle;
use IPC::Open3;
use IO::File;

my $test = shift or die "No test supplied\n";

-d $test or die "No such directory $test\n";

my $args = File::Spec->catfile($test, 'args');
my $args_canonify = File::Spec->catfile($test, 'args.canonify');
my $args_compare = File::Spec->catfile($test, 'args.compare');
my $baseline = File::Spec->catfile($test, 'baseline');
my $canonical = File::Spec->catfile($test, 'canonical');
my $errors_canonify = File::Spec->catfile($test, 'errors.canonify');
my $errors_compare = File::Spec->catfile($test, 'errors.compare');
my $original = File::Spec->catfile($test, 'original');
my $result_canonify = File::Spec->catfile($test, 'result.canonify');
my $result_compare = File::Spec->catfile($test, 'result.compare');
my $source = File::Spec->catfile($test, 'source');
my $diff = File::Spec->catfile($test, 'diff');

my @sources;
if (-d $source)
  {
    find(sub {return $File::Find::prune = 1 if /^\../ or /~$/;
              push @sources, $File::Find::name if -f $_}, $source);
  }
elsif (-f $source)
  {
    push @sources, $source;
  }
else
  {
    undef $source;
  }

undef $args unless -f $args;
undef $args_canonify unless -f $args_canonify;
undef $args_compare unless -f $args_compare;
undef $baseline unless -f $baseline;
undef $original unless -f $original;

my $canonify = 0;

$canonify = 1 if $source and not -f $canonical;

my $compare = 0;

$compare = 1 if $original and ($canonical or $canonify) and not -f $diff;

my $canonical_data;
if ($canonify)
  {
    my @args;
    if ($args_canonify)
      {
        my $fh = new IO::File $args_canonify, 'r';
        while (<$fh>) {chomp; push @args, $_}
      }
    elsif ($args)
      {
        my $fh = new IO::File $args, 'r';
        while (<$fh>) {chomp; push @args, $_}
      }

    my $canonify_output = "";
    my $canonify_errors = "";
    my $canonify_result;

    {
      my ($write, $read, $err) = (new IO::Handle, new IO::Handle, new IO::Handle);
      my $pid = open3($write, $read, $err, './icheck', '--canonify',
                      $baseline ? ('--baseline', $baseline) : (),
                      @sources, @args);

      close $write;

      $canonify_output .= $_ while <$read>;
      close $read;

      $canonify_errors .= $_ while <$err>;
      close $err;

      waitpid $pid, 0;
      if (WIFSIGNALED($?))
        {
          die "icheck killed by signal " . WTERMSIG($?) . "\n";
        }

      unless (WIFEXITED($?))
        {
          die "icheck died wierdly (waitpid gave $?)\n";
        }

      $canonify_result = WEXITSTATUS($?);
    }

    if (not -f $errors_canonify and $canonify_errors)
      {
        print "Had errors when canonifying\n";
        my $fh = new IO::File $errors_canonify, 'w';
        print $fh $canonify_errors;
      }

    if (not -f $result_canonify and $canonify_result)
      {
        print "Had non-zero result when canonifying\n";
        my $fh = new IO::File $result_canonify, 'w';
        print $fh "$canonify_result\n";
      }

    print "Generated canonical output\n";
    my $fh = new IO::File $canonical, 'w';
    print $fh $canonify_output;
  }

if ($compare)
  {
    my @args;
    if ($args_compare)
      {
        my $fh = new IO::File $args_compare, 'r';
        while (<$fh>) {chomp; push @args, $_}
      }
    elsif ($args)
      {
        my $fh = new IO::File $args, 'r';
        while (<$fh>) {chomp; push @args, $_}
      }

    my $compare_output = "";
    my $compare_errors = "";
    my $compare_result;

    {
      my ($write, $read, $err) = (new IO::Handle, new IO::Handle, new IO::Handle);
      my @cmd = ('./icheck', '--compare', $original,
                 $canonical ? $canonical : '-',
                 @args);
      if ($ENV{TEST_VERBOSE})
        {
          print join(' ', @cmd) . "\n";
        }
      my $pid = open3($write, $read, $err, @cmd);

      if (not $canonical)
        {
          print $write $canonical_data;
        }
      close $write;

      $compare_output .= $_ while <$read>;
      close $read;

      $compare_errors .= $_ while <$err>;
      close $err;

      waitpid $pid, 0;
      if (WIFSIGNALED($?))
        {
          die "icheck killed by signal " . WTERMSIG($?) . "\n";
        }

      unless (WIFEXITED($?))
        {
          die "icheck died wierdly (waitpid gave $?)\n";
        }

      $compare_result = WEXITSTATUS($?);
    }

    if (not -f $errors_compare and $compare_errors)
      {
        print "Had errors when compareing\n";
        my $fh = new IO::File $errors_compare, 'w';
        print $fh $compare_errors;
      }

    if (not -f $result_compare and $compare_result)
      {
        print "Had non-zero result when compareing\n";
        my $fh = new IO::File $result_compare, 'w';
        print $fh "$compare_result\n";
      }

    print "Generated diff output\n";
    my $fh = new IO::File $diff, 'w';
    print $fh $compare_output;
  }

exit 0;
