#!/usr/bin/perl

# This software was produced by NIST, an agency of the U.S. government, 
# and by statute is not subject to copyright in the United States. Recipients 
# of this software assume all responsibilities associated with its operation, 
# modification and maintenance.

use strict;

# Check a TRECVID 2008 copy detection submission.
# #
# This script was tested on a Dell x86 workstation running Red Hat
# Enterprise Linux (RHEL) 4.  It probably does not run under Windows
# without a POSIX environment such as Cygwin.

# For non-RHEL4 systems, you may need to modify the paths below.

my $errlog_dir = ".";
my $MAX_ERRORS = 1000;
my $results_file;		# input file to be checked
my $errlog;			# file name of error log
my $num_errors;                 # number of errors seen so far
my $usage = "Usage: check_copydetection.pl <results-file>\n";

$results_file = shift or die $usage;

$errlog = $errlog_dir . "/" . $results_file . ".errlog";
open ERRLOG, ">$errlog"
    or error("Can't open error log for writing");

#--------------------------------------------------------------------------
my $refVideos    = "./ref.videos";
my $refDurations = "./ref.durations";

my $slash = "/";
my $botValidQueryId = 0;
my $topValidQueryId = 0;;
my $requiredNumQueries = 0;
my $lineNum = 0;
my $line = "";

my %qIdHash = ();
my %durations = ();
my %videos = ();
my %overlap = ();
my @list = ();


# open run file
open RUN, "$results_file" or
    die "Can't open runfile \"$results_file\": $!\n";

# Derive run type from file name: GROUPID.RUNTYPE.runId
# where RUNTYPE = a | v | m

my $i = rindex($results_file,$slash);
if ($i != -1)
{
    $results_file = substr($results_file,$i);
}
my (undef,$runType,$webpageRunId) = split /\./,$results_file;


# Read valid videoIds into a hash
open VIDEOS, "$refVideos" or die "Can't open $refVideos file: $!\n";
while ($line=<VIDEOS>)
{
    my ($fileName) = split /\s+/,$line;
    $videos{$fileName} = 1;
}


# Read ref video durations into a hash
open DURATIONS, "$refDurations" or die "Can't open $refDurations file: $!\n";
while ($line=<DURATIONS>)
{
    my (undef,$dur,$frames,$fileName,undef) = split /\s+/," $line";
    $durations{$fileName} = $dur;
    #$durations{$fileName} = $frames/25;
}

# Set range of valid query IDs
if ($runType eq "a")
{
    $botValidQueryId = 3001;
    $topValidQueryId = 4407;
    $requiredNumQueries = 1407;
}
elsif ($runType eq "v")
{
    $botValidQueryId = 1;
    $topValidQueryId = 2010;
    $requiredNumQueries = 2010;
} 
elsif ($runType eq "m")
{
    $botValidQueryId =  5001;
    $topValidQueryId = 19070;
    $requiredNumQueries = 14070;
} 

my $runId = "";
my $opSys = "";
my $cpu = "";
my $memory = "";

