#!/usr/bin/perl
use strict;
use warnings;

# Directory containing the compare and test files
my $cpmtstdir = "/home/wkt/Tecs/Misc/projects";

# Temporary directory used to do the testing
my $tempdir = "/home/wkt/Tecs/Assigs/test";

# Name of the test tool for each project
my %TestTool = (
    "01" => "HardwareSimulator.sh",
    "02" => "HardwareSimulator.sh",
    "03a" => "HardwareSimulator.sh",
    "03b" => "HardwareSimulator.sh",
    "04a" => "Assembler.sh",
    "04b" => "Assembler.sh",
    "05" => "HardwareSimulator.sh",
);

# Hash of compare and test files for each project
my %CmpList = (
    "01" => {
        "01/And16.cmp"     => 1,
        "01/And16.tst"     => 1,
        "01/And.cmp"       => 1,
        "01/And.tst"       => 1,
        "01/DMux4Way.cmp"  => 1,
        "01/DMux4Way.tst"  => 1,
        "01/DMux8Way.cmp"  => 1,
        "01/DMux8Way.tst"  => 1,
        "01/DMux.cmp"      => 1,
        "01/DMux.tst"      => 1,
        "01/Mux16.cmp"     => 1,
        "01/Mux16.tst"     => 1,
        "01/Mux4Way16.cmp" => 1,
        "01/Mux4Way16.tst" => 1,
        "01/Mux8Way16.cmp" => 1,
        "01/Mux8Way16.tst" => 1,
        "01/Mux.cmp"       => 1,
        "01/Mux.tst"       => 1,
        "01/Not16.cmp"     => 1,
        "01/Not16.tst"     => 1,
        "01/Not.cmp"       => 1,
        "01/Not.tst"       => 1,
        "01/Or16.cmp"      => 1,
        "01/Or16.tst"      => 1,
        "01/Or8Way.cmp"    => 1,
        "01/Or8Way.tst"    => 1,
        "01/Or.cmp"        => 1,
        "01/Or.tst"        => 1,
        "01/Xor.cmp"       => 1,
        "01/Xor.tst"       => 1
    },
    "02" => {
	"02/Add16.cmp"		=> 1,
	"02/Add16.tst"		=> 1,
	"02/ALU.cmp"		=> 1,
	"02/ALU.tst"		=> 1,
	"02/FullAdder.cmp"	=> 1,
	"02/FullAdder.tst"	=> 1,
	"02/HalfAdder.cmp"	=> 1,
	"02/HalfAdder.tst"	=> 1,
	"02/Inc16.cmp"		=> 1,
	"02/Inc16.tst"		=> 1
    },
    "03a" => {
	"03/1/Bit.cmp"		=> 1,
	"03/1/Bit.tst"		=> 1,
	"03/1/PC.cmp"		=> 1,
	"03/1/PC.tst"		=> 1,
	"03/1/RAM64.cmp"	=> 1,
	"03/1/RAM64.tst"	=> 1,
	"03/1/RAM8.cmp"		=> 1,
	"03/1/RAM8.tst"		=> 1,
	"03/1/Register.cmp"	=> 1,
	"03/1/Register.tst"	=> 1,
    },
    "03b" => {
	"03/2/RAM16K.cmp"	=> 1,
	"03/2/RAM16K.tst"	=> 1,
	"03/2/RAM4K.cmp"	=> 1,
	"03/2/RAM4K.tst"	=> 1,
	"03/2/RAM512.cmp"	=> 1,
	"03/2/RAM512.tst"	=> 1,
    },
    "04a" => {
	"04/fill/Fill.tst"	=> 1,
    },
    "04b" => {
	"04/mult/Mult.cmp"	=> 1,
	"04/mult/Mult.tst"	=> 1,
    },
    "05" => {
	"05/ComputerAdd.cmp"		=> 1,
	"05/ComputerAdd-external.cmp"	=> 1,
	"05/ComputerAdd-external.tst"	=> 1,
	"05/ComputerAdd.tst"		=> 1,
	"05/ComputerMax.cmp"		=> 1,
	"05/ComputerMax-external.cmp"	=> 1,
	"05/ComputerMax-external.tst"	=> 1,
	"05/ComputerMax.tst"		=> 1,
	"05/ComputerRect.cmp"		=> 1,
	"05/ComputerRect-external.cmp"	=> 1,
	"05/ComputerRect-external.tst"	=> 1,
	"05/ComputerRect.tst"		=> 1,
	"05/CPU.cmp"			=> 1,
	"05/CPU-external.cmp"		=> 1,
	"05/CPU-external.tst"		=> 1,
	"05/CPU.tst"			=> 1,
	"05/Memory.cmp"			=> 1,
	"05/Memory.tst"			=> 1,
	"05/PCLoadLogic.tst"		=> 1,
    },
);

