<?php
/**
 * @package	HikaShop for Joomla!
 * @version	3.4.1
 * @author	hikashop.com
 * @copyright	(C) 2010-2018 HIKARI SOFTWARE. All rights reserved.
 * @license	GNU/GPLv3 http://www.gnu.org/licenses/gpl-3.0.html
 */
defined('_JEXEC') or die('Restricted access');
?><?php
class plgHikashopshippingUSPS extends hikashopShippingPlugin
{
	var $multiple = true;
	var $name = 'usps';
	var $doc_form = 'usps';
	var $pluginConfig = array(
		'usps_user_id' => array('USPS WebTools User ID', 'input'),
		'post_code' => array('POST_CODE', 'input'),
		'services' => array('SHIPPING_SERVICES', 'checkbox',array(
			'PRIORITY' => 'Priority Mail',
			'MEDIA' => 'Media Mail',
			'EXPRESS' => 'Express Mail',
			'EXPRESSINT' => 'Priority Mail Express International (International)',
			'EXPRESSINTBOX' => 'Priority Mail Express International Flat Rate Boxes (International)',
			'FIRST CLASS' => 'First Class Mail',
			'FIRSTCLASSINT' => 'First Class Mail (International)',
			'INTERNATIONAL' => 'Priority Mail International (International)',
			'PRIORITYINTSMALL' => 'Priority Mail International Small Flat Rate Box (International)',
			'PRIORITYINTDVD' => 'Priority Mail International DVD Flat Rate priced box (International)',
			'PRIORITYINTLARGEVIDEO' => 'Priority Mail International Large Video Flat Rate priced box (International)',
			'PRIORITYINTMEDIUM' => 'Priority Mail International Medium Flat Rate Box (International)',
			'PRIORITYINTLARGE' => 'Priority Mail International Large Flat Rate Box (International)',
			'ENVELOPE' => 'USPS GXG Envelopes (International)',
		)),
		'weight_approximation' => array('Weight approximation (%)', 'input'),
		'dim_approximation' => array('Dimension approximation (%)', 'input'),
		'machinableCheck' => array(
			'Machinable Shipments', 'checkbox', array(
				'MACHINABLE' => 'Are most of your packages <a href="http://pe.usps.com/text/dmm300/101.htm">machinable</a>?',
			)
		),
		'sort_by_price' => array('Sort by shipping price', 'list',array(
			'NO' => 'No',
			'ASC' => 'Ascending',
			'DESC' => 'Descending',
		)),
		'with_dimension' => array('Use product dimension', 'boolean'),
		'firstclass_mail_type' => array('First class mail type', 'list',array(
			'PARCEL' => 'Parcel',
			'LETTER' => 'Letter',
			'FLAT' => 'Flat',
			'POSTCARD' => 'Postcard',
			'PACKAGE SERVICE' => 'Package service',
		)),
		'container' => array('Container', 'list',array(
			'VARIABLE' => 'Variable',
			'FLAT RATE ENVELOPE' => 'Flat rate envelope',
			'PADDED FLAT RATE ENVELOPE' => 'Padded flat rate envelope',
			'LEGAL FLAT RATE ENVELOPE' => 'Legal flat rate envelope',
			'SM FLAT RATE ENVELOPE' => 'Small flat rate envelope',
			'WINDOW FLAT RATE ENVELOPE' => 'Window flat rate envelope',
			'GIFT CARD FLAT RATE ENVELOPE' => 'Gift cart flat rate envelope',
			'FLAT RATE BOX' => 'Flat rate box',
			'SM FLAT RATE BOX' => 'Small flat rate box',
			'MD FLAT RATE BOX' => 'Medium flat rate box',
			'LG FLAT RATE BOX' => 'Large flat rate box',
			'RECTANGULAR' => 'Rectangular',
			'NONRECTANGULAR' => 'Non rectangular',
		))
		,'debug' => array('debug', 'boolean')
	);
	var $methods = array(
		'PRIORITY' => 1,
		'PRIORITYINTSMALL' => 2,
		'PRIORITYINTDVD' => 3,
		'PRIORITYINTLARGEVIDEO' => 4,
		'PRIORITYINTMEDIUM' => 5,
		'PRIORITYINTLARGE' => 6,
		'EXPRESS' => 7,
		'EXPRESSINT' => 8,
		'EXPRESSINTBOX' => 9,
		'FIRST CLASS' => 11,
		'FIRSTCLASSINT' => 12,
		'INTERNATIONAL' => 13,
		'ENVELOPE' => 14,
		'MEDIA' => 15,
	);

	var $use_cache = true;

	function shippingMethods(&$main){
		$methods = array();
		if(empty($main->shipping_params->services))
			return $methods;

		foreach($main->shipping_params->services as $service) {
			$selected = null;
			foreach($this->methods as $name => $key) {
				if($name == $service) {
					$selected = array('name' => $this->pluginConfig['services'][2][$name], 'key' => $key);
					break;
				}
			}
			if($selected) {
				$methods[$main->shipping_id . '-' . $selected['key']] = $selected['name'];
			}
		}
		return $methods;
	}

