<?php use Fpdf\Fpdf;

class shipment extends baseobject {
	
	protected static array $fields=array(
		'name'=>validator::REQUIRED,
	    'shipmentdestinationid'=>array(validator::REQUIRED, validator::INTEGER),
	    'idatremotefacility'=>validator::ALPHANUMERIC_PLUS_UNDERSCORE, //but most likely numeric. Just want to catch malicious URI hackery.
	    'urlatremotefacility'=>validator::ANY,
	    'proposalname'=>validator::ANY,
	    'sessionname'=>validator::ANY,
	    'shipperid'=>array(validator::REQUIRED, validator::INTEGER),
	    'dewarbarcodes'=>validator::ANY,
	    'platebarcodes'=>validator::ANY,
	    'dateshipped'=>validator::DATE,
	    'datereturned'=>validator::DATE,
	    'manifest'=>validator::ANY
	);
	
	protected static array $helpTexts=array(
		'name'=>'The name of the shipment',
	    'shipmentdestinationid'=>'Where the shipment is sent',
	    'idatremotefacility'=>'The ID of this shipment at the remote facility',
	    'urlatremotefacility'=>'The URL of this shipment at the remote facility',
	    'proposalname'=>'The proposal ID, as defined by the remote facility',
	    'sessionname'=>'The session ID, as defined by the remote facility',
	    'shipperid'=>'The local person responsible for the shipment'
	);
	
	protected static string $adminSelect='SELECT SQL_CALC_FOUND_ROWS shipment.*, shipmentdestination.name AS shipmentdestinationname,
                    user.fullname as shippername
            FROM shipment, shipmentdestination, user, project
            WHERE shipment.shipmentdestinationid=shipmentdestination.id
                AND shipment.shipperid=user.id
                AND shipment.projectid=project.id
        ';
	protected static string $normalSelect='SELECT SQL_CALC_FOUND_ROWS shipment.*, shipmentdestination.name AS shipmentdestinationname,
                    user.fullname as shippername
            FROM shipment, shipmentdestination, user, project
            WHERE shipment.shipmentdestinationid=shipmentdestination.id
                AND shipment.shipperid=user.id
                AND shipment.projectid=project.id
        ';
	
	public static function getById(int $id): ?array {
	    $shipment=parent::getById($id);
	    if(!empty($shipment['manifest'])){
	        $shipment['manifest']=static::parseAndSanitizeManifest($shipment['manifest']);
	    }
		$shipment['beamlines']=static::getCollectionBeamlines($id);
	    return $shipment;
	}

	public static function getByName(string $name): ?array {
	    $shipment=parent::getByName($name);
	    if(!empty($shipment['manifest'])){
	        $shipment['manifest']=static::parseAndSanitizeManifest($shipment['manifest']);
	    }
		$shipment['beamlines']=static::getCollectionBeamlines($shipment['id']);
	    return $shipment;
	}

	public static function getAll(array $request=[]): ?array {
        $results=parent::getAll($request);
        return static::stripManifestsFromMultipleResults($results);
    }

    public static function getByNameLike(string $searchTerms, array $request=[]): array {
        $results=parent::getByNameLike($searchTerms, $request);
        return static::stripManifestsFromMultipleResults($results);
    }

    public static function getByProperties(array $keyValuePairs, array $request=[]): array {
        $results=parent::getByProperties($keyValuePairs, $request);
        return static::stripManifestsFromMultipleResults($results);
    }

    public static function getByProperty(string $key, string $value, array $request=[]): array {
        $results=parent::getByProperty($key, $value, $request);
        return static::stripManifestsFromMultipleResults($results);
    }
    private static function stripManifestsFromMultipleResults(array $results): array {
        if(empty($results)){ return $results; }
        foreach($results['rows'] as &$row){
            unset($row['manifest']);
        }
        return $results;
    }

