package PS::MOPS::DC::Test::Tracklets;

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

my $DEBUG=0;

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

use PS::MOPS::DC::Tracklet;
use PS::MOPS::DC::Field;

my $_init=sub{};
my $_make_test_tracklets=sub{};
my $_compareTracklets=sub{};

# Constructor.
#
# instance
# num_tracklets
sub new
{
    my($class,%arg)=@_;
    my $self={};
    $self->{_INSTANCE}=$arg{instance}; # PSMOPS Test Instance.
    $self->{_NUM_TRACKLETS}=$arg{num_tracklets};
    $self->{_DETECTIONS}=$arg{detections}; # The detections that constitute the tracklet.
    $self->{_TEST_TRACKLETS}=undef;
    bless($self,$class);
    $self->$_init();
    return $self;
}

$_init=sub
{
    my($self)=shift;
    my @attributes=qw(trackletId
                      fieldId
                      vRA
                      vDEC
                      vRASigma
                      vDECSigma
                      accRA
                      accDEC
                      accRASigma
                      accDECSigma
                      extEpoch
                      extRA
                      extDEC
                      extMag
                      extRASigma
                      extDECSigma
                      extMagSigma
                      probability
                      detections
                      status
                      classification
                      ssmId
                      );
    my @test_tracklets=();
    $self->{_TEST_TRACKLETS}=\@test_tracklets;
    $self->{_ATTRIBUTES}=\@attributes;

    # Don't create tracklets yet, as detections and fields need to be defined first.
};

$_make_test_tracklets=sub
{
    my ($self,%arg)=@_;
    my $t=PS::MOPS::DC::Tracklet->new($self->{_INSTANCE}->getPSMOPSInstance(),
                                      classification=>'C',
                                      detections=>$self->{_DETECTIONS});
    @{$self->{_TEST_TRACKLETS}}[$arg{index}]=$t;
};

# Destructor.
sub DESTROY
{
}

# index
sub getTracklet
{
    my ($self,%arg)=@_;
    return @{$self->{_TEST_TRACKLETS}}[$arg{index}];
}

sub insertTracklets
{
    my ($self,%arg)=@_;
    my $success=1;
    for(my $i=0;$i<$self->{_NUM_TRACKLETS};$i++) {
        my $result=@{$self->{_TEST_TRACKLETS}}[$i]->insert(); # Tracklet ID is returned if successful.
        if(!$result){
            $success=0;
        }
    }
    return $success;
}

# detections = Detection objects that compose the tracklet.
sub setDetections
{
    my ($self,%arg)=@_;
    $self->{_DETECTIONS}=$arg{detections}; # Array reference.
}

sub makeTestTracklets
{
    my ($self,%arg)=@_;

    # Detections need to be defined.

    # Create tracklet objects.
    for(my $i=0;$i<$self->{_NUM_TRACKLETS};$i++) {
        $self->$_make_test_tracklets(index=>$i);
    }
}

# Retrieve all tracklets and verify them against the tracklets in memory.
# Note: Each tracklet in memory has a trackletId associated with it from the original tracklet insertion.
sub retrieveTracklets_TrackletID
{
    my ($self,%arg)=@_;
    my $success=1;
    for(my $i=0;$i<$self->{_NUM_TRACKLETS};$i++) {
        # @note Calling as a method, e.g.
        # PS::MOPS::DC::Tracklet->modct_retrieve(), does not work because the
        # PSMOPS Instance is being retrieved by shift. And in the case of a
        # method call the first array member is the calling object, not the
        # PSMOPS Instance.
        my $result=modct_retrieve($self->{_INSTANCE}->getPSMOPSInstance(),
                                  trackletId=>@{$self->{_TEST_TRACKLETS}}[$i]->{trackletId});
        if(!$self->$_compareTracklets(tracklet1=>$result,tracklet2=>@{$self->{_TEST_TRACKLETS}}[$i])){$success=0};
    }
    return $success;
}

# Retrieve tracklets that have a matching detection ID.
# At least two detections form a tracklet.
# An iterator will be returned by modct_retrieve.
#
# @todo Extend to multiple tracklets. This is only designed to handle a single
# tracklet so far.
#
# detection_id
# tracklet_id
sub retrieveTracklets_DetectionID
{
    my ($self,%arg)=@_;
    my $success=0;
    my $d_id=$arg{detection_id};
    my $t_id=$arg{tracklet_id};
    my $result=modct_retrieve($self->{_INSTANCE}->getPSMOPSInstance(),
                              detectionId=>$d_id);
    my $t=$result->next();
# DEBUGGING
#     print "result=$result\n";
#     print "result next=$t\n";

    if($t){
        if($t_id==$t->{trackletId}){$success=1;}
    }
    return $success;
}

# TODO: Handle derived objects.
#
# derivedobjectId
sub retrieveTracklets_DerivedObjectID
{
    my ($self,%arg)=@_;
#    my $result=modct_retrieve($self->{_INSTANCE}->getPSMOPSInstance(),
#                              derivedobjectId=>$arg{derived_object_id});
    return 1;
}

# Return the count of matching tracklets.
#
# TODO: Return more meaningful data.
#
# mjd = Range will be mjd + 30.
sub selectTracklets_MJD
{
    my ($self,%arg)=@_;
    if($DEBUG){
        print "[selectTracklets_MJD]\n";
        my $input=<>;
    }
    my $iter=modct_selectTracklets($self->{_INSTANCE}->getPSMOPSInstance(),
                                   mjd=>$arg{mjd},
                                   mysql_store_result=>1);
    my $cnt=0;
    while(defined($iter->next())){
        $cnt++;
    }
    return $cnt;
}

