<?php
/**
 * Shipping by Rules generic helper class (WP/WooCommerce-specific)
 * Reinhold Kainhofer, Open Tools, office@open-tools.net
 * @copyright (C) 2012-2016 - Reinhold Kainhofer
 * @license GNU/GPLv3 http://www.gnu.org/licenses/gpl-3.0.html
**/

if ( !defined( 'ABSPATH' ) ) { 
	die( 'Direct Access to ' . basename( __FILE__ ) . ' is not allowed.' );
}
require_once( plugin_dir_path( __FILE__ ) . '/../library/rules_shipping_framework.php');

class RulesShippingFrameworkWooCommerce extends RulesShippingFramework {
	protected static $_method_ordering	= 'woocommerce_shipping_rules_ordering';
	
	function __construct() {
		parent::__construct();
		load_plugin_textdomain('woocommerce-shipping-by-rules', false, basename( dirname( __FILE__ ) ) . '/languages' );
		$this->registerScopings(array(
			"categories"    => 'categories',
			"subcategories" => 'subcategories',
			"products"      => 'products',
			"skus"          => 'products',
		));
	}
	static function getHelper() {
		static $helper = null;
		if (!$helper) {
			$helper = new RulesShippingFrameworkWooCommerce();
			$helper->setup();
		}
		return $helper;
    }
	function urlPath($type, $file) {
		return plugins_url('library/' . $type . '/' . $file, __FILE__);
    }
	
	function isAdvanced() {
		return false;
	}
	function getCustomFunctions() {
		// Let other plugins add custom functions! 
		// The opentools_shipping_by_rules_replacements filter is expected to return an array of the form:
		//   array ('functionname1' => 'function-to-be-called',
		//          'functionname2' => array($classobject, 'memberfunc')),
		//          ...);
		return apply_filters( 'opentools_shipping_by_rules_replacements', array());
	}
	
	public function printWarning($message) {
		// Keep track of warning messages, so we don't print them twice:
		global $printed_warnings;
		if (!isset($printed_warnings))
			$printed_warnings = array();
		if (!in_array($message, $printed_warnings)) {
			wc_add_notice( $message, 'error');
			$printed_warnings[] = $message;
		}
	}

	/** @tag public-api
	 *  @function debug()
	 *    Print a debug message (untranslated) in the system-specific way.
	 *  @param $message the debug message to be printed 
	 */
	public function debug($message) {
		if ( true === WP_DEBUG ) {
			if ( is_array( $message ) || is_object( $message ) ) {
				error_log( print_r( $message, true ) );
			} else {
				error_log( $message );
			}
		}
	}

	/**
	 * HELPER FUNCTIONS, WooCommerce-specific
	 */
	public function __($string) {
		$args = func_get_args();
		$string = $this->readableString($string);
		$string = __($string, 'opentools-shippingrules');
		if (count($args)>1) {
			$args[0] = $string;
			return call_user_func_array("sprintf", $args);
		} else {
			return $string;
		}
	}

	protected function getCartProducts($package, $method) {
		return $package['contents'];
	}
	
	protected function getMethodId($method) {
		return $method->ID;
	}

	protected function getMethodName($method) {
		return $method->post_title;
	}

	protected function parseMethodRules (&$method) {
		$rulesets = Shipping_Rules_post_type::get_rulesets($method);
		foreach ($rulesets as $ruleset) {
			$this->parseMethodRule(
				/* Rules */    isset($ruleset['rules'])?$ruleset['rules']:'', 
				/* Countries */isset($ruleset['countries'])?$ruleset['countries']:array(), 
				/* Rule info */array(), 
				/* Method */   $method);
		}
	}

	/**
	 * Functions to calculate the cart variables:
	 *   - getOrderCounts($cart, $products, $method)
	 *   - getOrderDimensions
	 */
	/** Functions to calculate all the different variables for the given cart and given (sub)set of products in the cart */
	protected function getOrderCounts ($cart, $products, $method) {
		$counts = array(
			'articles'    => 0,
			'products'    => count($products),
			'quantity'    => 0,
			'minquantity' => 9999999999,
			'maxquantity' => 0,
		);

		foreach ($products as $product) {
			$counts['articles']   += $product['quantity'];
			$counts['maxquantity'] = max ($counts['maxquantity'], $product['quantity']);
			$counts['minquantity'] = min ($counts['minquantity'], $product['quantity']);
		}
		$counts['quantity'] = $counts['articles'];
		return $counts;
	}

