#!/usr/bin/perl

#use utf8;
use strict;
use CGI;

use Opals::Context;use POSIX qw(
    floor
);

use Time::localtime;
use Opals::Constant;

use Opals::Template qw(
    tmpl_read
    tmpl_write
    tmpl_preference
);
use Opals::User qw(
    user_list
    user_getInformationById
    user_balance
);
use Opals::Circulation qw(
    circ_placeReserve
    circ_fillReserve
    circ_cancelReserve
    circ_cancelHold_all
    circ_placeHold
    circ_userListReserve
    circ_GetLostNumber
    circ_GetDamagedNumber
    circ_numActiveItem
    circ_getRecInfo
    circ_getReserveID
    circ_numItemsAvailable
    circ_getRecID
    Circ_validateFollettBarcode
    Circ_validateSpectrumBarcode
    Circ_validateLeadingZeroBc
    circ_cancelHoldReserve
    circ_getReserveInfo
);



use Opals::Date qw(
    date_parse
    date_today
    date_text
    date_validateWorkday
    date_deltaWorkDay
    date_addDeltaWorkday
    date_getDeadLineDate
);
use Opals::Locale qw(
    loc_getMsgFile
    loc_write
);

use Opals::BarcodeMgmt qw(
    bcm_validateBc
);
use Opals::HTMLComponents qw(
    hc_getQuickUserEntryFormHTML
);

use JSON;
my $dbh = Opals::Context->dbh();
END { $dbh->disconnect(); }

my $cgi = CGI->new;
my $input = $cgi->Vars();
my ($permission, $cookie, $template) = tmpl_read(
        {
            dbh             => $dbh,
            cgi             => $cgi,
            tmplFile        => 'circ/reserve.tmpl',
            reqPermission   => 'circ_rsrv|circ_rsrv_self',
        }
);
my $syspref = tmpl_preference($dbh);
my $validateBc = $syspref->{'validateBarcode'};
my $barcodeType = $syspref->{'barcodeType'}; 
my $circulationSound = $syspref->{'circulationSound'}; 
my @cookieList = (@$cookie);

my $tm = localtime;
my $todayStr = sprintf("%04d-%02d-%02d", $tm->year+1900, ($tm->mon)+1, $tm->mday);

my ($bc,$rid,@ridArr, @ncrArr,$op, $dateExpiry);
$bc             = $cgi->param('bc');
$op             = $cgi->param('op');
$dateExpiry     = $cgi->param('dExpiry');
@ridArr         = ($cgi->param('rid'));
@ncrArr         = ($cgi->param('ncr'));
my $overRide    = $input->{'overRide'};
$overRide =0 if(!defined $overRide);

