package Opals::BackgroundJobs;

require Exporter;
@ISA       = qw(Exporter);

@EXPORT_OK = qw(
    bgjob_create
    bgjob_process
    bgjob_execute
    bgjob_getStatus
    bgjob_getOutput
);
use JSON;
use Time::Out qw(timeout) ;
use Time::HiRes qw( time );

# Version number
$VERSION   = 0.01;      
use  Opals::Utility qw(
    util_getXmlRecord
);

use Digest::SHA qw(
    sha1_hex
);
use Opals::MarcXml qw(
    mxml_update
    mxml_del_rec_holding
    mxml_cpDatafield
    mxml_mvDatafield
    mxml_addDatafield
    mxml_delDatafield
    mxml_delSubfield
    mxml_cpSubfield
    mxml_mvSubfield
    mxml_addSubfield
    mxml_datafieldReplace
    mxml_updateFieldInd
    mxml_updateItemType
    mxml_updateRecType
    mxml_mergeHolding
    mxml_recordPath
);


use strict;
############################################################################################
use constant   JOB_WAITING     =>  1;
use constant   JOB_PROCESSING  =>  2;
use constant   JOB_DONE        =>  3;
use constant   JOB_ERROR       =>  4;
use constant   ITEM_WAITING    =>  1;
use constant   ITEM_PROCESSING =>  2;
use constant   ITEM_DONE       =>  3;
use constant   ITEM_ERROR      =>  4;

############################################################################################
my $JOB_FUNCTION_MAP={
    moveShelfLoc    =>{fn=>\&_moveShelfLoc,   maxExecTime=>300},
    revertShelfLoc  =>{fn=>\&_revertShelfLoc, maxExecTime=>300},
    deleteHolding   =>{fn=>\&_delHolding},
    marcGlobalEdit  =>{fn=>\&_marcGlobalEdit}

};
############################################################################################
sub bgjob_getOutput{
    my($dbh,$jId)=@_;
    my ($output)=$dbh->selectrow_array("select output from opl_bgJob where jobId=$jId");
    return $output;
}

############################################################################################
sub bgjob_create{
    my($dbh,$job,$itemList)=@_;
    my $str=$job;
    my $jItemCount=0;
    if(defined $itemList){
        foreach my $i(@$itemList){
            $str .=$i->{'rid'}.$i->{'barcode'};
        }
    }
    my $signature = sha1_hex($job . $str);
    my ($jId)=$dbh->selectrow_array("select jobId from opl_bgJob where signature='$signature' && date_add(jobDate, INTERVAL 2  MINUTE) > NOW()");
    if(!defined $jId || $jId==0){
        my $sth=$dbh->prepare("insert into opl_bgJob set job=?,signature=?,status='waiting'");
        $sth->execute($job,$signature);
        $jId=$dbh->{'mysql_insertid'};
        $sth=$dbh->prepare("insert into opl_bgJobItem set jobId=?,rid=?,barcode=?");
        foreach my $item(@$itemList){
            $sth->execute($jId,$item->{'rid'},$item->{'barcode'}||'');
            $jItemCount++;
        }
    }
    return ($jId,$jItemCount);
}
############################################################################################
sub bgjob_getStatus{
    my($dbh,$jId)=@_;
    my $status=$dbh->selectrow_hashref("select status,output from opl_bgJob where jobId=$jId");
    return $status;
}
############################################################################################
sub bgjob_setJobStatus{
    my($dbh,$jId,$status)=@_;
    my $sth= $dbh->prepare("update opl_bgJob set status= ? where jobId=?");
    $sth->execute($status,$jId);
    $sth->finish;
}
############################################################################################
sub bgjob_getItemStatus{
    my($dbh,$jId,$barcode)=@_;
    my $status=undef;
    my $sth= $dbh->prepare("select status from opl_bgJobItem where jobId=? && barcode=?");
    $sth->execute($jId,$barcode);
    $sth->finish;
    if(my ($s) = $sth->fetchrow_array){
        $status=$s;
    }
    return $status;
}
############################################################################################
sub bgjob_setItemStatus{
    my($dbh,$jId,$rid,$barcode,$status)=@_;
    if(defined $barcode){
        $dbh->do("update opl_bgJobItem set status= ? where jobId=? && barcode=?",undef,$status,$jId,$barcode);      
    }
    elsif(defined $rid && $rid>0){
        $dbh->do("update opl_bgJobItem set status= ? where jobId=? && rid=?",undef,$status,$jId,$rid);      
    }
}
############################################################################################
sub bgjob_execute_1{
    my($dbh,$jId)=@_;
    my $nb_secs = 15;
        bgjob_process($dbh,$jId);

}

