<?php
include("classes/ISOCountry.php");
class ShippingCalculator  {
	// Defaults
	var $weight = 1;
	var $weight_unit = "lb";
	var $size_length = 4;
	var $size_width = 8;
	var $size_height = 2;
	var $size_unit = "in";
	var $debug = false; // Turn on to see XML sent and recieved

	// Config (you can either set these here or send them in a config array when creating an instance of the class)
	var $services;
	var $from_zip;
	var $from_state;
	var $from_country;
	var $to_zip;
	var $to_stat;
	var $to_country;
	var $ups_access;
	var $ups_user;
	var $ups_pass;
	var $ups_account;
	var $usps_user;
	var $fedex_account;
	var $fedex_meter;

	// Results
	var $rates;

	// Setup Class with Config Options
	function ShippingCalculator($config) {

		if($config) {
			foreach($config as $k => $v) {
				$this->$k = $v;
				//print $this->$k."<br />";
			}
		}

	}

	// Calculate
	function calculate($company = NULL,$code = NULL) {
		$this->rates = NULL;
		$services = $this->services;
		//echo var_dump($services);
		if($company and $code) $services[$company][$code] = 1;
		foreach($services as $company => $codes) {
			foreach($codes as $code => $name) {
				switch($company) {
					case "ups":
						$this->rates[$company][$code] = $this->calculate_ups($code);
						break;
					case "usps":
						$this->rates[$company][$code] = $this->calculate_usps($code);
						break;
					case "fedex":
						$this->rates[$company][$code] = $this->calculate_fedex($code);
						break;
                    case "canadapost":
                        $this->rates[$company][$code] = $this->calculate_canadapost($code);
                        break;
                    case "aussiepost":
                        $this->rates[$company][$code] = $this->calculate_aussiepost($code);
                        break;
				}
			}
		}

		return $this->rates;
	}

	// Calculate UPS
	function calculate_ups($code) {
		$url = "https://www.ups.com/ups.app/xml/Rate";
    	$data = '<?xml version="1.0"?>
<AccessRequest xml:lang="en-US">
	<AccessLicenseNumber>'.$this->ups_access.'</AccessLicenseNumber>
	<UserId>'.$this->ups_user.'</UserId>
	<Password>'.$this->ups_pass.'</Password>
</AccessRequest>
<?xml version="1.0"?>
<RatingServiceSelectionRequest xml:lang="en-US">
	<Request>
		<TransactionReference>
			<CustomerContext>Bare Bones Rate Request</CustomerContext>
			<XpciVersion>1.0001</XpciVersion>
		</TransactionReference>
		<RequestAction>Rate</RequestAction>
		<RequestOption>Rate</RequestOption>
	</Request>
	<PickupType>
		<Code>01</Code>
	</PickupType>
	<Shipment>
		<Shipper>
			<Address>
				<PostalCode>'.$this->from_zip.'</PostalCode>
				<CountryCode>'.$this->from_country.'</CountryCode>
			</Address>
		<ShipperNumber>'.$this->ups_account.'</ShipperNumber>
		</Shipper>
		<ShipTo>
			<Address>
				<PostalCode>'.$this->to_zip.'</PostalCode>
				<CountryCode>'.$this->to_country.'</CountryCode>
			<ResidentialAddressIndicator/>
			</Address>
		</ShipTo>
		<ShipFrom>
			<Address>
				<PostalCode>'.$this->from_zip.'</PostalCode>
				<CountryCode>'.$this->from_country.'</CountryCode>
			</Address>
		</ShipFrom>
		<Service>
			<Code>'.$code.'</Code>
		</Service>
		<Package>
			<PackagingType>
				<Code>02</Code>
			</PackagingType>
			<Dimensions>
				<UnitOfMeasurement>
					<Code>IN</Code>
				</UnitOfMeasurement>
				<Length>'.($this->size_unit != "in" ? $this->convert_sze($this->size_length,$this->size_unit,"in") : $this->size_length).'</Length>
				<Width>'.($this->size_unit != "in" ? $this->convert_sze($this->size_width,$this->size_unit,"in") : $this->size_width).'</Width>
				<Height>'.($this->size_unit != "in" ? $this->convert_sze($this->size_height,$this->size_unit,"in") : $this->size_height).'</Height>
			</Dimensions>
			<PackageWeight>
				<UnitOfMeasurement>
					<Code>LBS</Code>
				</UnitOfMeasurement>
				<Weight>'.($this->weight_unit != "lb" ? $this->convert_weight($this->weight,$this->weight_unit,"lb") : $this->weight).'</Weight>
			</PackageWeight>
		</Package>
	</Shipment>
</RatingServiceSelectionRequest>';

		// Curl
		$results = $this->curl($url,$data);

		// Debug
		if($this->debug == true) {
			print "<xmp>".$data."</xmp><br />";
			print "<xmp>".$results."</xmp><br />";
		}

		// Match Rate
		preg_match('/<MonetaryValue>(.*?)<\/MonetaryValue>/',$results,$rate);

		return $rate[1];
	}

