#!/usr/bin/perl
#use utf8;
use strict;
use CGI;

use Opals::Context;
use Opals::Constant;
#use Text::CSV;
use Opals::Template qw(
    tmpl_read
    tmpl_write
    tmpl_preference
);
use Opals::Circulation qw(
    circ_getUserCircStatus
    circ_getItemStatus
    circ_getItemInfo
    circ_getRecInfo
    circ_getRecID
    circ_processLoan
    circ_processReturn
    circ_placeHold
    circ_fillHold
    circ_cancelHold
    circ_cancelReserve
    circ_placeReserve
    circ_fillReserve
    circ_resetReserve
    circ_updateItemStatus
    circ_processRenew
    circ_userListLoan
    circ_userListReserve
    circ_GetLostNumber
    circ_GetDamagedNumber
    circ_lostDeclarating
    Circ_validateFollettBarcode
    Circ_validateSpectrumBarcode
    Circ_validateLeadingZeroBc
    
);

use Opals::Date qw(
    date_parse
    date_today
    date_text
    date_validateWorkday
    date_deltaWorkDay
    date_addDeltaWorkday
);
use Opals::User qw(
    user_login
    user_currentUser
    user_permission
    user_permission_1
    user_category
    user_list
    user_getInformationById
);
use Opals::BarcodeMgmt qw(
    bcm_validateBc
);


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

my $syspref  = tmpl_preference($dbh);


my $cgi = CGI->new;
my $input = $cgi->Vars();

print "Content-type: text/html\n\n";
my $barcode=$input->{'itemBc'};
my $todayStr;
   $barcode  =~ s/^\s|\s$//g;
my $userBc   =$input->{'userBc'};
   $userBc  =~ s/^\s|\s$//g;
my $transDate=$input->{'transDate'};
my $op       =$input->{'op'};

my $sessionID =$input->{'sid'};
my $transNote="";
my $idLoan   =-1;
my $status   =0;

my $uid   =$input->{'uid'};
my $pwd   =$input->{'pwd'};
my $validateBc       = $syspref->{'validateBarcode'}; 
my $barcodeType      = $syspref->{'barcodeType'}; 

if($op eq 'loan'){
    my ($errCode, $ck, $user,$permission);
    if($sessionID && $sessionID ne ""){
       my $app={sessionID=>$sessionID,username=>"",password=>""};
       ($errCode, $ck, $user) = user_currentUser($dbh, undef,$app);
       $permission = user_permission_1($dbh, $user->{'uid'}) if ($errCode==0);

    }
    print $permission->{'circ_loan'} ;   
        
    if($permission->{'circ_loan'}){
        doLoan($dbh,$userBc,$barcode,$transDate);
    }
}

elsif($op eq 'report'){
    print createReport($dbh,$transDate);
}
elsif($op eq 'auth'){
    print authenticate($dbh,$uid,$pwd);
}

#-----------------------------------------------------------------------------------------------------
sub authenticate{
    my($dbh,$uid,$pwd)=@_;
    my $permission;
    my $application={username=>$uid,password=>$pwd};
    my($errCode,$sessionID,$user)=user_login($dbh,undef,$application);
    
    $permission = user_permission_1($dbh, $user->{'uid'}) if ($errCode==0);
    if ($permission && $permission->{'circ_loan'}){
        return $sessionID;
    }
    else{
        return "";
    }
}

#-----------------------------------------------------------------------------------------------------