# List of files to be tested for each project

my %TestList = (
    "01" => {
        "And16.hdl"     => 1,
        "And.hdl"       => 1,
        "DMux4Way.hdl"  => 1,
        "DMux8Way.hdl"  => 1,
        "DMux.hdl"      => 1,
        "Mux16.hdl"     => 1,
        "Mux4Way16.hdl" => 1,
        "Mux8Way16.hdl" => 1,
        "Mux.hdl"       => 1,
        "Not16.hdl"     => 1,
        "Not.hdl"       => 1,
        "Or16.hdl"      => 1,
        "Or8Way.hdl"    => 1,
        "Or.hdl"        => 1,
        "Xor.hdl"       => 1,
    },
    "02" => {
	"Add16.hdl"	=> 1,
	"ALU.hdl"	=> 1,
	"FullAdder.hdl"	=> 1,
	"HalfAdder.hdl"	=> 1,
	"Inc16.hdl"	=> 1,
	"Or16Way.hdl"	=> 1,
    },
    "03a" => {
	"Bit.hdl"	=> 1,
	"DFF16.hdl"	=> 1,
	"PC.hdl"	=> 1,
	"RAM64.hdl"	=> 1,
	"RAM8.hdl"	=> 1,
	"Register.hdl"	=> 1,
    },
    "03b" => {
	"RAM16K.hdl"	=> 1,
	"RAM4K.hdl"	=> 1,
	"RAM512.hdl"	=> 1,
    },
    "04a" => {
	"Fill.asm"	=> 1,
    },
    "04b" => {
	"Mult.asm"	=> 1,
    },
    "05" => {
	"Computer.hdl"		=> 1,
	"CPU.hdl"		=> 1,
	"Memory.hdl"		=> 1,
	"Mux8Way.hdl"		=> 1,
	"PCLoadLogic.hdl"	=> 1,
    },
);

### MAIN PROGRAM ###

# Check arguments
if ( @ARGV < 2 ) {
    print( STDERR "Usage: $0 projectid    list of Zip files\n" );
    exit(1);
}

my $projid = shift(@ARGV);

# Make sure the project id is defined
if ( !defined( $CmpList{$projid} ) ) {
    print( STDERR "Error: unknown project $projid, list is:\n    " );
    print( STDERR join(' ', sort(keys(%CmpList))) . "\n");
    exit(1);
}

# Make the temporary directory if required
mkdir($tempdir) if ( !-d $tempdir );

# Clean out the temporary directory
foreach my $f ( glob("$tempdir/*") ) {
    unlink($f);
}

# For every Zip file named on the command line
foreach my $zipfile (@ARGV) {

    # Unpack the HDL files into the temporary directory
    system("unzip -j -qq -d $tempdir $zipfile");
    if ( ( $? >> 8 ) != 0 ) {
        print( STDERR "Error unpacking $zipfile\n" );
        next;
    }

    # Remove any files which are not needed for testing
    foreach my $f ( glob("$tempdir/*") ) {
        my $basef = $f;
        $basef =~ s{.*/}{};    # Basename only
        unlink($f) if ( !defined( $TestList{$projid}{$basef} ) );
    }

    # Copy the tst and cmp files into the temporary directory
    foreach my $f ( keys( %{ $CmpList{$projid} } ) ) {
        system("cp $cpmtstdir/$f $tempdir");
    }

    print("Zip file $zipfile\n");

    # Report any HDL file if it is missing
    foreach my $f ( keys( %{ $TestList{$projid} } ) ) {
        if ( !-f "$tempdir/$f" ) {
            print("  Missing assignment file $f\n");
        }
    }

    # For every test script in the project
    foreach my $t ( keys( %{ $CmpList{$projid} } ) ) {
        next if ( !( $t =~ m{\.tst$} ) );    # Skip non-test script files
        $t =~ s{.*/}{};                      # Keep only the basename

        # Run the testing tool on the HDL file
        print("  Running $t: ");
        system("$TestTool{$projid} $tempdir/$t");
    }

    # Clean out the temporary directory
    foreach my $f ( glob("$tempdir/*") ) {
        unlink($f);
    }

    print("\n\n");
}

exit(0);
