#!/usr/bin/perl i-w

package ippToPsps::IppToPspsDb;

use warnings;
use strict;

use ippToPsps::MySQLDb;
our @ISA = qw(ippToPsps::MySQLDb);    # inherits from MySQLDb

###########################################################################
#
# Returns a list of batches created within the provided dates
#
###########################################################################
sub getBatches {
    my ($self, $batches, $fromTime, $toTime) = @_;

    my $query = $self->{_db}->prepare(<<SQL);
    SELECT created, 
           batch_type, 
           exp_id, 
           batch_id, 
           survey_id, 
           deleted, 
           dvo_db, 
           processed, 
           on_datastore, 
           loaded_to_ODM, 
           load_failed, 
           merge_worthy, 
           merged 
        FROM batches 
        WHERE created >= '$fromTime'
        AND created <= '$toTime'
SQL

    # TODO remove date restriction
    $query->execute;
    ${$batches} = $query->fetchall_arrayref();
    return scalar @{${$batches}};
}

###########################################################################
#
# Returns a info about one particular batch
#
###########################################################################
sub getBatch{
    my ($self, $batch_id, $batches) = @_;

    my $query = $self->{_db}->prepare(<<SQL);
    SELECT created, 
           batch_type, 
           exp_id, 
           batch_id, 
           survey_id, 
           deleted, 
           dvo_db, 
           processed, 
           on_datastore, 
           loaded_to_ODM, 
           load_failed, 
           merge_worthy, 
           merged
        FROM batches 
        WHERE batch_id = $batch_id 
SQL
  
    $query->execute;
    ${$batches} = $query->fetchall_arrayref();
    return scalar @{${$batches}};
}

###########################################################################
#
# Returns a count of all distinct exposures between provided limits
#
###########################################################################
sub countExposures {
    my ($self, $fromExp, $toExp, $datastoreProduct, $batchType, $dvoDb) = @_;

    my $query = $self->{_db}->prepare(<<SQL);
    SELECT COUNT(distinct exp_id) 
        FROM batches 
        WHERE exp_id >= $fromExp
        AND exp_id <= $toExp 
        AND dvo_db = '$dvoDb' 
        AND batch_type = '$batchType' 
        AND datastore_product = '$datastoreProduct' 
SQL

    $query->execute;
    return scalar $query->fetchrow_array();
}

###########################################################################
#
# Returns a count of exposures (between provided limits) that have been merged
#
###########################################################################
sub countMergedExposures {
    my ($self, $fromExp, $toExp, $datastoreProduct, $batchType, $dvoDb) = @_;

    my $query = $self->{_db}->prepare(<<SQL);
    SELECT COUNT(distinct exp_id) 
        FROM batches 
        WHERE exp_id >= $fromExp
        AND exp_id <= $toExp 
        AND dvo_db = '$dvoDb' 
        AND batch_type = '$batchType' 
        AND datastore_product = '$datastoreProduct' 
        AND merged
SQL

    $query->execute;
    return scalar $query->fetchrow_array();
}

###########################################################################
#
# Returns a list of exposures (between provided limits) that have not been merged
#
###########################################################################
sub getUnmergedExposures {
    my ($self, $exposures, $fromExp, $toExp, $datastoreProduct, $batchType, $dvoDb) = @_;

    my $query = $self->{_db}->prepare(<<SQL);
    SELECT batch_id, exp_id 
        FROM batches 
        WHERE exp_id >= $fromExp
        AND exp_id <= $toExp 
        AND dvo_db = '$dvoDb' 
        AND batch_type = '$batchType' 
        AND datastore_product = '$datastoreProduct' 
        AND !merged
        GROUP BY exp_id
SQL

    $query->execute;
    ${$exposures} = $query->fetchall_arrayref();
    return scalar @{${$exposures}};
}

#######################################################################################
#
#  Override constructor
#
########################################################################################
sub new {
    my ($class) = @_;

    # Call the constructor of the parent class, Person.
    my $self = $class->SUPER::new($_[1], $_[2], $_[3], $_[4], $_[5], $_[6]);

    bless $self, $class;

    $self->update();
    return $self;
}