	function onShippingDisplay(&$order, &$dbrates, &$usable_rates, &$messages) {
		if(!hikashop_loadUser())
			return false;

		if($this->loadShippingCache($order, $usable_rates, $messages))
			return true;

		$local_usable_rates = array();
		$local_messages = array();
		$ret = parent::onShippingDisplay($order, $dbrates, $local_usable_rates, $local_messages);
		if($ret === false)
			return false;

		if(!function_exists('curl_init')) {
			$app = JFactory::getApplication();
			$app->enqueueMessage('The USPS shipping plugin needs the CURL library installed but it seems that it is not available on your server. Please contact your web hosting to set it up.','error');
			return false;
		}

		$cache_rates = array();
		$cache_messages = array();
		$found = false;

		$currentShippingZone = null;
		$currentCurrencyId = null;
		$currencyClass = hikashop_get('class.currency');
		foreach($local_usable_rates as $rate) {
			if($rate->shipping_type != 'usps')
				continue;
			if(empty($order->shipping_address))
				continue;

			$found = true;
			$zones = null;

			if(!empty($rate->shipping_zone_namekey)) {
				if(empty($zones)) {
					$zoneClass = hikashop_get('class.zone');
					$zones = $zoneClass->getOrderZones($order);
				}

				if(!in_array($rate->shipping_zone_namekey,$zones)) {
					$messages['no_shipping_to_your_zone'] = JText::_('NO_SHIPPING_TO_YOUR_ZONE');
					continue;
				}
			}

			$check = false;
			if(empty($order->shipping_address->address_post_code)) {
				$check = true;
				$message = 'The USPS shipping plugin requires the user to enter a postal code when goods are shipped within the United States. Please go to "Display->Custom fields" and set the post code field to required.';
			} elseif(!preg_match('#^[0-9]{5}(-?[0-9]{4})?$#',$order->shipping_address->address_post_code)) {
				$check = true;
				$message = 'The post code entered is not valid';
			}

			if($check) {
				if(empty($zones)) {
					$zoneClass = hikashop_get('class.zone');
					$zones = $zoneClass->getOrderZones($order);
				}

				$db = JFactory::getDBO();
				$db->setQuery('SELECT zone_namekey FROM '.hikashop_table('zone').' WHERE zone_code_3='.$db->Quote('USA'));
				$usa_zone = $db->loadResult();
				if(in_array($usa_zone, $zones)) {
					$messages['post_code_missing'] = $message;
					continue;
				}
			}

			foreach($order->products as $product) {
				if (!empty($rate->shipping_params->weight_approximation)) {
					if (isset($product->product_weight)) $product->product_weight = $product->product_weight + $product->product_weight * $rate->shipping_params->weight_approximation / 100;
					if (isset($product->product_weight_orig)) $product->product_weight_orig = $product->product_weight_orig + $product->product_weight_orig * $rate->shipping_params->weight_approximation / 100;
				}
				if (!empty($rate->shipping_params->dim_approximation)) {
					if (isset($product->product_height)) $product->product_height = $product->product_height + $product->product_height * $rate->shipping_params->dim_approximation / 100;
					if (isset($product->product_length)) $product->product_length = $product->product_length + $product->product_length * $rate->shipping_params->dim_approximation / 100;
					if (isset($product->product_width)) $product->product_width = $product->product_width + $product->product_width * $rate->shipping_params->dim_approximation / 100;
					if (isset($product->product_total_volume)) $product->product_total_volume = $product->product_total_volume + $product->product_total_volume * $rate->shipping_params->dim_approximation / 100;
					if (isset($product->product_total_volume_orig)) $product->product_total_volume_orig = $product->product_total_volume_orig + $product->product_total_volume_orig * $rate->shipping_params->dim_approximation / 100;
				}
				if (isset($product->variants)) {
					foreach($product->variants as $variant) {
						if (!empty($rate->shipping_params->weight_approximation)) {
							if (isset($variant->product_weight)) $variant->product_weight = $variant->product_weight + $variant->product_weight * $rate->shipping_params->weight_approximation / 100;
							if (isset($variant->product_weight_orig)) $variant->product_weight_orig = $variant->product_weight_orig + $variant->product_weight_orig * $rate->shipping_params->weight_approximation / 100;
						}
						if (!empty($rate->shipping_params->dim_approximation)) {
							if (isset($variant->product_height)) $variant->product_height = $variant->product_height + $variant->product_height * $rate->shipping_params->dim_approximation / 100;
							if (isset($variant->product_length)) $variant->product_length = $variant->product_length + $variant->product_length * $rate->shipping_params->dim_approximation / 100;
							if (isset($variant->product_width)) $variant->product_width = $variant->product_width + $variant->product_width * $rate->shipping_params->dim_approximation / 100;
							if (isset($variant->product_total_volume)) $variant->product_total_volume = $variant->product_total_volume + $variant->product_total_volume * $rate->shipping_params->dim_approximation / 100;
							if (isset($variant->product_total_volume_orig)) $variant->product_total_volume_orig = $variant->product_total_volume_orig + $variant->product_total_volume_orig * $rate->shipping_params->dim_approximation / 100;
						}
					}
				}
			}

			if(!isset($rate->shipping_params->with_dimension))
				$rate->shipping_params->with_dimension = 1;
			if($rate->shipping_params->with_dimension)
				$required_dimensions = array('w','x','y','z');
			else
				$required_dimensions = array('w');
			$limit = array();

			$envelope = array(
				'FLAT RATE ENVELOPE',
				'PADDED FLAT RATE ENVELOPE',
				'LEGAL FLAT RATE ENVELOPE',
				'SM FLAT RATE ENVELOPE',
				'WINDOW FLAT RATE ENVELOPE',
				'GIFT CARD FLAT RATE ENVELOPE'
			);
			$variable = array(
				'VARIABLE',
				'FLAT RATE BOX',
				'SM FLAT RATE BOX',
				'MD FLAT RATE BOX',
				'RECTANGULAR',
				'NONRECTANGULAR'
			);


			if(in_array($rate->shipping_params->container, $variable)) {
				$limit['w'] = 1120;
				if($rate->shipping_params->with_dimension)
					$limit['length_girth'] = 108;
			} elseif(in_array($rate->shipping_params->container, $envelope)) {
				if($rate->shipping_params->with_dimension) {
					$limit = array(
						'x' => 0.75,
						'y' => 12,
						'z' => 15
					);
				} else
					$limit['unit'] = 1;
			} elseif($rate->shipping_params->container == 'LG FLAT RATE BOX'){
				$limit['w'] = 1120;
				if($rate->shipping_params->with_dimension)
					$limit['length_girth'] = 130;
			} else
				$limit['unit'] = 1;

			$packages = $this->getOrderPackage($order, array('weight_unit' => 'oz', 'volume_unit' => 'in', 'limit' => $limit, 'required_dimensions' => $required_dimensions));

			if(empty($packages))
				return true;

			$max_weight = 1120;
			$parcels = array();
			if(empty($order->shipping_address_full)) {
				$cart = hikashop_get('class.cart');
				if(isset($order->shipping_address->address_id))
					$cart->loadAddress($order->shipping_address_full, $order->shipping_address->address_id, 'object', 'shipping');
			}

			if(isset($packages['w']) && $packages['w'] > 0){
				if($packages['w'] > $max_weight) {
					$messages['items_weight_over_limit'] = JText::_('ITEMS_WEIGHT_TOO_BIG_FOR_SHIPPING_METHODS');
					$cache_messages['items_weight_over_limit'] = JText::_('ITEMS_WEIGHT_TOO_BIG_FOR_SHIPPING_METHODS');
					$this->setShippingCache($order, null, $cache_messages);
					return true;
				}

				if($packages['w'] < 1)
					$packages['w'] = 1; // Minimum 1oz
				$parcel = new stdClass();
				$parcel->Country = @$order->shipping_address_full->shipping_address->address_country->zone_code_2;
				if(empty($parcel->Country))
					$parcel->Country = 'US';
				$parcel->Pickup_Postcode = substr(preg_replace('#[^a-z0-9]#i', '', @$rate->shipping_params->post_code), 0, 5);
				$parcel->Destination_Postcode = substr(preg_replace('#[^a-z0-9]#i', '', $order->shipping_address->address_post_code), 0, 5);

				if($rate->shipping_params->with_dimension == 1){
					$girth = array($packages['y'], $packages['x'], $packages['z']);
					sort($girth);

					$parcel->Length = $girth[2];
					$parcel->Width = $girth[1];
					$parcel->Height = $girth[0];
					$parcel->Girth = (($parcel->Height + $parcel->Width) * 2) + $parcel->Length;
				} else {
					$parcel->Length = '';
					$parcel->Width = '';
					$parcel->Height = '';
					$parcel->Girth = '';
				}

				$parcel->Quantity = 1;
				$parcel->Weight = round($packages['w'], 2);
				$parcels[] = $parcel;
			}else{
				foreach($packages as $package){
					if((!isset($package['w']) || $package['w'] <= 0))
						continue;

					if($package['w'] > $max_weight) {
						$messages['items_weight_over_limit'] = JText::_('ITEMS_WEIGHT_TOO_BIG_FOR_SHIPPING_METHODS');
						$cache_messages['items_weight_over_limit'] = JText::_('ITEMS_WEIGHT_TOO_BIG_FOR_SHIPPING_METHODS');
						$this->setShippingCache($order, null, $cache_messages);
						return true;
					}

					if($package['w'] < 1)
						$package['w'] = 1; // Minimum 1oz
					$parcel = new stdClass();
					$parcel->Country = @$order->shipping_address_full->shipping_address->address_country->zone_code_2;
					if(empty($parcel->Country))
						$parcel->Country = 'US';
					$parcel->Pickup_Postcode = substr(preg_replace('#[^a-z0-9]#i', '', @$rate->shipping_params->post_code), 0, 5);
					$parcel->Destination_Postcode = substr(preg_replace('#[^a-z0-9]#i', '', $order->shipping_address->address_post_code), 0, 5);

					if($rate->shipping_params->with_dimension == 1){
						$girth = array($package['y'], $package['x'], $package['z']);
						sort($girth);

						$parcel->Length = $girth[2];
						$parcel->Width = $girth[1];
						$parcel->Height = $girth[0];
						$parcel->Girth = (($parcel->Height + $parcel->Width) * 2) + $parcel->Length;
					} else {
						$parcel->Length = '';
						$parcel->Width = '';
						$parcel->Height = '';
						$parcel->Girth = '';
					}

					$parcel->Quantity = 1;
					$parcel->Weight = round($package['w'], 2);
					$parcels[] = $parcel;
				}
			}

			$query = 'SELECT currency_id FROM '.hikashop_table('currency').' WHERE currency_code=\'USD\'';
			$db = JFactory::getDBO();
			$db->setQuery($query);
			$currency = $db->loadResult();

			$rates = array();
			if($parcels[0]->Country == 'US') {
				$modes = array('PRIORITY', 'MEDIA', 'EXPRESS', 'FIRST CLASS');
				foreach($modes as $mode) {
					if(!empty($rate->shipping_params->$mode))
						$this->addRate($rates, $mode, $parcels, $rate, $currency, false);
				}
			} else {
				$modes = array('INTERNATIONAL', 'PRIORITYINTSMALL', 'PRIORITYINTDVD', 'PRIORITYINTLARGEVIDEO', 'PRIORITYINTMEDIUM', 'PRIORITYINTLARGE', 'EXPRESSINTBOX', 'EXPRESSINT', 'FIRSTCLASSINT', 'ENVELOPE');
				foreach($modes as $mode) {
					if(!empty($rate->shipping_params->$mode))
						$this->addRate($rates, $mode, $parcels, $rate, $currency, true);
				}
			}

			if($rate->shipping_params->sort_by_price == 'DESC')
				$rates = $this->array_sort($rates, 'shipping_price', 'usps', SORT_DESC);
			else if($rate->shipping_params->sort_by_price == 'ASC')
				$rates = $this->array_sort($rates, 'shipping_price', 'usps', SORT_ASC);

			$i = 0;

			if(empty($rates)) {
				$messages['no_shipping_quotes'] = 'Failed to obtain shipping quotes.';
				$cache_messages['no_shipping_quotes'] = 'Failed to obtain shipping quotes.';
				continue;
			}

			foreach($rates as $finalRate) {
				if(isset($finalRate->shipping_price_orig) || isset($finalRate->shipping_currency_id_orig)){
					if($finalRate->shipping_currency_id_orig == $finalRate->shipping_currency_id)
						$finalRate->shipping_price_orig = $finalRate->shipping_price;
					else
						$finalRate->shipping_price_orig = $currencyClass->convertUniquePrice($finalRate->shipping_price, $finalRate->shipping_currency_id, $finalRate->shipping_currency_id_orig);
				}

				$finalRate->shipping_ordering .= '_' . $i++;
				$usable_rates[$finalRate->shipping_id] = $finalRate;
				$cache_rates[$finalRate->shipping_id] = $finalRate;
			}
		}

		if(!$found) {
			$messages['no_rates'] = JText::_('NO_SHIPPING_METHOD_FOUND');
			$cache_messages['no_rates'] = JText::_('NO_SHIPPING_METHOD_FOUND');
		}

		$this->setShippingCache($order, $cache_rates, $cache_messages);

		return true;
	}