############################################################################################
sub bgjob_execute{
    my($dbh)=@_;
    my($jobId)=$dbh->selectrow_array("select jobId from opl_bgJob where status='waiting' limit 1");
    return if(!defined $jobId);
    bgjob_process($dbh,$jobId);
=item
    my ($site)          = $dbh->selectrow_array("select database()");
    my $OPALS_CONF_DIR  = '/etc/opals/conf/';
    my $rootDir         = Opals::Context->config('rootDir');
    my $script          = "$rootDir/script/cron/exec_bgJob &";
    my $opals_conf      = "/etc/opals/conf/$site";
    $ENV{'PERL5LIB'}    = "$rootDir/module";
    $ENV{'OPALS_CONF'}  = $opals_conf;
    $ENV{'BG_JOBID'}    = $jobId;
    my $pid = fork;
    system($script);
=cut    
    
}
############################################################################################
sub bgjob_process{
    my($dbh,$jId)=@_;
    my ($jobJson)=$dbh->selectrow_array("select job from opl_bgJob where jobId=$jId && status <>'done' && status <>'error'");
    if($jobJson ne ''){
        my $job=decode_json($jobJson);
        if(defined $JOB_FUNCTION_MAP->{$job->{'type'}}){
            my $fn      = $JOB_FUNCTION_MAP->{$job->{'type'}}->{'fn'};
            my $nb_secs = $JOB_FUNCTION_MAP->{$job->{'type'}}->{'maxExecTime'};
            if(defined $nb_secs && $nb_secs>0){
                #timeout $nb_secs => sub {
                    &$fn($dbh,$jId,$job);
                #   }
            }
            else{
                &$fn($dbh,$jId,$job);
            }
        }
    }
    return 1;#$status;
}
############################################################################################
sub _moveShelfLoc{
    my($dbh,$jobId,$job)=@_;
    my $newLoc= $job->{'newLocation'};
    my $sth=$dbh->prepare("select rid,barcode from opl_bgJobItem where jobId=? && status ='waiting' order by rid");
    $sth->execute($jobId);
    my $preRid=0;
    my $xml="";
    my $marc=undef;
    bgjob_setJobStatus($dbh,$jobId,JOB_PROCESSING);
    while( my ($rid,$bc)=$sth->fetchrow_array){
        if($rid!=$preRid){
            if($preRid>0){
                $xml = MARC::File::XML::record($marc);
                mxml_update($dbh, {rid=>$preRid, marcXml=>$xml});
            }
            $preRid =$rid;
            $xml =util_getXmlRecord($rid);
            $marc = Opals::Marc::Record::newFromXml($xml);

        }
        
        my @holdings = $marc->field('852');
        foreach my $h (@holdings) {
            if($bc eq  $h->subfield('p')){
                my $formerLoc="";
                if(defined $h->subfield('c') && $h->subfield('c') eq $newLoc){
                    next;
                }
                elsif(!defined $h->subfield('c')){
                    $h->add_subfields( c => $newLoc );
                }
                else{
                    $formerLoc =$h->subfield('c');
                    $h->update( 'c'=>$newLoc);
                }
                if(!defined $h->subfield('d')){
                    $h->add_subfields( d => $formerLoc );
                }
                else{
                    #$h->update( 'd'=>$formerLoc);
                }
            }
        }
       bgjob_setItemStatus($dbh,$jobId,$rid,$bc,ITEM_DONE);
    }
    if(defined $marc){
         $xml = MARC::File::XML::record($marc);
         mxml_update($dbh, {rid=>$preRid, marcXml=>$xml});
    }

   bgjob_setJobStatus($dbh,$jobId,JOB_DONE);


}
############################################################################################
sub _revertShelfLoc{
    my($dbh,$jobId,$job)=@_;
    my $sth=$dbh->prepare("select rid,barcode from opl_bgJobItem where jobId=? && status ='waiting' order by rid");
    $sth->execute($jobId);
    my $preRid=0;
    my $xml="";
    my $marc=undef;
    bgjob_setJobStatus($dbh,$jobId,JOB_PROCESSING);
    while( my ($rid,$bc)=$sth->fetchrow_array){
        if($rid!=$preRid){
            if($preRid>0){
                $xml = MARC::File::XML::record($marc);
                mxml_update($dbh, {rid=>$preRid, marcXml=>$xml});
            }
            $preRid =$rid;
            $xml =util_getXmlRecord($rid);
            $marc = Opals::Marc::Record::newFromXml($xml);

        }
        
        my @holdings = $marc->field('852');
        foreach my $h (@holdings) {
            if($bc eq  $h->subfield('p')){
                my $formerLoc='';
                $formerLoc=$h->subfield('d') if(defined $h->subfield('d'));
                #open debug,">/tmp/aa"; print debug "$formerLoc\n"; close debug;
               # next if(!defined $h->subfield('d'));
               # my $formerLoc=$h->subfield('d');
                if(!defined $h->subfield('c')){
                    $h->add_subfields( c => $formerLoc );
                }
                else{
                   # $formerLoc =$h->subfield('c');
                    $h->update( 'c'=>$formerLoc);
                    $h->delete_subfield(code=>'d');
                }
            }
        }
       bgjob_setItemStatus($dbh,$jobId,$rid,$bc,ITEM_DONE);
    }
    if(defined $marc){
         $xml = MARC::File::XML::record($marc);
         mxml_update($dbh, {rid=>$preRid, marcXml=>$xml});
    }
   bgjob_setJobStatus($dbh,$jobId,JOB_DONE);
}