#######################################################################################
#
# Gets the last processed exposure ID 
#
########################################################################################
sub getLastExpId {
    my ($self) = @_;

    my $query = $self->{_db}->prepare(<<SQL);
    SELECT exp_id 
        FROM batches 
        WHERE processed = 1
        ORDER BY exp_id 
        DESC LIMIT 1;
SQL

    $query->execute;

    my $expId = $query->fetchrow_array();

    if ($self->{_verbose}) {print "* Last successfully processed exposure ID was $expId\n";}

    return $expId;
}

#######################################################################################
#
# Updates an existing database record 
#
########################################################################################
sub setAsProcessed {
    my ($self, $batchId, $expId, $totalDetections, $minObjId, $maxObjId) = @_;

    if (!$minObjId) {$minObjId = -1;}
    if (!$maxObjId) {$maxObjId = -1;}

if (!$totalDetections) {$totalDetections = -1;}

    my $query = $self->{_db}->prepare(<<SQL);
    UPDATE batches 
        SET 
          processed = 1, 
          total_detections = $totalDetections,
          min_obj_id = $minObjId,
          max_obj_id = $maxObjId
        WHERE batch_id = $batchId 
        AND exp_id = $expId;
SQL

    $query->execute; # TODO check response of this
}

#######################################################################################
#
# Sets published state of a batch 
#
########################################################################################
sub setPublished {
    my ($self, $batchId, $expId, $published) = @_;

    my $query = $self->{_db}->prepare(<<SQL);
    UPDATE batches 
        SET on_datastore = $published 
        WHERE batch_id = $batchId 
        AND exp_id = $expId;
SQL

    $query->execute; # TODO check response of this
}

#######################################################################################
#
# Checks whether this exposure failed to load to the ODM
#
########################################################################################
sub didLoadFail {
    my ($self, $expId) = @_;

    my $query = $self->{_db}->prepare(<<SQL);
    SELECT COUNT(*) 
        FROM batches 
        WHERE exp_id = $expId
        AND load_failed
SQL

    $query->execute;

    return scalar $query->fetchrow_array();
}

#######################################################################################
#
# Checks whether this exposure is 'MergeWorthy'
#
########################################################################################
sub isMergeWorthy {
        my ($self, $expId) = @_;

        my $query = $self->{_db}->prepare(<<SQL);
        SELECT COUNT(*) 
            FROM batches 
            WHERE exp_id = $expId
            AND merge_worthy
SQL

    $query->execute;

    return scalar $query->fetchrow_array();
}


#######################################################################################
#
# Checks whether this exposure has a min object ID under old PSPS hard limit of 72010000000000001
#
########################################################################################
sub isMinObjIdUnderLimit {
    my ($self, $expId) = @_;

    my $query = $self->{_db}->prepare(<<SQL);
    SELECT COUNT(*) 
        FROM batches 
        WHERE exp_id = $expId
        AND min_obj_id > 0
        AND min_obj_id < 72010000000000001
SQL

    $query->execute;

    return scalar $query->fetchrow_array();
}
#######################################################################################
#
# Checks whether this exposure has been processed
#
########################################################################################
sub isExposureProcessed {
    my ($self, $expId) = @_;

    my $query = $self->{_db}->prepare(<<SQL);
    SELECT COUNT(*) 
        FROM batches 
        WHERE exp_id = $expId
        AND processed
SQL

    $query->execute;

    return scalar $query->fetchrow_array();
}

#######################################################################################
#
# Checks whether this exposure has been merged 
#
########################################################################################
sub isExposureMerged {
    my ($self, $expId) = @_;

    my $query = $self->{_db}->prepare(<<SQL);
    SELECT COUNT(*) 
        FROM batches 
        WHERE exp_id = $expId
        AND merged
SQL

    $query->execute;

    return scalar $query->fetchrow_array();
}

#######################################################################################
#
# Checks whether this exposure has been loaded to ODM 
#
########################################################################################
sub isExposureLoadedToOdm {
    my ($self, $expId) = @_;

    my $query = $self->{_db}->prepare(<<SQL);
    SELECT COUNT(*) 
        FROM batches 
        WHERE exp_id = $expId
        AND loaded_to_ODM
SQL

    $query->execute;

    return scalar $query->fetchrow_array();
}