while ($line =  <RUN>)
{
    $lineNum++;

    chomp $line;

    my (undef,$code,$rest) = split /\s+/," $line",3;

    if ($code eq "R")
    {
	my ($qId,$vId,$firstRefTime,$lastRefTime,$conf,$firstQueryTime) = split /\s+/,$rest;

	# valid queryId for this run type (v, a, m)?
	if ($qId > $topValidQueryId || $qId < $botValidQueryId)
	{
	    &error("Line $lineNum: invalid query id ($qId) for this run type: $runType");
	}

	# valid video file name?
	if (! exists $videos{$vId})
	{
	    &error("Line $lineNum: invalid video id: \"$vId\"");
	}

	# last ref time > first time ref?
	if ($lastRefTime <= $firstRefTime)
	{
	    &error("Line $lineNum: last ref time ($lastRefTime) precedes first ref time ($firstRefTime)");
	}

	# first ref time >= 0?
	if ($firstRefTime < 0)
	{
	    &error("Line $lineNum: first ref time ($firstRefTime) < = 0");
	}

	# first ref time <= end of video?
	if ($firstRefTime > $durations{$vId})
	{
	    &error("Line $lineNum: first ref time ($firstRefTime) beyond end of ref video ($durations{$vId})");
	}

	# last ref time <= end of video?
	if ($lastRefTime > $durations{$vId})
	{
	    &error("Line $lineNum: last ref time ($lastRefTime) beyond end of ref video ($durations{$vId})");
	}

	# Add result item to hash used to look for possible overlapping items
	if (!exists $overlap{"$qId $vId"})
	{
	    $overlap{"$qId $vId"} = ();
	}

	@list = [$firstRefTime, $lastRefTime];

	push @{ $overlap{"$qId $vId"} }, @list;


	# first query ref <= end of query?

    }
    # Timing info: queryId elapsedQueyProcessingTimeInSeconds
    elsif ($code eq "T")
    {
	my ($qId,$time) = split /\s+/,$rest;

	# valid queryId for this run type (v, a, a+v)?
	if ($qId < $botValidQueryId || $qId > $topValidQueryId)
	{
	    &error("Line $lineNum: invalid query id for this run type: $runType");
	}
	else
	{
	    $qIdHash{$qId} = 1;
	}
    }
    # runId: string
    elsif ($code eq "I")
    {
	$runId = $rest;
	chomp $rest;

	if ($runId !~ /\A[a-zA-Z0-9]+\Z/)
	{
	    &error("Line $lineNum: run ID in file \"$runId\" is not just alphanumeric [a-z,A-Z,0-9]");
	}

	if ($runId ne $webpageRunId)
	{
	    &error("Line $lineNum: input run ID \"$webpageRunId\" does not match run ID in file \"$runId\"");
	}
    }
    # operating system: string
    elsif ($code eq "S")
    {
	$opSys = $rest;
    }
    # cpu: string
    elsif ($code eq "C")
    {
	$cpu = $rest;
    }
    # amount of memory: string
    elsif ($code eq "M")
    {
	$memory = $rest;
    }
    # unknown code
    else
    {
	&error("Line $lineNum: Unrecognized code \"$code\"");
    }


}

# Check runId present?
if ($runId eq "")
{
    &error("run id missing");
}

# Check operating system present?
if ($opSys eq "")
{
    &error("operating system info missing");
}

# Check cpu present?
if ($cpu eq "")
{
    &error("cpu type missing");
}

# Check memory present?
if ($memory eq "")
{
    &error("memory size missing");
}

# Check timing for all queries present?
my $actualNumQueries = scalar keys %qIdHash;
if ($actualNumQueries != $requiredNumQueries)
{
    &error("missing/extra timing info for queries: timing for $requiredNumQueries queries required, $actualNumQueries submitted");
}

# Check no overlapping result items
my $i = 0;
my $j = 0;
my $maxI = 0;
my $queryVideo = "";

foreach my $queryVideo (sort keys %overlap)
{
    $maxI = scalar @{$overlap{$queryVideo}};
    
    for ($i=0;$i<$maxI;$i++)
    {
	for ($j=$i+1;$j<$maxI;$j++)
	{

	    if (
	        ((@{@{$overlap{$queryVideo}}[$i]}[0] >=  @{@{$overlap{$queryVideo}}[$j]}[0]) &&
	         (@{@{$overlap{$queryVideo}}[$i]}[0] <=  @{@{$overlap{$queryVideo}}[$j]}[1]))
	    ||
	        ((@{@{$overlap{$queryVideo}}[$i]}[1] >=  @{@{$overlap{$queryVideo}}[$j]}[0]) &&
	         (@{@{$overlap{$queryVideo}}[$i]}[1] <=  @{@{$overlap{$queryVideo}}[$j]}[1]))
	       )

	    {
		&error("Overlapping results items for Query $queryVideo: (@{@{$overlap{$queryVideo}}[$i]}) (@{@{$overlap{$queryVideo}}[$j]})");
	    }
	}
    }

}


#--------------------------------------------------------------------------
print ERRLOG "Finished processing $results_file\n";
close ERRLOG || die "Close failed for error log $errlog: $!\n";

if ($num_errors) { exit 255; }
exit 0;


# print error message, keeping track of total number of errors
sub error {
   my $msg_string = pop(@_);

    print ERRLOG 
    "$0 of $results_file: Error --- $msg_string\n";

    $num_errors++;
    if ($num_errors > $MAX_ERRORS) {
        print ERRLOG "$0 of $results_file: Quit. Too many errors!\n";
        close ERRLOG ||
		die "Close failed for error log $errlog: $!\n";
	exit 255;
    }
}