if ($permission && ($permission->{'circ_rsrv_self'} || $permission->{'circ_rsrv'})) {
    my ($uid, $reserve);
    # user has permission to reserve for others
    if ($permission->{'circ_rsrv'}) {
        $template->param(circ_rsrv => 1);
        $uid=getUID();
    }
    # self-reserve only
    else {
        $uid = $template->param('curUserId');
    }
  
    if($bc ne '' ){
        $bc    =~ s/^\s+|\s+$//g;
        $bc= bcm_validateBc($dbh,$bc,$barcodeType) if($validateBc eq '1');
        $rid = circ_getRecID($dbh,$bc);
        my $recInfo  = circ_getRecInfo($dbh, $rid, $uid);
        if(!$rid){
            $template->param(reserveError=>1,invalideBc=>1);
        }
        elsif($recInfo->{'reservePeriod'}>0){
                push @ridArr,$rid ;
                push @ncrArr,1;
        }
        else{
           $template->param(
                reserveNotAllow =>1,
                reserveError=>1,
                title        => $recInfo->{'title'},      
                author       => $recInfo->{'author'},
                pubName      => $recInfo->{'pubName'},
                pubDate      => $recInfo->{'pubDate'}
           ); 
        }
    }
  
    # Do operation 
    #
     # Tue, Oct 11, 2011 @ 13:49:34 EDT
     my $numCurReserve = 0;
     foreach my $nc ( @ncrArr){$numCurReserve +=$nc};
     my ($maxreserv, $numOfReserve) = getMaxUserReserve($dbh,$uid);
     my $totalResv = $numOfReserve + $numCurReserve;
              
    my ($reserveExist,$reserveStatus,$reserveExpiry,$numHold,$holdExpiry)=(0,0,"",0,"" );
    #open debug,">/tmp/rr2"; print debug to_json(\@ridArr,{pretty=>1});close debug;
    if($op eq 'placeReserve' && $uid && $uid >= 0 && scalar(@ridArr)){
        if($maxreserv < $totalResv && $overRide ne '1' ){
            $template->param(reserveReachMax =>1,reserveError=>1);
        }
        else {
            my @reserveRsList=();
            my ($reserveMsg1,$reserveMsg2,$reserveMsg3)=(0,0,0);
      

            for(my $i=0; $i < scalar(@ridArr); $i++){
                ($reserveMsg1,$reserveMsg2,$reserveMsg3)=(0,0,0);
                my $recInfo  = circ_getRecInfo($dbh, @ridArr[$i], $uid);
                if(!$recInfo->{'title'} || $recInfo->{'title'} eq ''){
                    next;
                }

                my $idReserve= circ_getReserveID($dbh,@ridArr[$i],$uid);
                if($idReserve) {
                  $reserveExist=1;
                }
                else{
                     if(!$ncrArr[$i] || $ncrArr[$i] eq '' ) {
                         $ncrArr[$i]=1;
                     }
                  
                     ($reserveStatus,$reserveExpiry,$numHold,$holdExpiry) =doReserve($dbh, $uid, $recInfo, $ncrArr[$i],$dateExpiry);
                    
                    if($numHold==0){
                        $reserveMsg1= 1;
                    }
                    elsif($numHold==@ncrArr[$i]){
                        $reserveMsg2= 1 ;
                    }
                    else{
                        $reserveMsg3= 1;
                    }
                }    
                my $msgValMap={
                        reserve_msg_04=>{dueDate=>$holdExpiry},
                        reserve_msg_05=>{noReserve=>@ncrArr[$i]-$numHold,dueDate=>$reserveExpiry},
                        reserve_msg_06=>{noHold=>$numHold,dueDate=>$holdExpiry},
                        };
                my $reserveMsgMap =loc_getMsgFile('circ/reserve.msg',$msgValMap);

                push @reserveRsList, {
                    reserveExist => $reserveExist,
                    reserveFail  =>$reserveStatus?0:1,
                    title        => $recInfo->{'title'},      
                    author       => $recInfo->{'author'},
                    pubName      => $recInfo->{'pubName'},
                    pubDate      => $recInfo->{'pubDate'},
                    numReserve   => $ncrArr[$i]-$numHold,
                    reserveExpiry   => $reserveExpiry,
                    reserveHold  => $numHold?1:0,
                    numHold      => $numHold,
                    holdExpiry   => $holdExpiry,
                    reserveMsg_01=>$reserveMsg1,
                    reserveMsg_02=>$reserveMsg2,
                    reserveMsg_03=>$reserveMsg3,
                    reserve_msg_04=>$reserveMsgMap->{'reserve_msg_04'},
                    reserve_msg_05=>$reserveMsgMap->{'reserve_msg_05'},
                    reserve_msg_06=>$reserveMsgMap->{'reserve_msg_06'},
                    dateExpiry =>$dateExpiry,
                };
            }#for
            $template->param(reserveRsList=> \@reserveRsList ,
                             result => 1
                             );
        }#else
    }
    elsif ($op eq 'cancelReserve') {
        my @cancelRs=();
        for (my $i=0; $i < $input->{'totalid'}; $i++ ) {
                my $idReserve=$input->{'idrsrv' . $i};
                my $reserveInfo = circ_getReserveInfo($dbh,$idReserve );
                if($idReserve>0){
                    my $recInfo  = circ_getRecInfo($dbh, $reserveInfo->{'rid'} );
                    my $userOnholdList =circ_cancelHoldReserve($dbh,$idReserve,$todayStr);
                    $recInfo->{'holdList'}=$userOnholdList;
                    push @cancelRs,$recInfo;
                }
        }

        $template->param(rCancel => 1,cancelRs=>\@cancelRs);
    }
    if (scalar(@ridArr)) {
        my $itemList =get2ReserveItemList($dbh,\@ridArr,\@ncrArr);
        $template->param(itemList =>$itemList);
    }

    my $sortField = 'title';
    if( $input->{'sortField'}) {
       $sortField  = $input->{'sortField'};
    }

    my $sortDir   =  '';
    if( $input->{'sortDir'}){
       $sortDir = "$input->{'sortDir'}";
    };
    $template->param(
       sortField => $sortField,
       sortDir   => $sortDir,
    );


    if($sortDir eq 'desc' ){
        $template->param(sortDown =>1 );
    }
    else{
        $template->param(sortUp =>1 );
    }
  
    if ($uid && $uid>=0) {
        getUserInfo($dbh, $uid,$sortField,$sortDir);
    }
    if ($uid>=0 ) { $template->param(checkid => 1); }
}
else {
    $template->param(
        input => [
            {name => 'rid', value => $ridArr[0],},
            {name => 'op',  value => $op,},
            {name => 'de',  value => $dateExpiry,},
            {name => 'ncr', value => $ncrArr[0]},
        ],
    );
}
   if($circulationSound){
        $template->param(circSound     =>1); 
    }

    my $user_firstname = $template->param('user_firstname');
    my $user_lastname = $template->param('user_lastname');
    my $numReserve    = $template->param('numReserve');
    my $holdExpiry    = $template->param('holdExpiry');
    my $reserveExpiry = $template->param('reserveExpiry');
    my $numHold    = $template->param('numHold');
    my $msgValMap={
                    #reserve_msg_04=>{dueDate=>$holdExpiry},
                    #reserve_msg_05=>{noReserve=>$numReserve,dueDate=>$reserveExpiry},
                    #reserve_msg_06=>{noHold=>$numHold,dueDate=>$holdExpiry},
                    reserve_msg_07=>{user_firstname=>$user_firstname,user_lastname=>$user_lastname},
                    userExpiredConfrmMsg=>{name=>$user_firstname +" " + $user_lastname}
                    };
    my $reserveMsgMap =loc_getMsgFile('circ/reserve.msg',$msgValMap);
    loc_write($template,$reserveMsgMap);

    my $qeUserForm =hc_getQuickUserEntryFormHTML($dbh);
    $template->param(qeUserForm=>$qeUserForm);

    