# Return the count of matching tracklets.
#
# TODO: Return more meaningful data.
#
# mjd1 = Range will be mjd1 + 1.
sub selectTracklets_MJD1
{
    my ($self,%arg)=@_;
    my $iter=modct_selectTracklets($self->{_INSTANCE}->getPSMOPSInstance(),
                                   mjd1=>$arg{mjd1},
                                   mysql_store_result=>1);
    my $cnt=0;
    while( defined( $iter->next() )){
        $cnt++;
    }
    return $cnt;
}

# Given a field ID, return all the tracklet ids that contain detections in that field.
#
# Return array reference of tracklet ids.
#
# field_id
sub selectTrackletIdsByFieldId
{
    my ($self,%arg)=@_;
    my $tracklet_ids_ref=modct_selectTrackletIdsByFieldId($self->{_INSTANCE}->getPSMOPSInstance(),
                                                          fieldId=>$arg{field_id});
    if(scalar(@{$tracklet_ids_ref})>=1){
        return $tracklet_ids_ref;
    }
    else {
        return 0;
    }
}

# Retrieve a lunation of tracklets.
# Return count of detections in tracklets where mjd range is [mjd,mjd+30).
#
# mjd = Range will be mjd1 + 30.
sub selectDetracklets_MJD
{
    my ($self,%arg)=@_;
    my $iter=modct_selectDetracklets($self->{_INSTANCE}->getPSMOPSInstance(),
                                     mjd=>$arg{mjd});
    my $cnt=0;
    while( defined( $iter->next() )){
        $cnt++;
    }
    return $cnt;
}

# Return count of detections in tracklets where mjd range is [mjd,mjd+1).
#
# mjd1 = Range will be mjd + 1.
sub selectDetracklets_MJD1
{
    my ($self,%arg)=@_;
    my $iter=modct_selectDetracklets($self->{_INSTANCE}->getPSMOPSInstance(),
                                     mjd1=>$arg{mjd1});
    my $cnt=0;
    while( defined( $iter->next() )){
        $cnt++;
    }
    return $cnt;
}

# modct_selectDetracklets returns an Iterator.
# Iterator composed of detections associated with tracklet terminating in field field_id.
#
# field_id = Terminating field ID.
sub selectDetracklets_fieldId
{
    my ($self,%arg)=@_;
    my $iter=modct_selectDetracklets($self->{_INSTANCE}->getPSMOPSInstance(),
                                  fieldId=>$arg{field_id});

    # Count the number of detections retrieved. This should match the global number of detections.
    my $cnt=0;
    while(defined($iter->next())){
        $cnt++;
    }
    return $cnt;
}

# Return count of detections constituting tracklet with ID tracklet_id.
#
# tracklet_id
sub selectDetracklets_trackletId
{
    my ($self,%arg)=@_;
    my $iter=modct_selectDetracklets($self->{_INSTANCE}->getPSMOPSInstance(),
                                     trackletId=>$arg{tracklet_id});
    my $cnt=0;
    while(defined($iter->next())){
        $cnt++;
    }
    return $cnt;
}

# Return count of detections within velocity cone of field with ID target_field_id.
#
# TODO: Test boundaries of velocity cone.
#
# @param target_field_id
# @todo Use realistic values for field_radius_deg, maxv_degperday.
sub selectDetracklets_target_field_id
{
    my ($self,%arg)=@_;
    my $iter=modct_selectDetracklets($self->{_INSTANCE}->getPSMOPSInstance(),
                                     target_field_id=>$arg{target_field_id},
                                     field_radius_deg=>7,
                                     maxv_degperday=>1);
    my $cnt=0;
    while(defined($iter->next())){
        $cnt++;
    }
    return $cnt;
}

# Get field ID of tracklet furthest in time.
#
# tracklets = List of tracklets.
sub getLastFieldId
{
    my ($self,%arg)=@_;
    my @tracklets=();
    foreach my $t (@{$self->{_TEST_TRACKLETS}}) {
      push(@tracklets,$t);
    }
    my $result=modct_getLastFieldId(undef,
                                    @tracklets);
    return $result;
}

# Compare two tracklets.
#
# tracklet1 = First tracklet to compare.
# tracklet2 = Second tracklet to compare.
$_compareTracklets=sub
{
    my ($self,%arg)=@_;
    my $match=1;
    foreach my $a (@{$self->{_ATTRIBUTES}}) {
        if($a ne "detections"){ # Skip checking detections for now.
            if( !defined($arg{tracklet1}->{$a}) && !defined($arg{tracklet2}->{$a}) ) {
                print STDERR "\tWarning: attribute $a not defined.\n";
            }
            elsif(! ($arg{tracklet1}->{$a} eq $arg{tracklet2}->{$a}) ) {
                print STDERR "\tWarning: attribute $a not equal in both tracklets.\n";
                $match=0;
            }
        }
    }
    return $match;
};

# Test updating the status for all tracklets.
# Verify that status was updated correctly by retrieving each tracklet's status.
sub updateStatus
{
    my ($self,%arg)=@_;
    my $success=1;
    # Retrieve all the tracklet objects.
    for(my $i=0;$i<$self->{_NUM_TRACKLETS};$i++) {
        my $id=@{$self->{_TEST_TRACKLETS}}[$i]->{trackletId};
        my $result=modct_retrieve($self->{_INSTANCE}->getPSMOPSInstance(),
                                  trackletId=>$id);
        # Update the status of the tracklet.
        if($result) {
            $result->status('A');
        }
        else {
            $success=0;
        }
        # Retrieve the tracklet again to verify that status was updated.
        my $result2=modct_retrieve($self->{_INSTANCE}->getPSMOPSInstance(),
                                   trackletId=>$id);
        if($result2->{status} ne 'A') {$success=0;}
    }    
    return $success;
}

1;
