<?php
/**
* @filename        FedEx.php
* @description     This class is for FedEx
* @version         1.0
* @package         MSHShippingLibrary.shippingClasses
* @author          Mohammad Sajjad Hossain - info@sajjadhossain.com
*
* @link            http://sajjadhossain.com
* @created on      January 17, 2011
* @Dependencies
* @license
***/

class FedEx extends Carrier
{
    private $services;

    public function FedEx()
    {
        parent::Carrier();
        $this->setRequestUrl('https://gateway.fedex.com:443/xml');
        $this->setDefaultUserId('362821096'); //FedEx Account Number
        $this->setDefaultCredentialOne('105081613'); //FedEx Meter Number
        $this->setDefaultCredentialTwo('z7VMxcOTNhuKJggH'); //FedEx Key
        $this->setDefaultCredentialThree('5NoENLvdLYZirHTfoKkgGYHn5'); //FedEx Password
        $this->setCacheTableSuffix('fedex');

        /*$defaultServices = array(   'FEDEX_GROUND',
                                    'FEDEX_2_DAY',
                                    'STANDARD_OVERNIGHT',
                                    'INTERNATIONAL_ECONOMY',
                                    'INTERNATIONAL_FIRST',
                                    'INTERNATIONAL_PRIORITY');
        $this->setServices($defaultServices);*/
    }

    public function calculate()
    {
        $this->logMessage('Validating Information for FedEx');
        $this->validateServiceSelected();
        $this->validateFromState();
        $this->validateFromZip();
        $this->validateFromCountyCode();
        $this->validateToCity();
        $this->validateToState();
        $this->validateToZip();
        $this->validateToCountyCode();
        $this->item->validate(array('weight', 'length', 'height', 'width'));

        if($this->isValid())
        {
            $this->setRequestString($this->getXML());
            $this->setHeader('Content-Type', 'text/xml');

            if(!$this->isDefault)
            {
                $this->setRequestUrl('https://gateway.fedex.com:443/xml');
            }

            $this->makeAPICall();
        }
        return $this->response;
    }

    protected function getSearchString()
    {
        $this->searchString = $this->getUserId() . ':'
                                . $this->getFromZip() . ':'
                                . $this->item->getQuantity() . ':'
                                . $this->item->getWeightInKilogram() . ':'
                                . $this->item->getLengthInCentimeter() . ':'
                                . $this->item->getWidthInCentimeter() . ':'
                                . $this->item->getHeightInCentimeter() . ':'
                                . $this->getWeightUnit() . ':'
                                . $this->getLengthUnit() . ':'
                                . $this->getShippingDestinationType();

        if(in_array($this->getToCountryCode(), array('US', 'CA')))
        {
            $this->searchString .= $this->getToStateCode() . ':'
                                . $this->getToCountryCode() . ':'
                                . $this->getToZip();
        }
        else
        {
            $this->searchString .= $this->getToCountryCode();
        }

        return $this->searchString;
    }

    private function getXML()
    {
        $xml = '<?xml version="1.0" encoding="UTF-8"?>
                <RateRequest xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/" xmlns="http://fedex.com/ws/rate/v9" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
                  <WebAuthenticationDetail>
                    <UserCredential>
                      <Key>' . $this->getCredentialTwo() . '</Key>
                      <Password>' . $this->getCredentialThree() . '</Password>
                    </UserCredential>
                  </WebAuthenticationDetail>
                  <ClientDetail>
                    <AccountNumber>' . $this->getUserId() . '</AccountNumber>
                    <MeterNumber>' . $this->getCredentialOne() . '</MeterNumber>
                  </ClientDetail>
                  <TransactionDetail>
                    <CustomerTransactionId>Rate a Single Package V9</CustomerTransactionId>
                  </TransactionDetail>
                  <Version>
                    <ServiceId>crs</ServiceId>
                    <Major>9</Major>
                    <Intermediate>0</Intermediate>
                    <Minor>0</Minor>
                  </Version>
                  <ReturnTransitAndCommit>false</ReturnTransitAndCommit>
                  <RequestedShipment>
                    <ShipTimestamp>' . date('Y-m-d'). 'T' . date('H:i:s') . '-05:00</ShipTimestamp>
                    <DropoffType>REQUEST_COURIER</DropoffType>
                    <PackagingType>YOUR_PACKAGING</PackagingType>
                    <Shipper>
                      <Contact>
                        <PersonName></PersonName>
                        <CompanyName></CompanyName>
                        <PhoneNumber></PhoneNumber>
                      </Contact>
                      <Address>
                        <StreetLines></StreetLines>
                        <City>' . $this->getFromCity(). '</City>
                        <StateOrProvinceCode>' . $this->getFromStateCode() . '</StateOrProvinceCode>
                        <PostalCode>' . $this->getFromZip() . '</PostalCode>
                        <CountryCode>' . $this->getFromCountryCode() . '</CountryCode> 
                      </Address>
                    </Shipper>
                    <Recipient>
                      <Contact>
                        <PersonName></PersonName>
                        <CompanyName></CompanyName>
                        <PhoneNumber></PhoneNumber>
                      </Contact>
                      <Address>
                        <StreetLines></StreetLines>';
        if(in_array($this->getToCountryCode(), array('US', 'CA')))
        {
            $xml .= '<City>' . $this->getToCity(). '</City>
                     <StateOrProvinceCode>' . $this->getToStateCode() . '</StateOrProvinceCode>
                     <PostalCode>' . $this->getToZip() . '</PostalCode>';
        }

         $xml .= '      <CountryCode>' . $this->getToCountryCode() . '</CountryCode>
                       <Residential>'.$this->getShippingDestinationType().'</Residential>
                      </Address>
                    </Recipient>
                    <ShippingChargesPayment>
                      <PaymentType>SENDER</PaymentType>
                      <Payor>
                        <AccountNumber></AccountNumber>
                        <CountryCode>US</CountryCode>
                      </Payor>
                    </ShippingChargesPayment>
                    <RateRequestTypes>ACCOUNT</RateRequestTypes>
                    <PackageCount>' . $this->item->getQuantity() . '</PackageCount>
                    <PackageDetail>INDIVIDUAL_PACKAGES</PackageDetail>
                    ';

        for($i = 1; $i <= $this->item->getQuantity(); $i++)
        {
            $weight = number_format($this->item->getWeightInPound(), 1, '.', '');
            $length = (int) ceil($this->item->getLengthInInch());
            $width = (int) ceil($this->item->getWidthInInch());
            $height = (int) ceil($this->item->getHeightInInch());

            $xml .= '<RequestedPackageLineItems>
                      <SequenceNumber>' . $i . '</SequenceNumber>
                      <Weight>
                        <Units>' . strtoupper($this->getWeightUnit()) . '</Units>
                        <Value>' . $weight . '</Value>
                      </Weight>
                      <Dimensions>
                        <Length>' . $length . '</Length>
                        <Width>' . $width . '</Width>
                        <Height>' . $height . '</Height>
                        <Units>' . strtoupper($this->getLengthUnit()) . '</Units>
                      </Dimensions>
                    </RequestedPackageLineItems>';
        }
        $xml .= '</RequestedShipment>
                </RateRequest>';  
                
        return $xml;
    }