	/**
	 * @return bool
	 * @throws BadRequestException
	 * @throws NotFoundException
	 * @throws ServerException
	 */
    public static function canCreate(): bool {
		if(session::isAdmin()){ return true; }
		$grp=usergroup::getByName(usergroup::TECHNICIANS);
		if(usergroup::userisingroup($grp['id'])){ return true; }
		$grp=usergroup::getByName(usergroup::SHIPPERS);
		if(usergroup::userisingroup($grp['id'])){ return true; }
		return false;
	}

	/**
	 * @param $id
	 * @return bool
	 * @throws NotFoundException
	 * @throws BadRequestException
	 * @throws ServerException
	 */
	public static function canUpdate(int $id): bool	{
	    return static::canCreate();
	}

    /**
     * @param array $request
     * @return array
     * @throws BadRequestException
     * @throws ForbiddenException
     * @throws NotFoundException
     * @throws ServerException
     */
	public static function create(array $request=[]): array {
	    $sharedProject=project::getByName(baseproject::SHARED);
		if(!$sharedProject){
			throw new NotFoundException('Could not read shared project');
		}
	    $sharedProjectId=$sharedProject['id'];
	    $request['projectid']=$sharedProjectId;
	    $request['shipperid']=session::getUserId();

	    $containerIds=array();
	    if(!empty($request['dewarbarcodes'])){
	        $codes=explode(',',$request['dewarbarcodes']);
	        foreach($codes as $barcode){
	            $dewar=container::getByName($barcode);
	            if(!$dewar || 'dewar'!=strtolower($dewar['containercategoryname'])){ continue; }
	            $containerIds[]=$dewar['id'];
	        }
	    }
	    if(!empty($request['platebarcodes'])){
	        $codes=explode(',',$request['platebarcodes']);
	        foreach($codes as $barcode){
	            $plate=container::getByName($barcode);
	            if(!$plate){ continue; }
	            $containerIds[]=$plate['id'];
	        }
	        
	    }
	    unset($request['dewarbarcodes']);
	    unset($request['platebarcodes']);
	    
	    $shipment=parent::createByClassName($request,'shipment');
	    if(!empty($containerIds)){
	        $count=1;
	        foreach($containerIds as $cid){
	            containercontent::create(array(
	                'projectid'=>$sharedProjectId,
	                'shipmentid'=>$shipment['created']['id'],
	                'parent'=>$shipment['created']['id'],
	                'child'=>$cid,
	                'position'=>$count
	            ));
	            $count++;
	        }
	    }
	    return $shipment;
	}

	private static function getMxlimsJson(array $shipment): string {
		include_once __DIR__.'/../../vendor/mxlims/ShipmentEncoder/src/v0_6_7ShipmentEncoder.php';
		try {
			$encoder=new mxlims\tools\php\ShipmentEncoder\src\v0_6_7ShipmentEncoder();
			$proteins=[];
			$manifest=$shipment['manifest'];
			if(empty($manifest)){
				throw new BadRequestException('No manifest, cannot generate MXLIMS JSON');
			}
			$sessionNumber=$shipment['sessionname'];
			//DLS session number might be stored as mx4025-1, we need the 1
			$sessionNumber=str_replace($shipment['proposalname'].'-', '', $sessionNumber);
			if(is_numeric($sessionNumber)){
				//Must be an integer
				$sessionNumber=(int)$sessionNumber;
			} else {
				//might be "" or "Mail-in"
				$sessionNumber=null;
			}
			$encoder->createShipment($shipment['proposalname'], $sessionNumber);
			session::becomeAdmin(); //need admin to get email
			$shipper=user::getById($shipment['shipperid']);
			session::revertAdmin();
			$encoder->setLabContactOutbound($shipper['fullname'], $shipper['email']);
			$encoder->setLabContactReturn($shipper['fullname'], $shipper['email']);
			$dewars=$manifest['rows'];
			foreach ($dewars as $dewar){
				$mxlimsDewar=$encoder->addDewarToShipment($dewar['name']);
				$pucks=$dewar['childitems'];
				foreach ($pucks as $puck){
					if(!isset($puck['id'])){ continue; }
					$mxlimsPuck=$encoder->addPuckToDewar($mxlimsDewar['index'], $puck['name'], $puck['positions']);
					$pinPosition=-1;
					foreach ($puck['childitems'] as $pin){
						$pinPosition++;
						if(is_string($pin) || !$pin){
							continue;
						}
						$sample=$pin['childitems'][1];
						$acronym=$sample['proteinacronym'];
						if(!isset($proteins[$acronym])){
							$protein=$encoder->addMacromoleculeToShipment($acronym);
							$proteins[$acronym]=$protein['index'];
						}
						$encoder->addSinglePinSampleToPuck($mxlimsPuck['index'], $pinPosition, $proteins[$acronym], $sample['name'], $pin['name']??null);
					}
				}
			}
			return $encoder->encodeToJSON(true);
		} catch (Exception $exception){
			throw new ServerException('MXLIMS shipment encoder threw exception: '.$exception->getMessage());
		}
	}