	function addRate(&$rates, $type, $parcels, &$rate, $currency, $isInternational) {
		$app = JFactory::getApplication();		
		$usps_user_id = $rate->shipping_params->usps_user_id;

		$origin_zip = $parcels[0]->Pickup_Postcode;
		$destination_zip = $parcels[0]->Destination_Postcode;
		$this->countries = $this->USPS_country_list();
		$destination_country = $this->countries[$parcels[0]->Country];
		$machinable = 'false';
		$package_id = 1; //will change this when setting up for multiple packages.

		if (isset($rate->shipping_params->MACHINABLE)) {
			$machinable = "true";
		}

		if($isInternational) {
			$request = '<'.'?xml version="1.0"?'.'>'.
				'<IntlRateV2Request USERID="' . $usps_user_id . '">';

			if($parcels[0]->Country == 'CA')
				$request .= '<Revision>2</Revision>';
			else
				$request .= '<Revision />';

			foreach($parcels as $parcel){
				if($parcel->Weight > 13 && $type =='FIRSTCLASSINT' && isset($rate->shipping_params->firstclass_mail_type) && $rate->shipping_params->firstclass_mail_type == 'PARCEL')
					return;
				$package_weight_arr = $this->getUSPSweightDimensions($parcel->Weight);
				$package_weight_lb = $package_weight_arr['Pounds'];
				$package_weight_oz = $package_weight_arr['Ounces'];
				$package_weight_oz = round($package_weight_oz,2);

				$request .=	'<Package ID="' . $package_id . '">'.
					'<Pounds>' . $package_weight_lb . '</Pounds>'.
					'<Ounces>' . $package_weight_oz . '</Ounces>'.
					'<Machinable>' . $machinable . '</Machinable>'.
					'<MailType>Package</MailType>'.
					'<ValueOfContents>100</ValueOfContents>'.
					'<Country>' . $destination_country . '</Country>'.
					'<Container>' . $rate->shipping_params->container . '</Container>'.
					'<Size>Regular</Size>'.
					'<Width>'.$parcel->Width.'</Width>'.
					'<Length>'.$parcel->Length.'</Length>'.
					'<Height>'.$parcel->Height.'</Height>'.
					'<Girth>'.$parcel->Girth.'</Girth>'.
					'</Package>';
				$package_id++;
			}
			if($parcels[0]->Country == 'CA')
				$request .= '<OriginZip>' . $origin_zip . '</OriginZip>';

			$request .= '</IntlRateV2Request>';
		}
		else {
			$request = '<'.'?xml version="1.0"?'.'>'.
				'<RateV4Request USERID="' . $usps_user_id . '">'.
				'<Revision />';

			foreach($parcels as $parcel){
				if($parcel->Weight > 13 && $type =='FIRST CLASS' && isset($rate->shipping_params->firstclass_mail_type) && $rate->shipping_params->firstclass_mail_type == 'PARCEL')
					return;

				$request.='<Package ID="' . $package_id . '">'.
					'<Service>' . $type . '</Service>';

				if($type =='FIRST CLASS'){
					if(!empty($rate->shipping_params->firstclass_mail_type)){
						$request .= '<FirstClassMailType>' . $rate->shipping_params->firstclass_mail_type . '</FirstClassMailType>';
					}
					else{
						$request .= '<FirstClassMailType>Parcel</FirstClassMailType>';
					}
				}
				$package_weight_arr = $this->getUSPSweightDimensions($parcel->Weight);
				$package_weight_lb = $package_weight_arr['Pounds'];
				$package_weight_oz = $package_weight_arr['Ounces'];
				$package_weight_oz = round($package_weight_oz,2);
				$request .= '<ZipOrigination>' . $origin_zip . '</ZipOrigination>'.
					'<ZipDestination>' . $destination_zip . '</ZipDestination>'.
					'<Pounds>' . $package_weight_lb . '</Pounds>'.
					'<Ounces>' . $package_weight_oz . '</Ounces>'.
					'<Container>' . $rate->shipping_params->container . '</Container>'.
					'<Size>Regular</Size>'.
					'<Width>'.$parcel->Width.'</Width>'.
					'<Length>'.$parcel->Length.'</Length>'.
					'<Height>'.$parcel->Height.'</Height>'.
					'<Girth>'.$parcel->Girth.'</Girth>'.
					'<Machinable>' . $machinable . '</Machinable>'.
					'</Package>';
				$package_id++;
			}
			$request.='</RateV4Request>';
		}

		$ctrl = hikaInput::get()->getString('ctrl','');
		if(!empty($rate->shipping_params->debug) && $ctrl == 'checkout')
			echo '<!--' . "\r\n" . 'USPS DEBUG' . "\r\n" . $request . "\r\n" . '-->';

		$responseError = false;
		$response_xml = $this->doUSPS($request, !$isInternational);

		if(!$response_xml)
			return false;

		if($response_xml->Number) {
			if(!empty($rate->shipping_params->debug) && $ctrl == 'checkout')
				$app->enqueueMessage( 'USPS error: ' . $response_xml->Number . ' ' . $response_xml->Description);
			$responseError = true;
		}

		if($response_xml->Package->Error) {
			if(!empty($rate->shipping_params->debug) && $ctrl == 'checkout')
				$app->enqueueMessage( 'USPS error: ' . $response_xml->Package->Error->Number . ' ' . $response_xml->Package->Error->Description);
			$responseError = true;
		}

		if($isInternational) {
			$rateResult = $response_xml->xpath('Package/Service');
			$usps_rate_arr = $this->xml2array($rateResult);

			$decoding = array(
				2 => 'INTERNATIONAL',
				16 => 'PRIORITYINTSMALL',
				24 => 'PRIORITYINTDVD',
				25 => 'PRIORITYINTLARGEVIDEO',
				9 => 'PRIORITYINTMEDIUM',
				11 => 'PRIORITYINTLARGE',
				15 => 'FIRSTCLASSINT',
				16 => 'PRIORITYINTSMALL',
				24 => 'PRIORITYINTDVD',
				25 => 'PRIORITYINTLARGEVIDEO',
				9 => 'PRIORITYINTMEDIUM',
				11 => 'PRIORITYINTLARGE',
				26 => 'EXPRESSINTBOX',
				1 => 'EXPRESSINT',
				12 => 'ENVELOPE'
			);

			$usps_rates = array();
			foreach($usps_rate_arr as $k) {
				$id = (int)$k['@attributes']['ID'];
				if(isset($decoding[$id]) && strcmp($type, $decoding[$id]) == 0) {
					if(isset($k['ServiceErrors']) || $k['Postage'] == 0)
						continue;

					$usps_rates[] = array(
						'Service' => html_entity_decode($k['SvcDescription']),
						'Rate' => $k['Postage']
					);
				}
			}
		} else {
			$rateResult = $response_xml->xpath('Package/Postage');
			$usps_rate_arr = $this->xml2array($rateResult);

			$usps_rates = array();
			foreach($usps_rate_arr as $k => $v) {
				$usps_rates[] = array(
					'Service' => html_entity_decode($v['MailService']),
					'Rate' => $v['Rate']
				);
			}

			if(empty($usps_rates)) {
				$responseError = true;
			}
		}

		if($responseError == false && !empty($usps_rates)) {
			if(empty($rates[$type])) {
				$info = new stdClass();
				$info = (!HIKASHOP_PHP5) ? $rate : clone($rate);

				$typeKey = str_replace(' ','_', $type);
				$shipping_name = JText::_($typeKey);

				if($shipping_name != $typeKey)
					$info->shipping_name = $shipping_name;
				else
				$info->shipping_name = preg_replace('#sup.*?sup#', '', $info->shipping_name.' : '. $this->pluginConfig['services'][2][$type]);

				$shipping_description = JText::_($type.'_DESCRIPTION');
				if($shipping_description != $type.'_DESCRIPTION') {
					$info->shipping_description .= $shipping_description;
				}
				$info->shipping_id .= '-'.$this->methods[$type];
				$rates[$type] = $info;
			}

			$currencyClass = hikashop_get('class.currency');

			foreach($usps_rates as $usps_rate){
				if(!isset($usps_rate['Rate']))
					continue;
				if(isset($currency) && $currency != $rates[$type]->shipping_currency_id)
					$rates[$type]->shipping_price += $currencyClass->convertUniquePrice($usps_rate['Rate'], $currency, $rates[$type]->shipping_currency_id);
				else
					$rates[$type]->shipping_price += $usps_rate['Rate'];

			}
		}
	}