	protected function getOrderDimensions ($cart, $products, $method) {
		/* Cache the value in a static variable and calculate it only once! */
		$dimensions=array(
			'volume' => 0,
			'maxvolume' => 0, 'minvolume' => 99999999,
			'maxlength' => 0, 'minlength' => 99999999, 'totallength' => 0,
			'maxwidth'  => 0, 'minwidth' => 99999999,  'totalwidth'  => 0,
			'maxheight' => 0, 'minheight' => 99999999, 'totalheight' => 0,
		);
		foreach ($products as $product) {
	
			$l = $product['data']->length;
			$w = $product['data']->width;
			$h = $product['data']->height;

			$volume = $l * $w * $h;
			$dimensions['volume'] += $volume * $product['quantity'];
			$dimensions['maxvolume'] = max ($dimensions['maxvolume'], $volume);
			$dimensions['minvolume'] = min ($dimensions['minvolume'], $volume);
				
			$dimensions['totallength'] += $l * $product['quantity'];
			$dimensions['maxlength'] = max ($dimensions['maxlength'], $l);
			$dimensions['minlength'] = min ($dimensions['minlength'], $l);
			$dimensions['totalwidth'] += $w * $product['quantity'];
			$dimensions['maxwidth'] = max ($dimensions['maxwidth'], $w);
			$dimensions['minwidth'] = min ($dimensions['minwidth'], $w);
			$dimensions['totalheight'] += $h * $product['quantity'];
			$dimensions['maxheight'] = max ($dimensions['maxheight'], $h);
			$dimensions['minheight'] = min ($dimensions['minheight'], $h);
		}

		return $dimensions;
	}
	
	protected function getOrderWeights ($cart, $products, $method) {
		$dimensions=array(
			'weight' => 0,
			'maxweight' => 0, 'minweight' => 9999999999,
		);
		foreach ($products as $product) {
			$w = $product['data']->get_weight();
			$dimensions['maxweight'] = max ($dimensions['maxweight'], $w);
			$dimensions['minweight'] = min ($dimensions['minweight'], $w);
			$dimensions['weight'] += $w * $product['quantity'];
		}
		return $dimensions;
	}
	
	protected function getOrderListProperties ($cart, $products, $method) {
		$categories = array();
		$skus = array();
		$tags = array();
		$shipping_classes = array();
		foreach ($products as $product) {
			$id = $product['data']->id;
			if ($product['data']->get_sku()) {
				$skus[] = $product['data']->get_sku();
			}
			foreach (wc_get_product_terms( $id, 'product_cat') as $c) {
				$categories[] = $c->slug;
			}
			foreach (wc_get_product_terms( $id, 'product_tag') as $c) {
				$tags[] = $c->slug;
			}
			$shipclass = $product['data']->get_shipping_class();
			if ($shipclass) {
				$shipping_classes[] = $shipclass;
			}
		}
		$skus = array_unique($skus);
		$categories = array_unique($categories);
		$tags = array_unique($tags);
		$shipping_classes = array_unique($shipping_classes);

		return array (
			'skus'       => $skus, 
			'categories' => $categories,
			'tags'       => $tags,
			'shippingclasses' => $shipping_classes,
		);
	}
	
	protected function getOrderAddress ($cart, $method) {
		$address = $cart['destination'];
		$zip = isset($address['postcode'])?trim($address['postcode']):'';
		$data = array(
			'zip'      => $zip,
			'postcode' => $zip,
			'zip1'     => substr($zip,0,1),
			'zip2'     => substr($zip,0,2),
			'zip3'     => substr($zip,0,3),
			'zip4'     => substr($zip,0,4),
			'zip5'     => substr($zip,0,5),
			'zip6'     => substr($zip,0,6),
			'city'     => trim($address['city']),
			'country'  => trim($address['country']),
			'state'    => trim($address['state']),
			'address1' => trim($address['address']),
			'address2' => trim($address['address_2']),
		);
		/* Get the user from the package information and extract further information about the buyer */
		$user = $cart['user'];
		// TODO: Extract user!
/**		$data['company'] = isset($address['company'])?$address['company']:'';
		$data['title'] = isset($address['title'])?$address['title']:'';
		$data['first_name'] = isset($address['title'])?$address['title']:'';
		$data['middle_name'] = isset($address['middle_name'])?$address['middle_name']:'';
		$data['last_name'] = isset($address['last_name'])?$address['last_name']:'';
		$data['phone1'] = isset($address['phone_1'])?$address['phone_1']:'';
		$data['phone2'] = isset($address['phone_2'])?$address['phone_2']:'';
		$data['fax'] = isset($address['fax'])?$address['fax']:'';
		$data['email'] = isset($address['email'])?$address['email']:'';
*/
		// The country check needs the countryid variable, so duplicate from country:
		$data['countryid'] = $data['country'];
		return $data;
	}
	