############################################################################################
sub _delHolding{
    my($dbh,$jobId,$job)=@_;
    my $updateUnion=1;
    my $delMarcWithNoHolding=1;
    my $sth=$dbh->prepare("select rid,barcode from opl_bgJobItem where jobId=? && status ='waiting' order by rid");
    $sth->execute($jobId);
    bgjob_setJobStatus($dbh,$jobId,JOB_PROCESSING);
    while( my ($rid,$bc)=$sth->fetchrow_array){
        my ($status)=$dbh->selectrow_array("select status from opl_itemstatus where barcode=? order by id desc limit 1",undef,$bc);
        if($status!=5){
            mxml_del_rec_holding($dbh,{$rid=>{$bc=>1}},$delMarcWithNoHolding,$updateUnion);
        }
        bgjob_setItemStatus($dbh,$jobId,$rid,$bc,ITEM_DONE);
    }
    bgjob_setJobStatus($dbh,$jobId,JOB_DONE);
}


############################################################################################

sub _marcGlobalEdit{
    my($dbh,$jobId,$job)=@_;
    my $GE_FN_MAP={
        cpDataField         =>\&mxml_cpDatafield,
        datafieldReplace    =>\&mxml_datafieldReplace,
        updateItemType      =>\&mxml_updateItemType,
        updateRecType       =>\&mxml_updateRecType,
        delDatafield        =>\&mxml_delDatafield,
        delSubfield         =>\&mxml_delSubfield,
        addSubfield         =>\&mxml_addSubfield,
        addDatafield        =>\&mxml_addDatafield,
        mvSubfield          =>\&mxml_mvSubfield,
        mvDatafield         =>\&mxml_mvDatafield,
        cpSubfield          =>\&mxml_cpSubfield,
        updateFieldInd      =>\&mxml_updateFieldInd
    };

    my $ridBcList = getGE_bcList($dbh,$jobId);
    bgjob_setJobStatus($dbh,$jobId,JOB_PROCESSING);

    if(defined $GE_FN_MAP->{$job->{'action'}}){
        my $fn= $GE_FN_MAP->{$job->{'action'}};
        foreach my $item (@$ridBcList){
            my $rid=$item->{'rid'};
            my $bcList=$item->{'bcList'};
            my $xml = util_getXmlRecord($rid) || next;
            $job->{'param'}->{'rid'}=$rid;
            $job->{'param'}->{'bcList'}=$bcList;
            $xml = &$fn($xml,$job->{'param'});
            mxml_update($dbh, {rid=>$rid, marcXml=>$xml});
            bgjob_setItemStatus($dbh,$jobId,$rid,undef,ITEM_DONE);
        }
    }
    elsif($job->{'action'} eq 'mergeHolding'){
        my $rid=$job->{'param'}->{'tarRid'};
        my $xml_dest = util_getXmlRecord($rid);
        if($xml_dest && $xml_dest ne ''){
            foreach my $item (@$ridBcList){
                my $rid_src=$item->{'rid'};
                next if($rid_src eq $rid);
                my $xml_src = util_getXmlRecord($rid_src);
                next if(!$xml_src || $xml_src eq '');
                $xml_dest = mxml_mergeHolding($xml_src,$xml_dest);
                my $src = mxml_recordPath($rid_src) . '/' . $rid_src . '.xml';
                # Delete XML record
                unlink $src;
                $dbh->do("update opl_item set rid=? where rid=?",undef,$rid,$rid_src);
                $dbh->do("update opl_reserve set rid=? where rid=?",undef,$rid,$rid_src);
                $dbh->do("update opl_marcRecord set modDate=now(),zIndexed=0 where rid=? or rid= ?",undef,$rid,$rid_src);
                $dbh->do("update opl_itemStats set rid=? where rid= ?",undef,$rid,$rid_src);
                $dbh->do("delete from opl_recordStats where rid= ?",undef,$rid_src);
                bgjob_setItemStatus($dbh,$jobId,$rid_src,undef,ITEM_DONE);
            }
            mxml_update($dbh, {rid=>$rid, marcXml=>$xml_dest});
            bgjob_setItemStatus($dbh,$jobId,$rid,undef,ITEM_DONE);
        }
    
    }


    bgjob_setJobStatus($dbh,$jobId,JOB_DONE);
}
############################################################################################
sub getGE_bcList{
    my($dbh,$jobId)=@_;
    my @ridBcList=[];
    my $sth=$dbh->prepare("select rid,barcode from opl_bgJobItem where jobId=? && status ='waiting' order by rid");
    $sth->execute($jobId);
    my $preRid=-1;
    my $i=-1;
    while( my ($rid,$bc)=$sth->fetchrow_array){
        if($rid !=$preRid){
            $i++;
            @ridBcList[$i] ={rid=>$rid, bcList=>[$bc]};
            $preRid=$rid;
        }
        else{
            push @{@ridBcList[$i]->{'bcList'}},$bc;
        }
    }
    return \@ridBcList;

}
############################################################################################

1;