	function getShippingDefaultValues(&$element){
		$element->shipping_name='United States Postal Service';
		$element->shipping_description='';
		$element->shipping_images='usps';
		$element->shipping_params->PRIORITY='Priority Mail';
		$element->shipping_params->MEDIA='Media Mail';
		$element->shipping_params->EXPRESS='Express Mail';
		$FIRSTCLASS = 'FIRST CLASS';
		$element->shipping_params->$FIRSTCLASS='First Class Mail';
		$element->shipping_params->INTERNATIONAL='Priority Mail International (International)';
		$element->shipping_params->PRIORITYINTSMALL='Priority Mail International Small Flat Rate Box (International)';
		$element->shipping_params->PRIORITYINTDVD='Priority Mail International DVD Flat Rate priced box (International)';
		$element->shipping_params->PRIORITYINTLARGEVIDEO='Priority Mail International Large Video Flat Rate priced box (International)';
		$element->shipping_params->PRIORITYINTMEDIUM='Priority Mail International Medium Flat Rate Box (International)';
		$element->shipping_params->PRIORITYINTLARGE='Priority Mail International Large Flat Rate Box (International)';
		$element->shipping_params->EXPRESSINTBOX='Express Mail International Flat Rate Boxes (International)';
		$element->shipping_params->EXPRESSINT='Express Mail International (International)';
		$element->shipping_params->FIRSTCLASSINT='First Class Mail (International)';
		$element->shipping_params->ENVELOPE='USPS GXG Envelopes (International)';
		$element->shipping_params->post_code='';
		$element->shipping_params->firstclass_mail_type='PARCEL';
		$element->shipping_params->container='RECTANGULAR';
		$element->shipping_params->MACHINABLE=true;
	}

