package PS::MOPS::DC::Test::Detections;

## @class PS::MOPS::DC::Test::Detections
#
# @par DESCRIPTION
# Class that contains a collection of detections. Used for testing a module in
# the PSMOPS System.

use strict;
use warnings;
our $VERSION = sprintf "%d", q$Revision: $ =~ /(\d+)/g;

use Error qw(:try);
use PS::MOPS::DC::Detection;
use PS::MOPS::DC::Test::Instance::Database::Table;
use PS::MOPS::Constants qw($MOPS_EFF_CLEAN
                           $MOPS_EFF_MIXED
                           $MOPS_EFF_BAD
                           $MOPS_EFF_NONSYNTHETIC);

my $_init=sub{};
my $_make_test_detection=sub{};
my $_set_all_clean=sub{};
my $_set_all_mixed=sub{};
my $_set_all_bad=sub{};
my $_set_all_nonsynth=sub{};

# Constructor.
#
# instance = PSMOPS Test Instance.
# num_detections = Number of detections to use during test.
sub new
{
    my ($class, %arg)=@_;
    my $self={};
    $self->{_INSTANCE}=$arg{instance}; # PSMOPS Test Instance.
    $self->{_TEST_DETECTIONS}=undef; # Reference to the test detection objects.
    $self->{_ARGUMENTS}=undef; # Arguments to the Detection module.
    $self->{_NUM_DETECTIONS}=$arg{num_detections};
    $self->{_TABLE}=undef;
    bless($self,$class);
    $self->$_init();
    return $self;
}

$_init=sub
{
    my ($self) = shift;
    my @arguments=qw(detId
                     ra
                     dec
                     epoch
                     mag
                     refMag
                     filter
                     raSigma
                     decSigma
                     magSigma
                     length_deg
                     orient_deg
                     fieldId
                     objectName
                     obscode
                     isSynthetic
                     status
                     );
    $self->{_ARGUMENTS}=\@arguments;
    my @test_detections=();
    $self->{_TEST_DETECTIONS}=\@test_detections;
    $self->{_TABLE}=PS::MOPS::DC::Test::Instance::Database::Table->new(name=>'detections',
                                                                       db=>$self->{_INSTANCE}->getDB());

    # Create detection objects.
    for(my $i=0;$i<$self->{_NUM_DETECTIONS};$i++) {
        $self->$_make_test_detection(index=>$i);
    }
};

# Destructor.
sub DESTROY
{
}

# Generate a test detection to be use for testing.
#
# index = Index in array.
$_make_test_detection=sub 
{
    my ($self,%arg)=@_;
    my $det=PS::MOPS::DC::Detection->new($self->{_INSTANCE}->getPSMOPSInstance(),
                                         mag=>10,
                                         dec=>0,
                                         ra=>0,
                                         epoch=>54000.5,
                                         s2n=>1.0,
                                         isSynthetic=>'Y',
                                         filter=>'DD'
                                         );
    @{$self->{_TEST_DETECTIONS}}[$arg{index}]=$det;
};

# Return field associated with detection.
#
# index = Index of test detection.
# field_id = ID of field in `fields` table.
sub test_getField
{
    my ($self,%arg)=@_;
    return @{$self->{_TEST_DETECTIONS}}[$arg{index}]->getField();
}

# index = Index of test detection in test detections array.
# field = Reference to field object that is to be associated with the test detection.
sub test_addToField
{
    my ($self,%arg)=@_;
    my $success=1; # addToField doesn't provide a return result.
    my $result=@{$self->{_TEST_DETECTIONS}}[$arg{index}]->addToField($arg{field});
    return $success;
}

# Retrieve a detection.
#
# index = Index of test detection.
sub test_modcd_retrieve
{
    my ($self,%arg)=@_;
    my $success=1;
    my $result;
    my $det;

    # Retrieve all detections in a field with ID field_id.
    eval {
        $result=modcd_retrieve($self->{_INSTANCE}->getPSMOPSInstance(),
                               detId=>@{$self->{_TEST_DETECTIONS}}[$arg{index}] -> {detId});
#DEBUGGING
#         print "index=$arg{index}\n";
#         print "result=$result\n";
#         print "result detid = $result->{detId}\n";

    };
    if($@){
        print "\tFailed.\n";
        $success=0;
    };
    # Compare retrieved detection to detection in memory.
    $self->compareDetections(detection1=>$det,detection2=>@{$self->{_TEST_DETECTIONS}}[$arg{index}]);
    return $success;
}

# Given a field ID, retrieve the detections that are associated with that field ID.
#
# TODO: Extend this to to handle more than one detection per field.
#
# field_id
sub test_modcd_retrieve__by_field
{
    my ($self,%arg)=@_;
    my $success=1;
    my $result;
    my $det;

    # Retrieve all detections in a field with ID field_id.
    eval {
        # Iterator object is returned:
        $result=modcd_retrieve($self->{_INSTANCE}->getPSMOPSInstance(),
                               fieldId=>$arg{field_id});

# DEBUGGING
#         print "\n";
#         print $arg{field_id};
#         print ": ";
#         print $result if $result;
#         print "\n";

        $det=$result->next();
    };
    if($@){
        print "\tFailed.\n";
        $success=0;
    };

    # Find the detection in memory that has the same detection id as the detection currently retrieved.
    # If the detection retrieved from the iterator does not have a detection ID then fail the test.
    if($det->{detId}){
        $self->compareDetections(detection1=>$det, detection2 => $self->getDetection( detection_id => $det->{detId} ) );
    }
    else {
        $success=0;
    }
    return $success;
}