	/**
	 * @param int $id
	 * @param array $request
	 * @return array
	 * @throws BadRequestException
	 * @throws ForbiddenException
	 * @throws NotFoundException
	 * @throws ServerException
	 */
	public static function update(int $id, array $request=[]): array {
	    $shipment=static::getById($id);
	    if(!$shipment){ throw new NotFoundException('No shipment with ID '.$id); }
		if(isset($request['getMxlims'])){
			return ["json"=>static::getMxlimsJson($shipment)];
		}
		$hasShipped=!empty($shipment['dateshipped']);
        if(isset($request['shipmentdestinationid'])){
	        if($hasShipped){ throw new ForbiddenException('Cannot update destination. Shipment has shipped.'); }
	        $destination=shipmentdestination::getById($request['shipmentdestinationid']);
	        if(!$destination){ throw new NotFoundException('No shipment destination with ID '.$request['shipmentdestinationid']); }
	    }
	   
	    $shipmentMessage=null;
	    
	    
	    if(isset($request['dateshipped']) && empty($shipment['dateshipped'])){
       
	        //update all containercontent where shipment is parent, set shipmentid, recursively through all children
	        $affected=1;
	        $passCount=0;
	        while($affected>0){
	            $items=database::queryGetAll(
	                   'SELECT child FROM containercontent WHERE shipmentid=:shipmentid', 
	                   array(':shipmentid'=>$shipment['id'])
	            );
	            if(!isset($items['rows'])) {
                    //This can happen if shipment was sent before and shipped date was nulled out for a re-send.
                    //Likely only in dev/test, but someone might do it in production...
                    $affected=0;
                } else {
	                $children=array_column($items['rows'],'child');
	                database::query(
	                     'UPDATE containercontent SET shipmentid=:shipmentid WHERE parent IN('. implode(',',$children) .') AND shipmentid IS NULL',
	                       array(':shipmentid'=>$shipment['id'])
	                );
	                $affected=database::getAffectedRows();
	            }
	        }
	        //prepare shipment::getcontainers() into shipment.manifest as unsanitized JSON - will be sanitized on return to client
	        $wasAdmin=session::isAdmin();
	        session::set('isAdmin',true);
	        $manifest=static::getcontainers($shipment['id']);
	        session::set('isAdmin',$wasAdmin);
	        $request['manifest']=json_encode($manifest);
	        
	        parent::update($id,$request);
	        static::generateManifestPdf($shipment['id'], true);
	        
	    }
            	    
        if(isset($request['datereturned']) && !empty($shipment['dateshipped'])){

	        //Unpack dewars from shipment, shipment from synchrotron
	        $result=database::queryGetAll(
	            'SELECT id FROM containercontent WHERE parent=:shipmentid1 OR child=:shipmentid2',
	            array(':shipmentid1'=>$id, ':shipmentid2'=>$id)
	        );
	        foreach($result['rows'] as $row){
	            database::query(
	                'DELETE FROM baseobject WHERE id=:id',
	                array(':id'=>$row['id'])
	            );
	        }
        	        
            //Disassociate all containers from shipment, ready for unpacking.
	        database::query(
	            'UPDATE containercontent SET shipmentid=NULL WHERE shipmentid=:shipmentid',
	            array(':shipmentid'=>$shipment['id'])
            );
        }

        $updated=parent::update($id,$request);
        $response=array('updated'=>$updated['updated']);
        if(!empty($shipmentMessage)){ $response['message']=$shipmentMessage; }
        return $response;
	}