    protected function prepareResponse()
    {
         $rawResponse = str_replace('v9:', '', $this->response->getRawResponse()); //replacing 'v9:' to make simple xml work properly

         $obj = simplexml_load_string($rawResponse);

         $severity = (string) $obj->HighestSeverity;

         if(in_array($severity, array('FAILURE', 'ERROR')))
         {
             $message = '';

             if($obj->Notifications)
             {
                 foreach($obj->Notifications as $notif)
                 {
                     $message .= $notif->Message . ' ';
                 }
             }
             $message = trim($message);

             if(!empty($message))
             {
                 $this->response->setError($message);
             }
         }
         else //WARNING, NOTE, SUCCESS
         {
             if($obj->RateReplyDetails)
             {
                 foreach($obj->RateReplyDetails as $rateDetails)
                 {
                     $amount = 0.00;
                     $serviceType = (string) $rateDetails->ServiceType;

                     if(in_array($serviceType, $this->getServices()))
                     {
                         $service =  $this->getServiceName($serviceType);

                         foreach($rateDetails->RatedShipmentDetails as $shipDetails)
                         {
                             if((string)$shipDetails->ShipmentRateDetail->RateType == (string)$rateDetails->ActualRateType)
                             {
                                 $amount = (string)$shipDetails->ShipmentRateDetail->TotalNetFedExCharge->Amount;
                                 break;
                             }
                         }

                         $this->response->setRate($service, $amount);
                     }
                 }

                 $rates = $this->response->getRates();
                 if(!empty($rates))
                 {
                     $this->insertResponseToDB();
                 }
             }
         }
    }

    private function getServiceName($enum)
    {
        if(empty($this->services))
        {
            $this->services = array(  'EUROPE_FIRST_INTERNATIONAL_PRIORITY' => 'Europe First International Priority',
                                'FEDEX_1_DAY_FREIGHT' => 'FedEx 1 Day Freight',
                                'FEDEX_2_DAY' => 'FedEx 2 Day',
                                'FEDEX_2_DAY_FREIGHT' => 'FedEx 2 Day Freight',
                                'FEDEX_3_DAY_FREIGHT' => 'FedEx 3 Day Freight',
                                'FEDEX_EXPRESS_SAVER' => 'FedEx Express Saver',
                                'FEDEX_FREIGHT' => 'FedEx Freight',
                                'FEDEX_GROUND' => 'FedEx Ground',
                                'FEDEX_NATIONAL_FREIGHT' => 'FedEx National Freight',
                                'FIRST_OVERNIGHT' => 'First Overnight',
                                'GROUND_HOME_DELIVERY' => 'Ground Home Delivery',
                                'INTERNATIONAL_ECONOMY' => 'International Economy',
                                'INTERNATIONAL_ECONOMY_FREIGHT' => 'International Economy Freight',
                                'INTERNATIONAL_FIRST' => 'International First',
                                //'INTERNATIONAL_GROUND' => 'International Ground',
                                'INTERNATIONAL_PRIORITY' => 'International Priority',
                                'INTERNATIONAL_PRIORITY_FREIGHT' => 'International Priority Freight',
                                'PRIORITY_OVERNIGHT' => 'Priority Overnight',
                                'SMART_POST' => 'Smart Post',
                                'STANDARD_OVERNIGHT' => 'Standard Overnight');
        }

        if(!empty($this->services[$enum]))
        {
            return $this->services[$enum];
        }

        return '';
    }
}

//End of file FexEx.php