sub test_modcd_delete
{
    my ($self,%arg)=@_;
    my $success=1;
    my $result=modcd_delete($self->{_INSTANCE}->getPSMOPSInstance(),
                            detId=>$arg{detection_id});
    if(!($result)) {
        $success=0;
    }
    return $success;
}

# Verify that the detections added to the database match the original
# detection objects.
sub verifyAddedDetections
{
    # For each detection in the test detections array, compare it to the
    # detection retrieved from the database.
}

# Compare two detections for equal values.
#
# TODO: Have definitions for all arguments.
#
# detection1 = First detection reference.
# detection2 = Second detection reference.
sub compareDetections
{
    my ($self,%arg)=@_;
# DEBUGGING
#     print "compareDetections\n";
#     print "$arg{detection1},$arg{detection2}\n";

    my $match=1;
    foreach my $argument (@{$self->{_ARGUMENTS}}) {
        if($argument ne "detId" && $argument ne "fieldId"){
            if( !defined($arg{detection1}->{$argument}) && !defined($arg{detection1}->{$argument}) ) {
            }
            elsif(! ($arg{detection1}->{$argument} eq $arg{detection2}->{$argument}) ) {
                $match=0;
            }
        }
    }
    return $match;
}

# getDetection(index)
# getDetection(detection_id)
#
# Return a detection object reference.
#
# index = Index of test detection.
# detection_id = Retrieve detection by searching for the detection id.
sub getDetection
{
    my ($self,%arg)=@_;
    my $found=0;
    if($arg{detection_id}){
        foreach my $d (@{$self->{_TEST_DETECTIONS}}) {
            if($d->{detId}==$arg{detection_id}){
                return $d;
            }
        }
        if(!$found) {
            die "Detection id=$arg{detection_id} not found.\n";
        }
    }
    else {
        return @{$self->{_TEST_DETECTIONS}}[$arg{index}];
    }
}

sub getTable
{
    my ($self)=shift;
    return $self->{_TABLE};
}

sub test_modcd_classifyDetections__clean
{
    my ($self,%arg)=@_;
    my $success=0;

    $self->$_set_all_clean();
    # Pass detection array to function.
    my @result=modcd_classifyDetections($self->{_INSTANCE}->getPSMOPSInstance(),
                                        @{$self->{_TEST_DETECTIONS}});
    my $classification=shift(@result);
    if($classification eq $MOPS_EFF_CLEAN) {
        $success=1;
    }
    return $success;
}

sub test_modcd_classifyDetections__bad
{
    my ($self,%arg)=@_;
    my $success=0;

    $self->$_set_all_bad();
    # Pass detection array to function.
    my @result=modcd_classifyDetections($self->{_INSTANCE}->getPSMOPSInstance(),
                                        @{$self->{_TEST_DETECTIONS}});
    my $classification=shift(@result);
    if($classification eq $MOPS_EFF_BAD) {
        $success=1;
    }
    return $success;
}

sub test_modcd_classifyDetections__mixed
{
    my ($self,%arg)=@_;
    my $success=0;

    $self->$_set_all_mixed();
    # Pass detection array to function.
    my @result=modcd_classifyDetections($self->{_INSTANCE}->getPSMOPSInstance(),
                                        @{$self->{_TEST_DETECTIONS}});
    my $classification=shift(@result);
    if($classification eq $MOPS_EFF_MIXED) {
        $success=1;
    }
    return $success;
}

# No synthetic detections are present.
sub test_modcd_classifyDetections__nonsynth
{
    my ($self,%arg)=@_;
    my $success=0;

    $self->$_set_all_nonsynth();
    # Pass detection array to function.
    my @result=modcd_classifyDetections($self->{_INSTANCE}->getPSMOPSInstance(),
                                        @{$self->{_TEST_DETECTIONS}});
    my $classification=shift(@result);
    if($classification eq $MOPS_EFF_NONSYNTHETIC) {
        $success=1;
    }
    return $success;
}

# Detection statuses are based on the objectName in the detection object.

$_set_all_clean=sub
{
    my ($self)=shift;
    # Assign the object name for each detection.
    foreach my $d (@{$self->{_TEST_DETECTIONS}}){
        $d->{objectName}="S13579";
    }
};

$_set_all_mixed=sub
{
    my ($self)=shift;
    my $length=scalar( @{$self->{_TEST_DETECTIONS}} );
    for(my $i=1;$i<$length;$i++){
        @{$self->{_TEST_DETECTIONS}}[$i]->{objectName}="S00001";
    }
    @{$self->{_TEST_DETECTIONS}}[0]->{objectName}='S00002';
};

$_set_all_bad=sub
{
    my ($self)=shift;
    my $length=scalar( @{$self->{_TEST_DETECTIONS}} );
    for(my $i=1;$i<$length;$i++){
        @{$self->{_TEST_DETECTIONS}}[$i]->{objectName}="S24680";
    }
    @{$self->{_TEST_DETECTIONS}}[0]->{objectName}='';
};

$_set_all_nonsynth=sub
{
    my ($self)=shift;
    my $length=scalar( @{$self->{_TEST_DETECTIONS}} );
    for(my $i=0;$i<$length;$i++){
        @{$self->{_TEST_DETECTIONS}}[$i]->{objectName}='';
    }
};

1;