	// Calculate USPS
	function calculate_usps($code) {
		// Weight (in lbs)
		if($this->weight_unit != 'lb') $weight = $this->convert_weight($weight,$this->weight_unit,'lb');
		else $weight = $this->weight;
		// Split into Lbs and Ozs
		$lbs = floor($weight);
		$ozs = ($weight - $lbs)  * 16;
		if($lbs == 0 and $ozs < 1) $ozs = 1;

		$url = "http://Production.ShippingAPIs.com/ShippingAPI.dll";
		//$url = "https://secure.shippingapis.com/ShippingAPITest.dll";
		//$url = "http://testing.shippingapis.com/ShippingAPITest.dll";
		//$data = 'API=RateV2&XML=<RateV2Request USERID="'.$this->usps_user.'"><Package ID="0"><Service>'.$code.'</Service><ZipOrigination>'.$this->from_zip.'</ZipOrigination><ZipDestination>'.$this->to_zip.'</ZipDestination><Pounds>'.$lbs.'</Pounds><Ounces>'.$ozs.'</Ounces><Size>REGULAR</Size><Machinable>TRUE</Machinable></Package></RateV2Request>';

        if($this->to_country=='US')
        {
		//For Production
		$data = 'API=RateV3&XML=<RateV3Request USERID="'.$this->usps_user.'"><Package ID="0"><Service>'.$code.'</Service><ZipOrigination>'.$this->from_zip.'</ZipOrigination><ZipDestination>'.$this->to_zip.'</ZipDestination><Pounds>'.$lbs.'</Pounds><Ounces>'.$ozs.'</Ounces><Size>REGULAR</Size><Machinable>TRUE</Machinable></Package></RateV3Request>';
        }
        else
        {
            $isoCountryIns = new ISOCountry();
            $country=$isoCountryIns->getCountryFullName($this->to_country);
            $data = 'API=IntlRate&XML=<IntlRateRequest USERID="'.$this->usps_user.'"><Package ID="1ST"><Pounds>'.$lbs.'</Pounds><Ounces>'.$ozs.'</Ounces><Machinable>false</Machinable><MailType>Package</MailType><Country>'.$country.'</Country></Package></IntlRateRequest>';
        }

		// Curl
		$results = $this->curl($url,$data);
		//echo "<br/>USPS Source Zip:<br/>".$this->from_zip."";
		//echo "<br/>USPS Response:<br/>".$results."";
		// Debug
		if($this->debug == true) {
			print "<xmp>".$data."</xmp><br />";
			print "<xmp>".$results."</xmp><br />";

		}

		// Match Rate
        if($this->to_country=='US')
        {
		    preg_match('/<Rate>(.+?)<\/Rate>/',$results,$rate);
            return $rate[1];
        }
        else
        {
            //preg_match('/<Postage>(.+?)<\/Postage>/',$results,$rate);
            //service available
            preg_match_all('/<SvcDescription>(.+?)<\/SvcDescription>/',$results,$names);

            preg_match_all('/<Postage>(.+?)<\/Postage>/',$results,$rate);

            $i=0;

            foreach($rate[1] as $rate)
            {
               $result[$names[1][$i]] = $rate;
               $i++;
            }

            return $result[$code];
        }


	}