	function onShippingConfiguration(&$element){
		$config = &hikashop_config();
		$this->usps = hikaInput::get()->getCmd('name','usps');
		$this->main_currency = $config->get('main_currency', 1);

		$currencyClass = hikashop_get('class.currency');
		$currency = $currencyClass->get($this->main_currency);
		$this->currencyCode = $currency->currency_code;
		$this->currencySymbol = $currency->currency_symbol;

		$this->categoryType = hikashop_get('type.categorysub');
		$this->categoryType->type = 'tax';
		$this->categoryType->field = 'category_id';

		parent::onShippingConfiguration($element);
		$elements = array($element);
	}

	function onShippingConfigurationSave(&$element) {
		$app = JFactory::getApplication();
		if(empty($element->shipping_params->usps_user_id)){
			$app->enqueueMessage(JText::sprintf('ENTER_INFO', 'USPS', ' WebTools User ID'));
		}
		if(empty($element->shipping_params->post_code)){
			$app->enqueueMessage(JText::sprintf('ENTER_INFO', 'USPS', JText::_('POST_CODE')));
		}
		if(isset($element->shipping_params->services)){
			$element->shipping_params->PRIORITY=in_array('PRIORITY',$element->shipping_params->services);
			$element->shipping_params->MEDIA=in_array('MEDIA',$element->shipping_params->services);
			$element->shipping_params->EXPRESS=in_array('EXPRESS',$element->shipping_params->services);
			$FIRSTCLASS = 'FIRST CLASS';
			$element->shipping_params->$FIRSTCLASS=in_array('FIRST CLASS',$element->shipping_params->services);
			$element->shipping_params->INTERNATIONAL=in_array('INTERNATIONAL',$element->shipping_params->services);
			$element->shipping_params->PRIORITYINTSMALL=in_array('PRIORITYINTSMALL',$element->shipping_params->services);
			$element->shipping_params->PRIORITYINTDVD=in_array('PRIORITYINTDVD',$element->shipping_params->services);
			$element->shipping_params->PRIORITYINTLARGEVIDEO=in_array('PRIORITYINTLARGEVIDEO',$element->shipping_params->services);
			$element->shipping_params->PRIORITYINTMEDIUM=in_array('PRIORITYINTMEDIUM',$element->shipping_params->services);
			$element->shipping_params->PRIORITYINTLARGE=in_array('PRIORITYINTLARGE',$element->shipping_params->services);
			$element->shipping_params->EXPRESSINTBOX=in_array('EXPRESSINTBOX',$element->shipping_params->services);
			$element->shipping_params->EXPRESSINT=in_array('EXPRESSINT',$element->shipping_params->services);
			$element->shipping_params->FIRSTCLASSINT=in_array('FIRSTCLASSINT',$element->shipping_params->services);
			$element->shipping_params->ENVELOPE=in_array('ENVELOPE',$element->shipping_params->services);
		}else {
			$app->enqueueMessage(JText::sprintf('CHOOSE_SHIPPING_SERVICE'));
		}
		if(!empty($element->shipping_params->machinableCheck))
			$element->shipping_params->MACHINABLE = in_array('MACHINABLE',$element->shipping_params->machinableCheck);
		parent::onShippingConfigurationSave($element);
	}