sub doLoan{
    my($dbh,$userBc,$barcode,$transDate)=@_;
      
    my $uid      =getUID($userBc);
    my $tmpBc=$barcode;
    $barcode    =~ s/^\s+|\s+$//g;
    $barcode = bcm_validateBc($dbh,$barcode,$barcodeType) if($validateBc eq '1');

    if($uid>=0){
        my $userStatus     = circ_getUserCircStatus($dbh,$uid,$barcode);
        my $rid            = circ_getRecID($dbh,$barcode);
        my $itemInfo       = circ_getItemInfo($dbh, $barcode, $uid);
        if($rid){
            my $itemCircStatus = circ_getItemStatus($dbh, $barcode, $uid);
            my $itemMsgCode    = getMsgCode_item($itemCircStatus,$uid);
            print $itemMsgCode;
            if($itemMsgCode eq '00' || $itemMsgCode eq '01' || $itemMsgCode eq '02') {
               $transNote='Invalid barcode or item does not exist.'; 
            }
            elsif( $itemMsgCode eq '01') {
               $transNote='Item is unavailable.'; 
            }
            elsif($itemMsgCode eq '02') {
               $transNote='Item is restricted.'; 
            }
            else{
               my $dateDue = getDeadLineDate($itemInfo->{'loanPeriod'},$transDate);
                  $idLoan = processCmd($dbh, $uid,$barcode,$itemInfo,$itemCircStatus,$itemMsgCode,$dateDue);
                  if($idLoan>0){
                      $status=1;
                  }
                
            }
        }
        else{
           $transNote='Invalid barcode or item does not exist.'; 
        }

    }
    else{
        $transNote='Invalid user barcode.'; 
    }
    logTrans($dbh,$idLoan,$barcode,$userBc,$transDate,$transNote,$status); 
}
#-------------------------------------------------------------------------------
sub createReport{
    my ($dbh,$transDate)=@_;
    my $retStr="";
    my $query = $dbh->prepare("select count(*) as total from  opl_offlineLoan where transDate=? ");
    $query->execute($transDate);
    my $rec =$query->fetchrow_hashref;
    my $totalTrans=$rec->{'total'};
    
    $query = $dbh->prepare("select count(*) as total from  opl_offlineLoan where transDate=? && status=1");
    $query->execute($transDate);
    $rec =$query->fetchrow_hashref;
    my $totalSuccess= $rec->{'total'};
    my $totalfails= $totalTrans - $totalSuccess;
    
    $retStr="
   Total Transactions : $totalTrans
   Success            : $totalSuccess
   Fail               : $totalfails
   ";
   if($totalfails>0){
        $retStr .=" 
   Error Detail:
   +-------------------+--------------------+--------------------------------------------+
   | Item Barcode      | User Barcode       | Error                                      |
   +-------------------+--------------------+--------------------------------------------+
"; 
    $query = $dbh->prepare("select * from  opl_offlineLoan where transDate=? && status=0");
    $query->execute($transDate);
    while ( $rec =$query->fetchrow_hashref) {
             my $itemBc= $rec->{'itemBC'};
                $itemBc= $itemBc . " "x(17 - length($itemBc));
             my $userBc= $rec->{'userBC'} ;
                $userBc= $userBc . " "x(18 - length($userBc));
             my $note  =  $rec->{'note'}  . " "x(42 - length  $rec->{'note'});

             $retStr .= "   | " .  $itemBc . " | " . $userBc  . " | " . $note ." |\n" ;
    }

   $retStr .= "   +-------------------+--------------------+--------------------------------------------+\n" ;
   }
    
    return $retStr;
}

#-------------------------------------------------------------------------------
sub logTrans{
    my($dbh,$idLoan,$itemBc,$userBc,$transDate,$note)=@_;
    my $query = $dbh->prepare("insert into opl_offlineLoan set idLoan=?,itemBC=?,userBC=?,transDate=?,note=?, status=?");
    $query->execute($idLoan,$itemBc,$userBc,$transDate,$note,$status);
    $query->finish;
}
 
#-------------------------------------------------------------------------------
sub getMsgCode_item{
    my ($itemCircStatus,$uid)=@_;
    my $retCode="";
    my $reserveList=$itemCircStatus->{'reserveList'};
    my $holdList=$itemCircStatus->{'holdList'};
    if($itemCircStatus->{'status'}==IT_STAT_NOEXIST){
        $retCode="00";    }
    elsif($itemCircStatus->{'status'}==IT_STAT_UNAVAIL){
        $retCode="01";    }
    elsif($itemCircStatus->{'status'}==IT_STAT_RESTRICTED){
        $retCode="02";    }
    elsif($itemCircStatus->{'status'}==IT_STAT_LOST){
        $retCode="03";    }
    elsif($itemCircStatus->{'status'}==IT_STAT_DAMAGED){
        $retCode="04";    }
    elsif($itemCircStatus->{'status'}==IT_STAT_ONHOLD){
           if(getReserveId($uid,$holdList)>0){
               $retCode="05";}
           elsif(getReserveId($uid,$reserveList)>0){
                $retCode="06";}
           else{
                $retCode="07";}
    }
    elsif($itemCircStatus->{'status'}==IT_STAT_FULL_AVAIL && getReserveId($uid,$holdList)>0 ){
          $retCode="05";
    }    
    elsif($itemCircStatus->{'status'}==IT_STAT_HAVE_RSVR ){
        if(scalar(@$reserveList)){
            my $topReserveUID = @$reserveList[0]->{'uid'};
            if($itemCircStatus->{'inLibrary'}){
                if($uid ==$topReserveUID){
                    $retCode="08";            }
                elsif(getReserveId($uid,$reserveList)>0){
                    $retCode="09";            }
                else{
                    $retCode="10";            }
            }
            else{
                if($uid == $itemCircStatus->{'l_uid'}){
                    $retCode="11";  
                }
                elsif($uid ==$topReserveUID){
                    $retCode="12";            }
                elsif(getReserveId($uid,$reserveList)>0){
                    $retCode="13";            }
                else{
                    $retCode="14";            }
            }
        }
    }
    elsif($itemCircStatus->{'status'}==IT_STAT_ONLOAN && $uid eq $itemCircStatus->{'l_uid'}){
        $retCode="15";    }
    elsif($itemCircStatus->{'status'}==IT_STAT_ONLOAN && $uid ne $itemCircStatus->{'l_uid'}){
        $retCode="16";    }
    else{ # fully available
        $retCode="17";    }
    return $retCode;    
}
#----------------------------------------------------------------------------------
#       this function is mainly used to check if a given uid is in the
#       reserve list or hold list and return its idReserve.
#----------------------------------------------------------------------------------
sub getReserveId{
    my($uid,$list)=@_;
    if(!$list){
        return 0;
    }
    for(my $i=0; $i < scalar(@$list); $i++){
        if(@$list[$i]->{'uid'} == $uid){
            return @$list[$i]->{'idReserve'};
        }
    }
    return 0;
}