	// Calculate FedEX
	function calculate_fedex($code) {
		//print_r($this->weight);
		//die();
		$url = "https://gatewaybeta.fedex.com/GatewayDC";
		$data = '<?xml version="1.0" encoding="UTF-8" ?>
<FDXRateRequest xmlns:api="http://www.fedex.com/fsmapi" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="FDXRateRequest.xsd">
	<RequestHeader>
		<CustomerTransactionIdentifier>Express Rate</CustomerTransactionIdentifier>
		<AccountNumber>'.$this->fedex_account.'</AccountNumber>
		<MeterNumber>'.$this->fedex_meter.'</MeterNumber>
		<CarrierCode>'.(in_array($code,array('FEDEXGROUND','GROUNDHOMEDELIVERY')) ? 'FDXG' : 'FDXE').'</CarrierCode>
	</RequestHeader>
	<DropoffType>REGULARPICKUP</DropoffType>
	<Service>'.$code.'</Service>
	<Packaging>YOURPACKAGING</Packaging>
	<WeightUnits>LBS</WeightUnits>
	<Weight>'.number_format(($this->weight_unit != 'lb' ? convert_weight($this->weight,$this->weight_unit,'lb') : $this->weight), 1, '.', '').'</Weight>
	<OriginAddress>
		<StateOrProvinceCode>'.$this->from_state.'</StateOrProvinceCode>
		<PostalCode>'.$this->from_zip.'</PostalCode>
		<CountryCode>'.$this->from_country.'</CountryCode>
	</OriginAddress>
	<DestinationAddress>
		<StateOrProvinceCode>'.$this->to_state.'</StateOrProvinceCode>
		<PostalCode>'.$this->to_zip.'</PostalCode>
		<CountryCode>'.$this->to_country.'</CountryCode>
	</DestinationAddress>
	<Payment>
		<PayorType>SENDER</PayorType>
	</Payment>
	<PackageCount>1</PackageCount>
</FDXRateRequest>';

        //dumpVar($data);
		// Curl
		$results = $this->curl($url,$data);

		// Debug
		if($this->debug == true) {
			print "<xmp>".$data."</xmp><br />";
			print "<xmp>".$results."</xmp><br />";
		}

		// Match Rate
		preg_match('/<NetCharge>(.*?)<\/NetCharge>/',$results,$rate);

		return $rate[1];
	}
    // Calculate Canada Postal Service
    function calculate_canadapost($code)
    {
        if($this->to_state && $this->to_country && $this->to_zip)
        {
           $url = "sellonline.canadapost.ca";

            $data= "
                <?xml version=\"1.0\" ?>
                <eparcel>
                    <language>en</language>
                    <ratesAndServicesRequest>
                        <merchantCPCID>" . $this->canadapost_merchant_cpcid . "</merchantCPCID>
                        <turnAroundTime>24</turnAroundTime>
                        <lineItems>
                            <item>
                                <quantity>1</quantity>
                                <weight>".($this->weight_unit != "kg" ? $this->convert_weight($this->weight,$this->weight_unit,"kg") : $this->weight)."</weight>
                                <length>".($this->size_unit != "cm" ? $this->convert_size($this->size_length,$this->size_unit,"cm") : $this->size_length)."</length>
                                <width>".($this->size_unit != "cm" ? $this->convert_size($this->size_width,$this->size_unit,"cm") : $this->size_width)."</width>
                                <height>".($this->size_unit != "cm" ? $this->convert_size($this->size_height,$this->size_unit,"cm") : $this->size_height)."</height>
                                <description>".$code."</description>
                                <readyToShip />
                            </item>
                        </lineItems>
                        <city>" .$this->to_city . "</city>\n
                        <provOrState>" . getStateFullName($this->to_state, $this->to_country). "</provOrState>\n
                        <country>" .getCountryFullName($this->to_country). "</country>\n
                        <postalCode>" .$this->to_zip. "</postalCode>\n
                    </ratesAndServicesRequest>
                </eparcel>
                ";

            $fp = fsockopen ( $url, $this->canadapost_port, $errno, $errstr, 30);
            if (!$fp) {
                    die("Open Socket Error: $errstr ($errno)<br>\n");
                    $error = true ;
                    $error_msg = $errstr ;
            } else
                fwrite( $fp, $data );
            if (!$fp) return ;
            while(!feof ($fp))
                $xml_response .= fgets( $fp, 4096 );
            fclose($fp);

            //service available
            preg_match_all('/<name>(.+?)<\/name>/',$xml_response,$names);
            preg_match_all('/<rate>(.+?)<\/rate>/',$xml_response,$rate);
            preg_match_all('/<description>(.+?)<\/description>/',$xml_response,$description);

            $i=0;

            foreach($rate[1] as $rate)
            {
                if($names[1][$i] != 'Expedited')
                {
                    $result[$names[1][$i]] = $rate;
                }
                else
                {
                    $result[$description[1][$i]] = $rate;
                }
                $i++;
            }

            return $result[$code];
        }
        else
        {
            return '';
        }
    }
    // Calculate AUSSIE POST
    function calculate_aussiepost($code) {

    $qs='';
    $qs .= 'Height='.($this->size_unit != "mm" ? $this->convert_size($this->size_height,$this->size_unit,"mm") : $this->size_height).'&';
    $qs .= 'Length='.($this->size_unit != "mm" ? $this->convert_size($this->size_length,$this->size_unit,"mm") : $this->size_length).'&';
    $qs .= 'Width='.($this->size_unit != "mm" ? $this->convert_size($this->size_width,$this->size_unit,"mm") : $this->size_width).'&';
    $qs .= 'Weight='.number_format(($this->weight_unit != 'gram' ? $this->convert_weight($this->weight,$this->weight_unit,'gram') : $this->weight), 1, '.', '').'&';
    $qs .= 'Pickup_Postcode='.$this->from_zip.'&';
    $qs .= 'Destination_Postcode='.$this->to_zip.'&';
    $qs .= 'Country='.$this->to_country.'&';
    $qs .= 'Service_Type='.$code.'&';
    $qs .= 'Quantity=1';

    $myfile=file('http://drc.edeliver.com.au/ratecalc.asp?'.$qs);
    $value=explode('=',$myfile[0]);

    if($value[1]==0){
        $value[1]='';
    }

    return trim($value[1]);

    }
	// Curl
	function curl($url,$data = NULL) {
		$ch = curl_init();
		curl_setopt($ch, CURLOPT_URL, $url);
		curl_setopt($ch, CURLOPT_HEADER, 1);
		curl_setopt($ch, CURLOPT_TIMEOUT, 60);
		curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
		curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, 0);
		curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, 0);
		if($data) {
			curl_setopt($ch, CURLOPT_POST,1);
			curl_setopt($ch, CURLOPT_POSTFIELDS,$data);
		}
		curl_setopt($ch, CURLOPT_USERAGENT, $_SERVER['HTTP_USER_AGENT']);
		$contents = curl_exec ($ch);

		return $contents;

		curl_close ($ch);
	}

	// Convert Weight
	function convert_weight($weight,$old_unit,$new_unit) {
		$units['oz'] = 1;
		$units['lb'] = 0.0625;
		$units['gram'] = 28.3495231;
		$units['kg'] = 0.0283495231;

		// Convert to Ounces (if not already)
		if($old_unit != "oz") $weight = $weight / $units[$old_unit];

		// Convert to New Unit
		$weight = $weight * $units[$new_unit];

		// Minimum Weight
		if($weight < .1) $weight = .1;

		// Return New Weight
		return round($weight,2);
	}

	// Convert Size
	function convert_size($size,$old_unit,$new_unit) {
		$units['in'] = 1;
		$units['cm'] = 2.54;
		$units['feet'] = 0.083333;
        $units['mm'] = 25.4;

		// Convert to Inches (if not already)
		if($old_unit != "in") $size = $size / $units[$old_unit];

		// Convert to New Unit
		$size = $size * $units[$new_unit];

		// Minimum Size
		if($size < .1) $size = .1;

		// Return New Size
		return round($size,2);
	}

	// Set Value
	function set_value($k,$v) {
		$this->$k = $v;
	}
}
?>