$template->param(circulation=>1);
$template->param(hlpUrl     => Opals::Constant->getHlpUrl('reserve') );
tmpl_write($dbh, $cgi, \@cookieList, $template);
#########################################################################

sub validateBc{
    my($dbh,$barcode,$barcodeType) =@_;
   if($barcodeType eq '1'){#follett
        $barcode  = Circ_validateFollettBarcode($dbh,$barcode );
    }
    elsif($barcodeType eq '2'){#spectrum
        $barcode  = Circ_validateSpectrumBarcode($dbh,$barcode );
    }
    elsif($barcodeType eq '3'){#leading zero
        $barcode  = Circ_validateLeadingZeroBc($dbh,$barcode );
    }

    #else{#follett
    #    $barcode  = Circ_validateFollettBarcode($dbh,$barcode );
    #}
    return $barcode;
   
}
################################################################################
sub doReserve{
    my($dbh, $uid, $recInfo, $numCopyReserve,$dateExpiry)=@_;
    
    my $rid=$recInfo->{'rid'};
    my $numHold=0;
    my $holdExpiry="";

   my ($errCode, $ck, $resp) = Opals::User::user_currentUser($dbh, $cgi);

    if(!$dateExpiry || $dateExpiry eq''){
        $dateExpiry = date_getDeadLineDate($recInfo->{'reservePeriod'}, $todayStr);
    }

  
    my $reserve = circ_placeReserve($dbh, $uid, $rid, $numCopyReserve, $todayStr, $dateExpiry,$resp->{'uid'} );
    
    if(!$reserve ){
        return (0,$dateExpiry,$numHold,$holdExpiry);
    }
    my $numItemAvailable = circ_numItemsAvailable($dbh,$rid);
    if($numItemAvailable) {
        my $idReserve  = circ_getReserveID($dbh, $rid, $uid);
        $holdExpiry = date_getDeadLineDate($recInfo->{'holdPeriod'}, $todayStr);
        my $loopEnd = $numItemAvailable < $numCopyReserve ? $numItemAvailable:$numCopyReserve;
        $numHold=$loopEnd;
        for(my $i=0; $i<$loopEnd;$i++){
            circ_fillReserve($dbh,$uid,$rid);
            circ_placeHold($dbh, $idReserve,$todayStr,$holdExpiry);
        }
        $dateExpiry=$numHold==$numCopyReserve?"":$dateExpiry;
    }
    $dateExpiry =~ s/\s23:59:59$//g;
    $holdExpiry =~ s/\s23:59:59$//g;
    return (1,$dateExpiry,$numHold,$holdExpiry);
}


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