	function onAfterOrderConfirm(&$order, &$methods, $method_id) {
		return true;
	}

	function doUSPS($XMLRequest, $domesticShipment) {
		$apiName = 'RateV4';

		if($domesticShipment == false)
			$apiName = 'IntlRateV2';

		$url = 'http://production.shippingapis.com/ShippingAPI.dll?API=' . $apiName . '&XML=' . urlencode($XMLRequest);

		$session = curl_init();
		curl_setopt($session, CURLOPT_FRESH_CONNECT,  true);
		curl_setopt($session, CURLOPT_POST,           false);
		curl_setopt($session, CURLOPT_FOLLOWLOCATION, false);
		curl_setopt($session, CURLOPT_FAILONERROR,    true);
		curl_setopt($session, CURLOPT_RETURNTRANSFER, true);
		curl_setopt($session, CURLOPT_COOKIEFILE,     '');
		curl_setopt($session, CURLOPT_SSL_VERIFYPEER, false);
		curl_setopt($session, CURLOPT_SSL_VERIFYHOST, false);
		curl_setopt($session, CURLOPT_ENCODING,       'UTF-8');
		curl_setopt($session, CURLOPT_URL,            $url);
		$result = curl_exec($session);
		$error = curl_error($session);

		if(empty($result)) {
			$app = JFactory::getApplication();
			$app->enqueueMessage( 'Cannot connect to USPS web service. You hosting company might be blocking outbound connections.<br/>'.$error);
			return false;
		}

		$responseDoc = simplexml_load_string($result);
		if($responseDoc === false) {
			$app = JFactory::getApplication();
			$app->enqueueMessage('Cannot dialog with USPS web service.<br/>'.$result);
			return false;
		}
		return $responseDoc;
	}