	/**
	 * @param int $id
	 * @param array $request
	 * @return array|mixed
	 * @throws BadRequestException
	 * @throws NotFoundException
	 * @throws ServerException
	 */
	public static function getcontainers(int $id, array $request=[]): ?array {
	    $shipment=static::getById($id);
	    if(!empty($shipment['manifest'])){
	        return $shipment['manifest'];
	    }
	    try {
            if(session::isShipper()){ session::becomeAdmin(); }
            $contents=containercontent::getByProperties(array('parent'=>$id));
            if(!$contents){ throw new NotFoundException('No containers in shipment'); }
            $containers=container::getContentsRecursively($shipment);
        } catch (Exception $e){
            session::revertAdmin();
	        throw $e;
        }
        session::revertAdmin();
	    $ret=array('total'=>0, 'rows'=>array());
	    foreach($containers as $c){
	        if(is_array($c)){
	            $ret['rows'][]=$c;
	            $ret['total']++;
	        }
	    }
	    return $ret;
	}

	private static function getCollectionBeamlines(int $id): array  {
		$beamlines=database::queryGetAll('SELECT DISTINCT bl.id AS id, bl.name AS name
				FROM diffractionrequest AS dr,dataset AS ds, beamline AS bl 
				WHERE dr.shipmentid=:shipmentid AND ds.diffractionrequestid=dr.id AND ds.beamlineid=bl.id			
		',[':shipmentid'=>$id]);
		if(!$beamlines){ return []; }
		return $beamlines['rows'];
	}

    /**
     * Takes a JSON encoding of the shipping manifest and unpacks it into an associative array.
     * The manifest will have been generated with administrator permissions and therefore may contain confidential
     * information. If the user is not an administrator, hides information that the user does not have permission to see.
     * @param $manifest
     * @return array
     */
	private static function parseAndSanitizeManifest(string $manifest): array {
		//TODO Replace anything where projectid is not in read-projects with (hidden)
        //if(!session::isAdmin()){
        //}
	    return json_decode($manifest,true);
	}

	/**
	 * Allows for retrieving manifest PDF over HTTP via API call shipment/NNN/manifestpdf
	 * @param int $id
	 * @param array $request
	 * @throws BadRequestException
	 * @throws ForbiddenException
	 * @throws NotFoundException
	 * @throws ServerException
	 */
	public static function getmanifestpdfs(int $id, array $request=[]): void {
	    static::generateManifestPdf($id);
	}


	/**
	 * Uses the third-party FPDF library to generate a PDF containing the contents of the shipment, suitable for
	 * note-taking during the data collection.
	 * @param int $id
	 * @param bool $saveAsIceBearFile
	 * @return void the saved file ID if saving to file, otherwise streams the PDF to the browser.
	 * @throws BadRequestException
	 * @throws ForbiddenException
	 * @throws NotFoundException
	 * @throws ServerException
	 */
	private static function generateManifestPdf(int $id, bool $saveAsIceBearFile=false): void {
	    $shipment=static::getById($id);
	    if(!$shipment){
	        throw new NotFoundException('No shipment with ID '.$id);
	    }
	    $shipmentDestination=shipmentdestination::getById($shipment['shipmentdestinationid']);
	    $manifest=$shipment['manifest'];
	    if(!$manifest){
	        throw new BadRequestException('Shipment has not been sent, or manifest was not generated on sending');
	    }
	    
	    $margin=10; //mm
	    $headerHeight=10; //mm
	    
	    $qrWidth=18;
	    
	    $pdf=new Fpdf();
	    $pdf->SetTitle('IceBear shipment manifest');
	    
	    $pdf->SetFillColor(192,192,192);
	    $pdf->SetFont('Arial','',12);

	    //Header page
	    $pdf->AddPage();
	    $pageWidth=$pdf->GetPageWidth();
	    $pageHeight=$pdf->GetPageHeight();
        $headerMargin=5;
        $firstHeaderHeight=20; //mm
        $contentHeight=$pageHeight-$headerHeight-$headerMargin-(2*$margin)-12; //Needs 12mm to fit last pin on each page, no idea why
	    
	    $pdf->Image(config::getWwwRoot().'/client/images/icebearlogo.png',$margin,$margin,0,$firstHeaderHeight);
	    $pdf->SetXY($margin+15, $margin);
	    $pdf->Cell(0,$firstHeaderHeight,'IceBear crystal shipment manifest',1,1,'C',true);

	    $pdf->SetXY($margin, $margin+90);
	    $pdf->Cell(0,20,'Shipment: '.$shipment['name'],0,1,'C');
	    $pdf->Cell(0,20,'To: '.$shipmentDestination['name'],0,1,'C');
	    $pdf->Cell(0,20,'Shipped: '.$shipment['dateshipped'],0,1,'C');
	    
	    $localUrl=(isset($_SERVER['HTTPS']) && $_SERVER['HTTPS']==='on'?'https://':'http://').$_SERVER['HTTP_HOST'].'/shipment/'.$shipment['id'];
	    $remoteUrl=$shipment['urlatremotefacility'];
	    
	    $localQr=false;
	    $remoteQr=false;
	    $files=shipment::getFiles($id);
	    if($files){
    	    $files=$files['rows'];
    	    foreach($files as $f){
    	        if('qrlocal.png'==$f['filename']){
    	            $localQr=$f;
    	        } else if('qrremote.png'==$f['filename']){
    	            $remoteQr=$f;
    	        }
    	    }
	    }
	    
	    $canGenerateQrs=baseobject::canMakeQrCodes();
	    
	    if($canGenerateQrs){
    	    if(!$localQr){
    	        $localQr=static::makeQrCode($id, $localUrl, 'qrlocal.png', 'QR link to shipment in IceBear');
    	        $localQr=$localQr['created'];
       	    }
    	    if(!$remoteQr && ''!=$remoteUrl){
    	        $remoteQr=static::makeQrCode($id, $remoteUrl, 'qrremote.png', 'QR link to shipment at '.$shipmentDestination['name']);
    	        $remoteQr=$remoteQr['created'];
    	    }
    	    $tempLocal=rtrim(config::get('core_filestore'),'/').'/ship_'.$id.'localqr.png';
    	    $tempRemote=rtrim(config::get('core_filestore'),'/').'/ship_'.$id.'remoteqr.png';
    	    if(!@copy(file::getPathToFile($localQr),$tempLocal)){
    	        throw new ServerException('Could not copy local qr back into temp for PDF generation');
    	    }
    	    if($remoteQr && !@copy(file::getPathToFile($remoteQr),$tempRemote)){
    	        throw new ServerException('Could not copy remote qr back into temp for PDF generation');
    	    }
    	    
    	    $pdf->SetFont('Arial','',10);
    	    
    	    $pdf->SetXY($margin, $margin+$firstHeaderHeight+5);
    	    $pdf->MultiCell(($pageWidth/2-$margin), 5, "IceBear:\n".$localUrl, 0, 'L');
    	    $pdf->Image(
    	        rtrim(config::get('core_filestore'),'/').'/ship_'.$id.'localqr.png' ,
    	        $margin, 
    	        $margin+$firstHeaderHeight+15,
    	        $qrWidth,
    	        $qrWidth,
    	        '',
    	        $localUrl
    	    );
    	    
    	    $pdf->SetXY($pageWidth/2, $margin+$firstHeaderHeight+5);
    	    if($remoteQr){
        	    $pdf->MultiCell(($pageWidth/2-$margin), 5, $shipmentDestination['name'].":\n".$remoteUrl, 0, 'R');
        	    $pdf->Image(
        	        rtrim(config::get('core_filestore'),'/').'/ship_'.$id.'remoteqr.png',
        	        $pageWidth-20-$margin, 
        	        $margin+$firstHeaderHeight+15,
        	        $qrWidth,
        	        $qrWidth,
        	        '',
        	        $remoteUrl
        	    );
    	    } else {
    	        $pdf->MultiCell(($pageWidth/2-$margin), 5, $shipmentDestination['name'].":\n".'(Remote URL not available)', 0, 'R');
    	    }
    	    $pdf->SetFont('Arial','',12);
    	    
    	    @unlink($tempLocal);
    	    @unlink($tempRemote);
	    }
	    
	    
	    foreach($manifest['rows'] as $dewar){
	        
	        foreach($dewar['childitems'] as $puck){
	            if("dummy"==$puck||""==$puck){ continue; }
	            $count=100; //Well over pins-per-page max, triggers page add on first pass through loop below
	            $positions=count($puck['childitems'])-1;
	            $perPage=$positions/2;
	            if($perPage>8){ $perPage=6; }
	            $pinHeight=$contentHeight/$perPage;
	            for($i=1;$i<=$positions;$i++){
	                $pdf->SetFont('Arial','',12);
	                if($count>=$perPage){
	                    $count=0;
	                    $pdf->AddPage();
	                    $pdf->Cell(0,$headerHeight,'Dewar '.$dewar['name'].', puck '.$puck['name'],1,1,'C',true);
	                }
                    $pinOriginX=$margin;
                    $pinOriginY=$margin+$headerHeight+$headerMargin+($count*$pinHeight);
                    $pdf->SetXY($pinOriginX, $pinOriginY);
                    $pdf->Cell(10,$pinHeight,$i,1,1,'C',true);
                    
	                $pin=$puck['childitems'][$i];
	                $crystal=$pin['childitems'][1];
	                
  	                if(""==$pin){ 
  	                    $pdf->SetXY($pinOriginX+10, $pinOriginY);
  	                    $pdf->Cell(0,$pinHeight,'(No pin in this position)',1,1,'C');
  	                    $count++;
  	                    continue; 
  	                } 
 	            
 	                //Pin barcode
  	                $pdf->SetXY($pinOriginX+10, $pinOriginY);
  	                $pinBarcode=$pin['name'];
  	                if(stripos($pinBarcode, 'dummypin')===0){
  	                    $pinBarcode='-';
  	                }
 	                $pdf->Cell(35,7,$pinBarcode,1,1,'C');
 	                //Sample name cell
 	                $pdf->SetXY($pinOriginX+45, $pinOriginY);
 	                $pdf->Cell(0,7,$crystal['name'],1,1,'C');
 	                //Main cell border
 	                $mainCellWidth=$pageWidth-(2*$margin)-10; //number cell is 10mm wide
 	                $pdf->SetXY($pinOriginX+10, $pinOriginY+7);
 	                $pdf->Cell(0,$pinHeight-7,'',1,1,'C');
 	                 	                                
 	                //shipping comment
 	                $pdf->SetXY($pinOriginX+10+$qrWidth, $pinOriginY+12);
 	                $pdf->SetFont('Arial','',10);
 	                $pdf->MultiCell($mainCellWidth-(2*$qrWidth), 5, $crystal['shippingcomment'], 0, 'L');
 	                $pdf->SetFont('Arial','',12);
 	                
 	                $localPinUrl=(isset($_SERVER['HTTPS']) && $_SERVER['HTTPS']==='on'?'https://':'http://').$_SERVER['HTTP_HOST'].'/crystal/'.$crystal['id'];
 	                $remotePinUrl='';
 	                if(isset($crystal['crystalurlatremotefacility'])){
 	                    $remotePinUrl=$crystal['crystalurlatremotefacility'];
 	                }
 	                
 	                $localPinQr=false;
 	                $remotePinQr=false;
 	                
 	                if($canGenerateQrs){
 	                    $files=crystal::getFiles($crystal['id']);
 	                    if($files){
 	                        $files=$files['rows'];
 	                        foreach($files as $f){
 	                            if('qrlocal.png'==$f['filename']){
 	                                $localPinQr=$f;
 	                            } else if('qrremote.png'==$f['filename']){
 	                                $remotePinQr=$f;
 	                            }
 	                        }
 	                    }
 	                    
 	                    if(!$localPinQr){
 	                        $localPinQr=static::makeQrCode($crystal['id'], $localPinUrl, 'qrlocal.png', 'QR link to crystal in IceBear');
 	                        $localPinQr=$localPinQr['created'];
 	                    }
 	                    if(!$remotePinQr && ''!=$remotePinUrl){
 	                        $remotePinQr=static::makeQrCode($crystal['id'], $remotePinUrl, 'qrremote.png', 'QR link to crystal at '.$shipmentDestination['name']);
 	                        $remotePinQr=$remotePinQr['created'];
 	                    }
 	                    $tempLocal=rtrim(config::get('core_filestore'),'/').'/xtal_'.$crystal['id'].'localqr.png';
 	                    $tempRemote=rtrim(config::get('core_filestore'),'/').'/xtal_'.$crystal['id'].'remoteqr.png';
 	                    if(!@copy(file::getPathToFile($localPinQr),$tempLocal)){
 	                        throw new ServerException('Could not copy local qr back into temp for PDF generation');
 	                    }
 	                    if($remotePinQr && !@copy(file::getPathToFile($remotePinQr),$tempRemote)){
 	                        throw new ServerException('Could not copy remote qr back into temp for PDF generation');
 	                    }
 	                    
 	                    $pdf->Image($tempLocal, $pinOriginX+10, $pinOriginY+10, $qrWidth, $qrWidth, '', $localPinUrl);
 	                    
 	                    $pdf->SetXY($pageWidth/2, $margin+$firstHeaderHeight+3);
 	                    if($remotePinQr){
 	                        $pdf->Image($tempRemote, $pageWidth-20-$margin,  $pinOriginY+10, $qrWidth, $qrWidth, '', $remotePinUrl);
 	                    }
 	                    
     	                $pdf->SetFont('Arial','I',10);
     	                $pdf->SetXY($pinOriginX+10, $pinOriginY+7);
     	                $pdf->Cell(0,5,'IceBear',0,1,'L');
     	                if($remotePinQr){
         	                $pdf->SetXY($pinOriginX+10, $pinOriginY+7);
         	                $pdf->Cell(0,5,$shipmentDestination['name'],0,1,'R');
     	                }
     	                $pdf->SetFont('Arial','',12);
     	                @unlink($tempLocal);
     	                @unlink($tempRemote);
 	                }
 	                
 	                
 	                $count++;
	            }
	        }
        }
	    
	    if($saveAsIceBearFile){
	        $tempPath=rtrim(config::get('core_filestore'),'/').'/ship_'.$id.'manifest.pdf';
	        $pdf->Output('F',$tempPath);
	        
	        session::becomeAdmin();
	        $file=file::createFromTempFile(array(
	            'parentid'=>$id,
	            'mimetype'=>'application/pdf',
	            'filename'=>'shipment'.$id.'manifest.pdf',
	            'description'=>'Shipment manifest',
	            'tempfile'=>$tempPath
	        ));
	        $file=$file['created'];
	        session::revertAdmin();
		} else {
	        $pdf->Output();
	        database::commit();
	        exit();
	    }
	    
	}

	/**
	 * @throws BadRequestException
	 * @throws ServerException
	 */
	public static function setShipperAdmin(): void {
		if (!session::isAdmin() && session::isShipper()){
			session::becomeAdmin();
		}
	}

	/**
	 * @throws BadRequestException
	 * @throws ServerException
	 */
	public static function revertShipperAdmin(): void {
		session::revertAdmin();
	}

}