	protected function getOrderPrices ($cart, $products, /*$cart_prices, */$method) {
		$data = array(
			'total'       => 0,
			'subtotal'    => 0,
			'taxtotal'    => 0,
			'taxsubtotal' => 0,
			'cost'        => 0,
		);
		// Calculate the prices from the individual products!
		// Possible problems are discounts on the order total
		foreach ($products as $product) {
			$data['total']                     += $product['line_total'];
			$data['subtotal']                  += $product['line_subtotal'];
			$data['taxtotal']                  += $product['line_tax'];
			$data['taxsubtotal']               += $product['line_subtotal_tax'];
			$data['cost']                      += $product['line_total'] + $product['line_tax'];
		}
		$data['amount'] = $data['cost'];
		$data['amountwithtax'] = $data['cost'];
		return $data;
	}

	/** Allow child classes to add additional variables for the rules or modify existing one
	 */
	protected function addCustomCartValues ($cart, $products, $method, &$values) {
	}
	protected function addPluginCartValues($cart, $products, $method, &$values) {
		return apply_filters( 'opentools_shipping_by_rules_get_cart_values', array(&$cartvals, $cart, $products, $method));
	}

	/** Filter the given array of products and return only those that belong to the categories, manufacturers, 
	*  vendors or products given in the $filter_conditions. The $filter_conditions is an array of the form:
	*     array( 'products'=>array(....), 'categories'=>array(1,2,3,42))
	*  Notice that giving an empty array for any of the keys means "no restriction" and is exactly the same 
	*  as leaving out the entry altogether
	*/
	public function filterProducts($products, $filter_conditions) {
		$result = array();
		
		// For the subcategories scoping we need all subcategories of the conditions:
		$subcategories = array();
		if (isset($filter_conditions['subcategories']) && !empty($filter_conditions['subcategories'])) {
			foreach ($filter_conditions['subcategories'] as $catslug) {
				// Get the term itself (we have only the slug!)
				$cat = get_term_by('slug', $catslug, 'product_cat');
				if (empty($cat))
					continue;
				$subcategories[] = $cat->slug;
				// Get the list of all subcategories of the given categories
				$args=array('child_of' => $cat->term_id, 'hierarchical' => 1);
				foreach (get_terms( 'product_cat', $args) as $subcat) {
					$subcategories[] = $subcat->slug;
				}
			}
			$subcategories = array_unique($subcategories);
		}
		
		// Now filter out all products that do not match the conditions
		foreach ($products as $p) {
			$prodcategories = array();
			foreach (wc_get_product_terms( $p['data']->id, 'product_cat') as $cat) {
				$prodcategories[] = $cat->slug;
			}
			if (!empty($filter_conditions['products']) && !in_array($p['data']->get_sku(), $filter_conditions['products']))
				continue;
			if (!empty($filter_conditions['categories']) && count(array_intersect($filter_conditions['categories'], $prodcategories))==0)
				continue;
			if (!empty($filter_conditions['subcategories']) && count(array_intersect($subcategories, $prodcategories))==0)
				continue;
			$result[] = $p;
		}
		return $result;
	}
	
	/**
	* get_shipping_rules_methods function.
	*/
	static public function get_rule_shipping_methods() {
		$unsortedmethods = get_posts (array ('posts_per_page' => '-1', 'post_type' => 'shipping_rules'));
		$ordering = get_option( self::$_method_ordering );
		$methods = array();
		foreach ($ordering as $o) {
			foreach ($unsortedmethods as $key=>$m) {
				if ($m->ID == $o) {
					$methods[$o] = $m;
					unset($unsortedmethods[$key]);
					break;
				}
			}
		}
		$methods = $methods + $unsortedmethods;
		return $methods;
	}
	
	static public function set_method_ordering($ordering) {
		update_option( self::$_method_ordering, $ordering );
	}

	protected function createMethodRule ($r, $countries, $ruleinfo) {
		return new ShippingRule($this, $r, $countries, $ruleinfo);
	}
	
	public function getUpgradeNagSettings() {
		$settings = array();
		if (!$this->isAdvanced()) {
			$settings['opentools_shippingbyrules_upgrade'] = array(
				'name' 		=> $this->__( 'Upgrade to the ADVANCED VERSION of the OpenTools Shipping by Rules plugin'),
				'type' 		=> 'opentools_shippingbyrules_upgrade',
				'link'		=> 'http://open-tools.net/woocommerce/advanced-shipping-by-rules-for-woocommerce.html',
			);
		}
		return $settings;
	}
	
	public function printUpgradeNagBox($settings) {
		include plugin_dir_path( __FILE__ ) . 'admin/html/html-upgrade-nag.php';
	}
	

}