#######################################################################################
#
# Checks whether we have successfully processed this exposure and loaded it to the datastore 
#
########################################################################################
sub isExposureAlreadyPublished {
    my ($self, $expId) = @_;

    my $query = $self->{_db}->prepare(<<SQL);
    SELECT COUNT(*) 
        FROM batches 
        WHERE exp_id = $expId
        AND processed
        AND on_datastore;
SQL

    $query->execute;

    my $processed = $query->fetchrow_array();

    # TODO can use return scalar $query->fetchrow_array();
    return $processed;
}

#######################################################################################
#
# Generates a new record in the database with a new batch ID 
#
########################################################################################
sub createNewBatch {
    my ($self, $expId, $surveyType, $batchType, $dvoDb, $datastoreProduct) = @_;

    my $query = $self->{_db}->prepare(<<SQL);
    SELECT batch_id 
        FROM batches 
        ORDER BY batch_id 
        DESC LIMIT 1;
SQL

    $query->execute;

    my $batchId = $query->fetchrow_array();

    $batchId++;

    $query = $self->{_db}->prepare(<<SQL);
    INSERT INTO batches
        (batch_id, exp_id, survey_id, batch_type, dvo_db, datastore_product)
        VALUES
        ($batchId, $expId, '$surveyType', '$batchType', '$dvoDb', '$datastoreProduct');
SQL

    $query->execute;

    print "* New batch '$batchId' for exposure ID = '$expId' and survey = '$surveyType'\n";

    return $batchId;
}

#######################################################################################
#
# Updates an existing database record to show datastore status
#
#######################################################################################
sub setBatchAsDeleted {
    my ($self,$batchId, $expId) = @_;

    my $query = $self->{_db}->prepare(<<SQL);
    UPDATE batches 
        SET deleted = 1 
        WHERE batch_id = $batchId 
        AND exp_id = $expId;
SQL

        $query->execute; # TODO check response of these
}

#######################################################################################
#
# Updates an existing database record with info from ODM
#
#######################################################################################
sub updateODMStatus {
    my ($self,$batchId, $expId, $loadedToOdm, $loadFailed, $mergeWorthy, $mergeCompleted) = @_;

    my $query = $self->{_db}->prepare(<<SQL);
    UPDATE batches 
        SET loaded_to_ODM = $loadedToOdm, 
            load_failed = $loadFailed, 
            merge_worthy = $mergeWorthy, 
            merged = $mergeCompleted 
        WHERE batch_id = $batchId 
        AND exp_id = $expId;
SQL

        $query->execute; # TODO check response of these
}



###########################################################################
#
# Update Db to newest version 
#
###########################################################################
sub update {
    my ($self) = @_;

    my $currentRevision = -1;

    while (1) {

        $currentRevision = $self->getRevision();
        if ($self->{_verbose}) {print "* Current database revision = $currentRevision\n";}

        if ($currentRevision == 0) {$self->createRevision_1();}
        elsif ($currentRevision == 1) {$self->createRevision_2();}
        elsif ($currentRevision == 2) {$self->createRevision_3();}
        elsif ($currentRevision == 3) {$self->createRevision_4();}
        elsif ($currentRevision == 4) {$self->createRevision_5();}
        elsif ($currentRevision == 5) {$self->createRevision_6();}
        elsif ($currentRevision == 6) {$self->createRevision_7();}
        else {last;}
    }
}

#######################################################################################
# 
# Create revision 1 of the database 
#
#######################################################################################
sub createRevision_1 {
    my ($self) = @_;

    print "* Creating revision 1 of '$self->{_dbName}'\n";

    my $query = $self->{_db}->prepare(<<SQL);
    CREATE TABLE revision (
            revision INT, 
            created TIMESTAMP DEFAULT NOW(),
            primary key (revision)
            );
SQL
        $query->execute;

    $query = $self->{_db}->prepare(<<SQL);
    CREATE TABLE batches (
            batch_id BIGINT NOT NULL, 
            exp_id BIGINT NOT NULL, 
            survey_id VARCHAR(30) DEFAULT "NONE",
            processed TINYINT DEFAULT 0,
            on_datastore TINYINT DEFAULT 0,
            loaded_to_ODM TINYINT DEFAULT 0,
            merge_worthy TINYINT DEFAULT 0,
            deleted TINYINT DEFAULT 0,
            created TIMESTAMP DEFAULT NOW(),
            primary key (batch_id, exp_id)
            );

SQL
        $query->execute;

    $self->setRevision(1);
}