#----------------------------------------------------------------------------------
sub getTopReserveId{
    my($list)=@_;
    if(!$list){
        return 0;
    }
   return @$list[0]->{'idReserve'};
}
#----------------------------------------------------------------------------------
sub getLastReserveId{
    my($list)=@_;
    if(!$list){
        return 0;
    }
    my $i =scalar(@$list) -1;
    return @$list[$i]->{'idReserve'};
}
#-------------------------------------------------------------------------------

sub processCmd{
    my ($dbh, $uid,$barcode,$itemInfo,$itemCircStatus,$cmdCode,$dateDue) =@_;
    my $dateReturn =$transDate;
        
    my $retVal=-1; 
    if ($cmdCode eq '03'){
        circ_setAvailable($dbh,$barcode,IT_STAT_LOST);       
    }
    elsif ($cmdCode eq '04'){
        circ_setAvailable($dbh,$barcode,IT_STAT_DAMAGED);       
    }    
    elsif($cmdCode eq '05'){  
        my $idReserve =getReserveId($uid,$itemCircStatus->{'holdList'});
        my $fillHold  = circ_fillHold($dbh,$idReserve,$barcode);
    }
    elsif($cmdCode eq '06'){
        my $idReserve     = getReserveId($uid,$itemCircStatus->{'reserveList'});
        my $fillReserve   = circ_fillReserve($dbh,$uid ,$itemCircStatus->{'rid'});
        my $idReserve     = getLastReserveId($itemCircStatus->{'holdList'});
        my $cancelHold    = circ_cancelHold($dbh,$idReserve ) ;
        my $resetReserve  = circ_resetReserve($dbh,$idReserve);
    }
    elsif($cmdCode eq '07'){
        my $idReserve    = getLastReserveId($itemCircStatus->{'holdList'});
        my $cancelHold   = circ_cancelHold($dbh,$idReserve) ;
        my $resetReserve = circ_resetReserve($dbh,$idReserve);
    }
    elsif($cmdCode eq '08' || $cmdCode eq '09'){
        my $fillReserve  = circ_fillReserve($dbh,$uid ,$itemCircStatus->{'rid'});
    }
    elsif($cmdCode eq '11' || $cmdCode eq '15' || $cmdCode eq '14' || $cmdCode eq '16'){
        my $return       = doReturn($dbh,$itemCircStatus->{'l_uid'}, $barcode,$dateReturn);
    }
 
    elsif($cmdCode eq '12' || $cmdCode eq '13'){
        my $return       = doReturn($dbh,$itemCircStatus->{'l_uid'}, $barcode,$dateReturn);
        my $fillReserve  = circ_fillReserve($dbh,$uid ,$itemCircStatus->{'rid'});
    }

    my $loan = circ_processLoan($dbh,$uid ,$barcode,$transDate,$dateDue);
   
    if($loan){
        #retreive loan id
        my $query= $dbh->prepare("select idLoan from opl_loan where barcode=? && uid=? && dateReturn is null");
        $query->execute($barcode,$uid);
        my $rec =$query->fetchrow_hashref;
        $retVal = $rec->{'idLoan'};    
        $query->finish;
    }

    return  $retVal;
}
#-------------------------------------------------------------------------------
sub doReturn{
    my ($dbh,$uid,$barcode,$dateReturn)=@_;
    
    my $sth = $dbh->prepare("select idloan,finerate, dateDue from opl_loan where uid=? && barcode=? && dateReturn is null");
    $sth->execute($uid,$barcode);
    my $rec = $sth->fetchrow_hashref;
    my $idloan   = $rec->{'idloan'};
    my $finerate   = $rec->{'finerate'};
    my $dateDue    = date_parse($rec->{'dateDue'});
    my $dateRet = date_parse($dateReturn);
    my $NumOfOverdue = date_deltaWorkDay($dateDue, $dateRet);
    my $OverdueFine  = $NumOfOverdue * $finerate;

    # Even overduefine = 0, it is still recorded to keep track how many times
    # user make overdue
    if ($NumOfOverdue)
    {
        $sth = $dbh->prepare("insert into opl_overdue set idloan=?, ondate=now(), amount=?");
        $sth->execute($idloan, $OverdueFine) || return FALSE;
    }

    return  circ_processReturn($dbh,$barcode,$dateReturn);
   
}
#---------------------------------------------------------
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",$year,$month,$day);
    return $retDate;
}
#-------------------------------------------------------------------------------
sub getUID{
    my ($bc)=@_;
    if ($bc) {
        my @uList = user_list($dbh,  $bc, '');
        if(scalar(@uList)==1){
           if( @uList[0]->{'status'}==1 ){
               return $uList[0]->{'uid'};
           }
      }
    }
    return -1;
}

#-------------------------------------------------------------------------------------------
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 );
    }
    return $barcode;
}