sub getUID{
    my $uid=-1;
    if ( $input->{'uInput'}) {
        # my @uList = user_list($dbh,  $input->{'uInput'}, USER_ACTIVE);
        my @uList = user_list($dbh,  $input->{'uInput'}, '');
        if(scalar(@uList)==1){
            #$uid = $uList[0]->{'uid'};
           if( @uList[0]->{'status'}==1 && @uList[0]->{'expired'}==0){
               $uid = $uList[0]->{'uid'};
               $template->param(block =>0);
               @uList[0]->{'block'}=  0;
               @uList[0]->{'name'}=  @uList[0]->{'firstname'} + " " + @uList[0]->{'lastname'};
           }
           else {
               $template->param(block =>1);
           }
            $template->param(uPerson => 1);
        }
        elsif(scalar(@uList)>=1){
            for(my $i=0; $i < scalar(@uList) ; $i++){
                @uList[$i]->{'block'}= @uList[$i]->{'status'}==1 ? 0:1;
                @uList[$i]->{'name'}=  @uList[$i]->{'firstname'} + " " + @uList[$i]->{'lastname'};
            }
           # $template->param(uList => \@uList);
        } 
     $template->param(uList => \@uList);
    }
    elsif ( $input->{'InputFromList'}){
        $uid = $input->{'InputFromList'};
    }
    else{
        if ( $input->{'hiddenid'} ) { 
            $uid = $input->{'hiddenid'}; 
        }
        else { $uid = $cgi->cookie('borrower');
        }
    }    return $uid;
}

##########################################################
sub getUID_bk{
    my $uid=-1;
    if ( $input->{'uInput'}) {
        my @uList = user_list($dbh,  $input->{'uInput'}, USER_ACTIVE);
        if(scalar(@uList)==1){
            $uid = $uList[0]->{'uid'};
        }
        elsif(scalar(@uList)>1){
            $template->param(uList => \@uList);
        }
     }
    elsif ( $input->{'InputFromList'}){
        $uid = $input->{'InputFromList'};
    }
    else{
        if ( $input->{'hiddenid'} ) { 
            $uid = $input->{'hiddenid'}; 
        }
        else { $uid = $cgi->cookie('borrower'); }
    }
    return $uid;
}

##########################################################
sub getUserInfo{
    my ($dbh, $uid,$sortField,$sortDir)=@_;

    my ($userInfo, $guardian) = user_getInformationById($dbh, $uid);
    my $balance = user_balance($dbh, $userInfo->{'uid'});
    $balance = floor($balance*100 + 0.5)/100; 
    my $showFine = ($syspref->{'charge_overdue'} || $balance) ? 1:0;
    
    GetReservedItems($dbh, $template, $userInfo->{'uid'},$sortField,$sortDir);
    
    $template->param(
        user_uid        => $userInfo->{'uid'},
        user_barcode    => $userInfo->{'userbarcode'},
        user_username   => $userInfo->{'username'},
        user_firstname  => $userInfo->{'firstname'},
        user_lastname   => $userInfo->{'lastname'},
        balance         => $balance,
        lost            => circ_GetLostNumber($dbh, $userInfo->{'uid'}),
        damaged         => circ_GetDamagedNumber($dbh, $userInfo->{'uid'}),
        hiddenid        => $userInfo->{'uid'},
        showfine        => $showFine,
    );
}