#######################################################################################
# 
# Create revision 2 of the database 
#
#######################################################################################
sub createRevision_2 {
    my ($self) = @_;

    print "* Creating revision 2 of '$self->{_dbName}'\n";

    my $query = $self->{_db}->prepare(<<SQL);
    ALTER TABLE batches 
        ADD COLUMN merged TINYINT DEFAULT 0
SQL
        $query->execute;

    $self->setRevision(2);
}

#######################################################################################
# 
# Create revision 3 of the database 
#
#######################################################################################
sub createRevision_3 {
    my ($self) = @_;

    print "* Creating revision 3 of '$self->{_dbName}'\n";

    my $query = $self->{_db}->prepare(<<SQL);
    ALTER TABLE batches 
        ADD COLUMN total_detections BIGINT DEFAULT 0
SQL
        $query->execute;

    $self->setRevision(3);
}

#######################################################################################
# 
# Create revision 4 of the database 
#
#######################################################################################
sub createRevision_4 {
    my ($self) = @_;

    print "* Creating revision 4 of '$self->{_dbName}'\n";

    my $query = $self->{_db}->prepare(<<SQL);
    ALTER TABLE batches 
        ADD COLUMN batch_type VARCHAR(30) DEFAULT "UNKNOWN"
SQL
        $query->execute;

    $self->setRevision(4);
}

#######################################################################################
# 
# Create revision 5 of the database 
#
#######################################################################################
sub createRevision_5 {
    my ($self) = @_;

    print "* Creating revision 5 of '$self->{_dbName}'\n";

    my $query = $self->{_db}->prepare(<<SQL);
         CREATE INDEX batchesIndex ON batches (batch_id);
SQL
    $query->execute;

    $self->setRevision(5);
}

#######################################################################################
# 
# Create revision 6 of the database 
#
#######################################################################################
sub createRevision_6 {
    my ($self) = @_;

    print "* Creating revision 6 of '$self->{_dbName}'\n";

    my $query = $self->{_db}->prepare(<<SQL);
    ALTER TABLE batches 
        ADD COLUMN dvo_db VARCHAR(50) DEFAULT "UNKNOWN",
        ADD COLUMN datastore_product VARCHAR(50)  DEFAULT "UNKNOWN",
        ADD COLUMN min_obj_id BIGINT DEFAULT 0,
        ADD COLUMN max_obj_id BIGINT DEFAULT 0
SQL
    $query->execute;

    $self->setRevision(6);
}

#######################################################################################
# 
# Create revision 7 of the database 
#
#######################################################################################
sub createRevision_7 {
    my ($self) = @_;

    print "* Creating revision 7 of '$self->{_dbName}'\n";

    my $query = $self->{_db}->prepare(<<SQL);
    ALTER TABLE batches 
        ADD COLUMN load_failed TINYINT DEFAULT 0
SQL
        $query->execute;

    $self->setRevision(7);
}

#######################################################################################
# 
# Sets current revision of ippToPsps database
#
#######################################################################################
sub setRevision {
    my ($self, $revision) = @_;

    my $query = $self->{_db}->prepare(<<SQL);
    INSERT INTO revision (revision) VALUES ($revision);
SQL
        $query->execute;
}

#######################################################################################
# 
# Gets current revision of ippToPsps database
#
#######################################################################################
sub getRevision {
    my ($self) = @_;

    if (!$self->SUPER::doesTableExist("revision")) {return 0;}

    my $query = $self->{_db}->prepare(<<SQL);
    SELECT revision
        FROM revision
        ORDER BY revision DESC LIMIT 1;
SQL

        $query->execute;
    my @row = $query->fetchrow_array();

    return $row[0];
}

sub insetDetection {
    my ($self, $expId, $imageID, $objID,$ippObjID, $ippDetectID) = @_;

   my $query = $self->{_db}->prepare(<<SQL);
    INSERT INTO detections
        (expId, imageID, objID, ippObjID, ippDetectID)
        VALUES
        ($expId, $imageID, $objID,$ippObjID, $ippDetectID);
SQL

    $query->execute;

}

1