	function processPackageLimit($limit_key, $limit_value, $product, $qty, $package, $units) {
		switch($limit_key) {
			case 'length_girth':
				$divide = $product['z'] + ($product['x'] + $product['y']) * 2;
				if(!$divide || $divide > $limit_value)
					return false;
				$current_limit_value = max(0.0, $limit_value - (float)(($package['x'] + $package['y']) * 2 + $package['z']));
				return (int)floor($current_limit_value / $divide);
				break;
		}
		return parent::processPackageLimit($limit_key, $limit_value , $product, $qty, $package, $units);
	}

	function getResponseXML($httpResponse) {
		$lines = preg_split('/(\r\n|\r|\n)/', $httpResponse);
		$responseBody = '';
		$lineCount = count($lines);
		for($i = 0; $i < $lineCount; $i++) {
			if($lines[$i] == '')
				break;
		}

		for($j = $i + 1; $j < $lineCount; $j++) {
			$responseBody .= $lines[$j] . "\n";
		}
		return $responseBody;
	}

	function getUSPSweightDimensions($ounces) {
		if($ounces < 16)
			return array('Pounds' => 0, 'Ounces' => $ounces);
		$pounds = floor($ounces / 16);
		$ounces =  fmod($ounces, $pounds * 16);
		return array('Pounds' => $pounds, 'Ounces' => $ounces);
	}