###########################################################
sub GetReservedItems
{
    my ($dbh, $template, $uid,$sortField,$sortDir) = @_;
    
    my $reserveList = circ_userListReserve($dbh, $uid);
    my $order = 0;
    foreach my $reserve (@$reserveList) 
    {
        $reserve->{'order'} = $order++;
        $reserve->{'dateReserve'} = date_text($reserve->{'dateReserve'}, 0);
        $reserve->{'dateExpiry'}  = date_text($reserve->{'dateExpiry'}, 0);
        if($reserve->{'numCopyReserve'}==0){
            $reserve->{'dateExpiry'}  ='';
        }
    }
    @$reserveList = sort {
            if ($sortField eq 'title'){
                 if ($sortDir eq 'desc'){
                      $b->{'titleSort'} <=> $a->{'titleSort'}
                   || lc($b->{'titleSort'}) cmp lc($a->{'titleSort'})
                 }
                 else{
                      $a->{'titleSort'} <=> $b->{'titleSort'}
                   || lc($a->{'titleSort'}) cmp lc($b->{'titleSort'})
                }
            }
    } @$reserveList;
    $template->param(NumOfReserves => scalar(@$reserveList));
    $template->param(reserveList => \@$reserveList);
}


##########################################################
sub getDeadLineDate{
    my ($lengthDay,$dateStr)= @_;
    my $retDate;
    
    my $ddate = date_parse($dateStr);
    $ddate = date_today() unless ($ddate);
    $ddate = date_addDeltaWorkday($ddate, $lengthDay);
    $ddate = date_validateWorkday($ddate,$lengthDay);
    my ($year,$month,$day) = $ddate->date();
    $retDate = sprintf("%04d-%02d-%02d 23:59:59",$year,$month,$day);
    return $retDate;
}

##########################################################
# Mon, Jan 03, 2011 @ 14:47:26 EST

sub get2ReserveItemList{
    my ($db,$ridArr,$ncrArr)=@_;
    my $itemList=[];
    if(scalar(@$ridArr)>0){
        my $ridList=join(",", @$ridArr);
        my $sth=$dbh->prepare(<<_SQL_);
select r.rid, title,author,pubName,pubDate,count(barcode) numItem 
from opl_marcRecord r inner join opl_item using(rid)  
where rid =? group by rid 
_SQL_

        for(my $i=0; $i<scalar(@$ridArr);$i++){
           $sth->execute(@$ridArr[$i]);
           if(my $item=$sth->fetchrow_hashref){
               $item->{"ncr"}=@$ncrArr[$i];
               push @$itemList,$item;
           }
        }
        $sth->finish;
    }
    return $itemList;
    
}


##########################################################
# Tue, Oct 11, 2011 @ 13:42:19 EDT
#
sub getMaxUserReserve{
    my($dbh,$uid)=@_;
    my ($maxreserv, $numOfReserve) =(0,0);

    my $sth =$dbh->prepare("select u.*,c.maxreserv 
        from opl_user as u inner join opl_category as c on u.categorycode=c.catid where u.uid=?");
    $sth->execute($uid);
    my ($rec) =$sth->fetchrow_hashref;
    $sth->finish;
    if($rec){
        $maxreserv = $rec->{'maxreserv'};
    }

    #count number of copies reserved
    $sth =$dbh->prepare( "select sum(numCopyReserve) numOfReserve from opl_reserve where numCopyReserve >=1 && dateExpiry >now()
                         && dateCancel is null  && uid=? group by uid");

    $sth->execute($uid);
    if(my ($countRsv) =$sth->fetchrow_array){
        $numOfReserve +=$countRsv;
    }
    #count number of copies on hold
    $sth =$dbh->prepare( "select count(*) from opl_hold h inner join opl_reserve r using(idReserve)
                         where uid=? && idloan=0 && h.dateExpiry>now() && h.dateCancel is null;");
    $sth->execute($uid);

    if(my ($countHold) =$sth->fetchrow_array){
        $numOfReserve +=$countHold;
    }

    return ($maxreserv, $numOfReserve);
}