	function USPS_Country_List() {
		return array(
'AF' => 'Afghanistan', 'AL' => 'Albania', 'AX' => 'Aland Island (Finland)', 'DZ' => 'Algeria', 'AD' => 'Andorra', 'AO' => 'Angola', 'AI' => 'Anguilla', 'AG' => 'Antigua and Barbuda',
'AR' => 'Argentina', 'AM' => 'Armenia', 'AW' => 'Aruba', 'AU' => 'Australia', 'AT' => 'Austria', 'AZ' => 'Azerbaijan', 'BC' => 'Canada', 'BS' => 'Bahamas', 'BH' => 'Bahrain',
'BD' => 'Bangladesh', 'BB' => 'Barbados', 'BY' => 'Belarus', 'BE' => 'Belgium', 'BZ' => 'Belize', 'BJ' => 'Benin', 'BM' => 'Bermuda', 'BT' => 'Bhutan', 'BO' => 'Bolivia',
'BA' => 'Bosnia-Herzegovina', 'BW' => 'Botswana', 'BR' => 'Brazil', 'VG' => 'British Virgin Islands', 'BN' => 'Brunei Darussalam', 'BG' => 'Bulgaria', 'BF' => 'Burkina Faso',
'MM' => 'Burma', 'BI' => 'Burundi', 'KH' => 'Cambodia', 'CM' => 'Cameroon', 'CA' => 'Canada', 'CV' => 'Cape Verde', 'KY' => 'Cayman Islands', 'CF' => 'Central African Republic',
'TD' => 'Chad', 'CL' => 'Chile', 'CN' => 'China', 'CX' => 'Christmas Island (Australia)', 'CC' => 'Cocos Island (Australia)', 'CO' => 'Colombia', 'KM' => 'Comoros',
'CG' => 'Congo, Republic of the', 'CD' => 'Congo, Democratic Republic of the', 'CK' => 'Cook Islands (New Zealand)', 'CR' => 'Costa Rica', 'CI' => 'Cote d Ivoire (Ivory Coast)',
'HR' => 'Croatia', 'CU' => 'Cuba', 'CY' => 'Cyprus', 'CZ' => 'Czech Republic', 'DK' => 'Denmark', 'DJ' => 'Djibouti', 'DM' => 'Dominica', 'DO' => 'Dominican Republic',
'EC' => 'Ecuador', 'EG' => 'Egypt', 'SV' => 'El Salvador', 'GQ' => 'Equatorial Guinea', 'ER' => 'Eritrea', 'EE' => 'Estonia', 'ET' => 'Ethiopia', 'FK' => 'Falkland Islands',
'FO' => 'Faroe Islands', 'FJ' => 'Fiji', 'FI' => 'Finland', 'FR' => 'France', 'FX' => 'France', 'GF' => 'French Guiana', 'PF' => 'French Polynesia', 'GA' => 'Gabon', 'GM' => 'Gambia',
'GE' => 'Georgia, Republic of', 'DE' => 'Germany', 'GH' => 'Ghana', 'GI' => 'Gibraltar', 'GB' => 'Great Britain and Northern Ireland', 'GR' => 'Greece', 'GL' => 'Greenland',
'GD' => 'Grenada', 'GP' => 'Guadeloupe', 'GT' => 'Guatemala', 'GN' => 'Guinea', 'GW' => 'Guinea-Bissau', 'GY' => 'Guyana', 'HT' => 'Haiti', 'HN' => 'Honduras', 'HK' => 'Hong Kong',
'HU' => 'Hungary', 'IS' => 'Iceland', 'IN' => 'India', 'ID' => 'Indonesia', 'IR' => 'Iran', 'IQ' => 'Iraq', 'IE' => 'Ireland', 'IL' => 'Israel', 'IT' => 'Italy', 'JM' => 'Jamaica',
'JP' => 'Japan', 'JO' => 'Jordan', 'KZ' => 'Kazakhstan', 'KE' => 'Kenya', 'KI' => 'Kiribati', 'KW' => 'Kuwait', 'KG' => 'Kyrgyzstan', 'LA' => 'Laos', 'LV' => 'Latvia',
'LB' => 'Lebanon', 'LS' => 'Lesotho', 'LR' => 'Liberia', 'LY' => 'Libya', 'LI' => 'Liechtenstein', 'LT' => 'Lithuania', 'LU' => 'Luxembourg', 'MO' => 'Macao',
'MK' => 'Macedonia, Republic of', 'MG' => 'Madagascar', 'MW' => 'Malawi', 'MY' => 'Malaysia', 'MV' => 'Maldives', 'ML' => 'Mali', 'MT' => 'Malta', 'MQ' => 'Martinique',
'MR' => 'Mauritania', 'MU' => 'Mauritius', 'YT' => 'Mayotte (France)', 'MX' => 'Mexico', 'FM' => 'Micronesia, Federated States of', 'MD' => 'Moldova', 'MC' => 'Monaco (France)',
'MN' => 'Mongolia', 'MS' => 'Montserrat', 'MA' => 'Morocco', 'MZ' => 'Mozambique', 'NA' => 'Namibia', 'NR' => 'Nauru', 'NP' => 'Nepal', 'NL' => 'Netherlands',
'AN' => 'Netherlands Antilles', 'NC' => 'New Caledonia', 'NZ' => 'New Zealand', 'NI' => 'Nicaragua', 'NE' => 'Niger', 'NG' => 'Nigeria',
'KP' => 'North Korea (Korea, Democratic People\'s Republic of)', 'NO' => 'Norway', 'OM' => 'Oman', 'PK' => 'Pakistan', 'PA' => 'Panama', 'PG' => 'Papua New Guinea', 'PY' => 'Paraguay',
'PE' => 'Peru', 'PH' => 'Philippines', 'PN' => 'Pitcairn Island', 'PL' => 'Poland', 'PT' => 'Portugal', 'QA' => 'Qatar', 'RE' => 'Reunion', 'RO' => 'Romania', 'RU' => 'Russia',
'RW' => 'Rwanda', 'SH' => 'Saint Helena',  'KN' => 'Saint Kitts (St. Christopher and Nevis)', 'LC' => 'Saint Lucia', 'PM' => 'Saint Pierre and Miquelon',
'VC' => 'Saint Vincent and the Grenadines', 'SM' => 'San Marino', 'ST' => 'Sao Tome and Principe', 'SA' => 'Saudi Arabia', 'SN' => 'Senegal', 'RS' => 'Serbia', 'SC' => 'Seychelles',
'SL' => 'Sierra Leone', 'SG' => 'Singapore', 'SK' => 'Slovak Republic', 'SI' => 'Slovenia', 'SB' => 'Solomon Islands', 'SO' => 'Somalia', 'ZA' => 'South Africa',
'GS' => 'South Georgia (Falkland Islands)', 'KR' => 'South Korea (Korea, Republic of)', 'ES' => 'Spain', 'LK' => 'Sri Lanka', 'SD' => 'Sudan', 'SR' => 'Suriname', 'SZ' => 'Swaziland',
'SE' => 'Sweden', 'CH' => 'Switzerland', 'SY' => 'Syrian Arab Republic', 'TW' => 'Taiwan', 'TJ' => 'Tajikistan', 'TZ' => 'Tanzania', 'TH' => 'Thailand',
'TL' => 'East Timor (Indonesia)', 'TG' => 'Togo', 'TK' => 'Tokelau (Union) Group (Western Samoa)', 'TO' => 'Tonga', 'TT' => 'Trinidad and Tobago', 'TN' => 'Tunisia', 'TR' => 'Turkey',
'TM' => 'Turkmenistan', 'TC' => 'Turks and Caicos Islands', 'TV' => 'Tuvalu', 'UG' => 'Uganda', 'UA' => 'Ukraine', 'US' => 'United States', 'AE' => 'United Arab Emirates',
'UY' => 'Uruguay', 'UZ' => 'Uzbekistan', 'VU' => 'Vanuatu', 'VA' => 'Vatican City', 'VE' => 'Venezuela', 'VN' => 'Vietnam', 'WF' => 'Wallis and Futuna Islands',
'WS' => 'Western Samoa', 'YE' => 'Yemen', 'ZM' => 'Zambia', 'ZW' => 'Zimbabwe'
		);
	}

	function xml2array( $xmlObject, $out = array())
	{
		foreach( (array) $xmlObject as $index => $node)
			$out[$index] = ( is_object ( $node ) ) ? $this->xml2array ( $node ) : $node;
		return $out;
	}

	function array_sort($array, $on, $type, $order = SORT_ASC)
	{
		if(empty($array))
			return array();

		$new_array = array();
		$sortable_array = array();
		foreach ($array as $key => $value) {
			if($value->shipping_type == $type)
				$sortable_array[$key] = $value->shipping_price;
		}

		switch ($order) {
			case SORT_ASC:
				asort($sortable_array);
			break;
			case SORT_DESC:
				arsort($sortable_array);
			break;
		}

		foreach ($sortable_array as $k => $v) {
			$new_array[$k] = $array[$k];
		}
		return $new_array;
	}
}
