<?php


if ( ! defined( 'ABSPATH' ) ) {
	exit; // Exit if accessed directly
}

/**
 * Plugin Name: Boxtal
 * Plugin URI: http://ecommerce.envoimoinscher.com/
 * Description: Manage multiple carriers using one single plugin and reduce your shipping costs without commitments or any contract to sign.
 * Version: 2.1.17
 * Author: Boxtale
 * Author URI: http://www.boxtal.com
 * Text Domain: envoimoinscher
 * Domain Path: /languages
 */

/**
 * Check if WooCommerce is active (include network check)
 **/
if ( ! function_exists( 'is_plugin_active_for_network' ) )
	require_once( ABSPATH . '/wp-admin/includes/plugin.php' );

if ( ! in_array( 'woocommerce/woocommerce.php', apply_filters( 'active_plugins', get_option( 'active_plugins' ) ) ) ) :
	if ( ! is_plugin_active_for_network( 'woocommerce/woocommerce.php' ) ) :
		return;
	endif;
endif;


if ( ! class_exists('envoimoinscher')){

		define('EMC_LOG_FILE','envoimoinscher');

		// Setup activation, deactivation and uninstall hooks
		register_activation_hook( __FILE__, array( 'envoimoinscher', 'activate' ) );
		register_deactivation_hook( __FILE__, array( 'envoimoinscher', 'deactivate' ) );
		register_uninstall_hook (__FILE__, array( 'envoimoinscher', 'uninstall' ) );

		// Add action to activate plugin on new multisite site if plugin already configured as network activated
		add_action( 'wpmu_new_blog', array( 'envoimoinscher', 'network_activated' ), 10, 6 );
		// Add action to uninstall plugin on multisite site if site is deleted
		add_action( 'wpmu_drop_tables', array( 'envoimoinscher', 'uninstall_multisite_instance' ) );

		class envoimoinscher {

			public $version = '2.1.17';
			public $platform = 'woocommerce';
            protected $activated_countries = null;
            protected $all_countries = null;
            protected $shipping_classes = null;

			/**
			 * @var The single instance of the class
			 */
			protected static $_instance = null;

            /** @var object Background update class */
            private static $background_updater;

			/**
			 * Main envoimoinscher Instance
			 *
			 * Ensures only one instance of envoimoinscher is loaded or can be loaded.
			 * @static
			 * @see EMC()
			 * @return envoimoinscher - Main instance
			 */
			public static function instance() {
				if ( is_null( self::$_instance ) ) {
					self::$_instance = new self();
				}
				return self::$_instance;
			}

			/**
			 * __construct function.
			 *
			 * @access public
			 * @return void
			 */
			public function  __construct() {
				$this->define_constants();
				$this->includes();
                $this->init_hooks();

				$this->id = self::getCalledClass();
				$this->method_description = __( 'Shipping plugin: 15 negotiated carriers', 'envoimoinscher' );
            }

			/**
			 * init function.
			 *
			 * @access public
			 * @return void
			 */
			public function init_hooks() {
				add_action( 'woocommerce_loaded', array( 'envoimoinscher_model', 'init_logger' ), 1 );
                // register & add a new custom status "Shipped" on "order details" and "orders" filter page
                add_action( 'init', array( &$this, 'register_shipped_order_status'), 11 );

                 // check for upgrades
                add_action( 'init', array( &$this, 'check_version' ), 10);
                add_action( 'init', array( &$this, 'init_background_updater' ), 9);
                add_action( 'init', array( &$this, 'init' ), 8);
                add_action( 'admin_init', array( &$this, 'install_actions' ) );
                add_action( 'admin_init', array( &$this, 'admin_redirects' ) );

                // check for news
                add_action( 'admin_init', array( &$this, 'check_news' ));

				// add shipping methods
				add_filter( 'woocommerce_shipping_methods', array(&$this, 'envoimoinscher_filter_shipping_methods'));

				// adds EMC metabox on order page
				add_action( 'add_meta_boxes_shop_order', array( &$this, 'envoimoinscher_metabox' ) );
				add_action( 'woocommerce_process_shop_order_meta', 'EMC_Meta_Box_Order_Shipping::save', 40, 2 );

				// hook pour l'ajout du select avec les points relais
				add_action('woocommerce_after_checkout_form',array(&$this,'load_pickup_point_js'));


				// hook to throw an alert if no relay point selected
				add_action('woocommerce_checkout_process', array(&$this,'hook_checkout_process'));
				// hook to save relay point
				add_action('woocommerce_checkout_order_processed', array(&$this,'hook_new_order'));

				// change label front office display
				add_filter( 'woocommerce_cart_shipping_method_full_label', array(&$this,'change_shipping_label'), 10, 2 );

				// add single order actions
				add_action( 'woocommerce_order_actions', array( &$this, 'add_order_action' ) );
				add_action( 'woocommerce_order_action_send_orders', array( &$this, 'process_send_order' ) );

				// add front office css
				add_action( 'wp_enqueue_scripts', array(&$this,'add_css') );

                // register & add a new custom status "Shipped" on "order details" and "orders" filter page
                add_filter( 'wc_order_statuses', array( &$this, 'add_shipped_to_order_statuses') );

                // add new bulk actions on "orders" filter page
                add_action('admin_footer-edit.php', array( &$this, 'add_bulk_action_options') );
                add_action('load-edit.php', array(&$this, 'emc_bulk_actions'));

                // manage actions on "orders" filter page
                add_filter('woocommerce_admin_order_actions', array( &$this,'custom_add_orders_actions'), 100 );

                // handle push notifications via woocommerce api
                add_action( 'woocommerce_api_envoimoinscher', array(&$this,'api_callback_handler') );

				// limit cart shipping costs calculation in FO
				add_filter('woocommerce_cart_ready_to_calc_shipping', array( &$this,'limit_cost_calculation') );

				// add tracking number in URL
				add_action( 'woocommerce_order_details_after_order_table', array( &$this,'add_tracking_in_FO') );
                
                // add ajax callback to get pickup points
                add_action( 'wp_ajax_get_points', array(&$this, 'get_points_callback') );
                add_action( 'wp_ajax_nopriv_get_points', array(&$this, 'get_points_callback') );
                
                // add ajax callback to set pickup point in session
                add_action( 'wp_ajax_set_point', array(&$this, 'set_point_callback') );
                add_action( 'wp_ajax_nopriv_set_point', array(&$this, 'set_point_callback') );

                /* ajax calls */
				if ( is_admin() ) {
                    // add rate line (account page)
                    add_action( 'wp_ajax_add_rate_line', array(&$this, 'add_rate_line_callback'));

                    // format address (account page)
                    add_action( 'wp_ajax_format_address', array(&$this, 'format_address_callback'));

                    // add address (account page)
                    add_action( 'wp_ajax_add_address', array(&$this, 'add_address_callback'));

                    // remove address (account page)
                    add_action( 'wp_ajax_remove_address', array(&$this, 'remove_address_callback'));

                    // add shipment (order edition page)
                    add_action( 'wp_ajax_add_shipment', array(&$this, 'add_shipment_callback'));
                    
                    // reset shipping options (order edition page)
                    add_action( 'wp_ajax_reset_shipping', array(&$this, 'reset_shipping_callback'));

                    // delete shipment (order edition page)
                    add_action( 'wp_ajax_delete_shipment', array(&$this, 'delete_shipment_callback'));

                    // send shipment (order edition page)
                    add_action( 'wp_ajax_send_shipment', array(&$this, 'send_shipment_callback'));

                    // get updated relay map link (order edition page)
                    add_action( 'wp_ajax_get_relay_map_link', array(&$this, 'get_relay_map_link_callback'));

                    // get parcel point (order edition page)
                    add_action( 'wp_ajax_get_parcel_point', array(&$this, 'get_parcel_point_callback'));

                    // add product line (simulator page)
                    add_action( 'wp_ajax_add_product_line', array(&$this, 'add_product_line_callback'));

                    // get simulator cart info (simulator page)
                    add_action( 'wp_ajax_get_simulator_cart_info', array(&$this, 'get_simulator_cart_info_callback'));

                    // get states from country (onboarding)
                    add_action( 'wp_ajax_return_states', array(&$this, 'return_states_callback'));

                    // create EMC account (onboarding)
                    add_action( 'wp_ajax_create_account', array(&$this, 'create_account_callback'));

                    // generate API keys (onboarding)
                    add_action( 'wp_ajax_generate_keys', array(&$this, 'generate_keys_callback'));

                    add_action( 'wp_ajax_format_price', array(&$this, 'format_price_callback') );
                    add_action( 'wp_ajax_check_labels', array(&$this, 'check_labels_callback') );
                    add_action( 'wp_ajax_mass_order', array(&$this, 'mass_order_callback') );
                    add_action( 'wp_ajax_download_document', array(&$this, 'download_document_callback') );
				}

				// add Choix Relais
                add_action( 'wp_ajax_get_choix_relais', array(&$this, 'get_choix_relais'));

                // add emc custom statuses to woocommerce sales report
                add_filter( 'woocommerce_reports_order_statuses', array(&$this, 'emc_reports_order_statuses') );

			}

			/*
			 * Fix for PHP 5.2
			 */
			public static function getCalledClass(){

				if ( function_exists('get_called_class') ) {
					return get_called_class();
				}
				else {
					$arr = array();
					$arrTraces = debug_backtrace();
					foreach ($arrTraces as $arrTrace){
						 if(!array_key_exists("class", $arrTrace)) continue;
						 if(count($arr)==0) $arr[] = $arrTrace['class'];
						 else if(get_parent_class($arrTrace['class'])==end($arr)) $arr[] = $arrTrace['class'];
					}
					return end($arr);
				}
			}

			/*
			 * Define EMC Constants
			 */
			private function define_constants() {
				if (!defined('EMC_PLUGIN_FILE')) define( 'EMC_PLUGIN_FILE', __FILE__ );
				if (!defined('EMC_PLUGIN_BASENAME')) define( 'EMC_PLUGIN_BASENAME', plugin_basename( __FILE__ ) );
				if (!defined('EMC_PLATFORM')) define( 'EMC_PLATFORM', $this->platform );
				if (!defined('EMC_VERSION')) define( 'EMC_VERSION', $this->version );

                // API data
                $env = get_option('EMC_ENV');
                define('EMC_MODE', $env);
                define('EMC_USER', get_option('EMC_LOGIN', ''));
                define('EMC_PASS', get_option('EMC_PASS', ''));
                define('EMC_KEY', get_option('EMC_KEY_' . strtoupper($env), ''));
			}

			/**
			 * Include required core files used in admin and on the frontend.
			 */
			public function includes() {
				require_once(WP_PLUGIN_DIR.'/envoimoinscher/envoimoinscher_model.php');
                include_once( 'includes/admin/class-emc-admin-notices.php' );

                // autoload library
                require_once(WP_PLUGIN_DIR. '/envoimoinscher/includes/autoload.php');

				if ( is_admin() ) {
					include_once( 'includes/admin/class-emc-admin-menus.php' );
					include_once( 'includes/admin/meta-boxes/class-emc-meta-box-order-shipping.php' );

                    // Setup/welcome
                    if ( ! empty( $_GET['page'] ) ) {
                        switch ( $_GET['page'] ) {
                            case 'emc-setup' :
                                include_once( 'includes/class-emc-admin-setup-wizard.php' );
                            break;
                        }
                    }
				}
			}

            /**
             * Init background updates
             */
            public static function init_background_updater() {
                include_once( 'includes/class-emc-background-updater.php' );
                self::$background_updater = new EMC_Background_Updater();
            }

            /**
             * Actions to take when plugin initialises.
             */
            public function init() {
                // Add localization
				load_plugin_textdomain('envoimoinscher', FALSE, dirname(plugin_basename(__FILE__)).'/languages/');
            }

			/**
			 * add css.
			 *
			 * @access public
			 * @return void
			 */
			public function add_css() {
				wp_enqueue_style( 'emc_front_styles', plugins_url( '/assets/css/style.css', EMC_PLUGIN_FILE ), array(), EMC_VERSION );
			}


			/* HOOKS */

			/**
			 * function to add description to carrier label display.
			 *
			 * @access public
			 * @param $full_label, $method
			 * @return full_label
			 */
			public function change_shipping_label($full_label, $method){
				// make changes only for emc carriers
				if(in_array( $method->id, envoimoinscher_model::get_enabled_shipping_methods(true) ) ) {
					// get carrier settings
					$carrier_settings = get_option('woocommerce_'.$method->id.'_settings');
					list($operator, $service) = explode('_', $method->id);

					// add logo
					if (get_option('EMC_CARRIER_DISPLAY') == 'yes') {
						$img_file = plugin_dir_path( __FILE__) . 'assets/img/carriers/logo-'.strtolower($operator).'.png';
						if (file_exists($img_file)) {
							$img = plugins_url( '/assets/img/carriers/logo-'.strtolower($operator).'.png', EMC_PLUGIN_FILE );
							$full_label = '<span class="carrier_logo '.$operator.'"><img src="'.$img.'"/></span>' . $full_label;
						}
					}

                    $language = get_locale();

					// add description
					$full_label .= '<br/><span class="description">'.envoimoinscher_model::get_translation($carrier_settings['srv_description'], $language, false).'</span>';

					// add delivery date
					$delivery_date = strtotime( get_option( '_delivery_date_' . $method->id ) );
                    // added default value for option because it's not automatically loaded on install
					$delivery_label = get_option( 'EMC_LABEL_DELIVERY_DATE', array('fr_FR' => 'Livraison prévue le : {DATE}', 'en_US' => 'Delivery on: {DATE}') );

					$string = str_replace( '{DATE}', '<span class="date">' . date_i18n( __( 'l, F jS Y', 'envoimoinscher' ), $delivery_date ) . '</span>', envoimoinscher_model::get_translation($delivery_label, $language, false) );

					if ( $string != '') {
						$full_label .=  '<br/><span class="delivery_date">'.$string.'</span>';
					}

					// Add a parcel points list
                    // can't seem to be able to get page id outside the loop on wp 3.9.2 / wc 2.3.3 so added old fallback based on URL
					if ( envoimoinscher_model::is_checkout_page() && in_array($method->id , WC()->session->get('chosen_shipping_methods') ) ) {
						$service = envoimoinscher_model::get_service_by_carrier_code($method->id);
						// check if carrier has pickup points
                        if ($service->srv_pickup_point) {
                            // check if required fields are filled
                            if (WC()->customer->get_shipping_country() && WC()->customer->get_shipping_city()) {
                                $countries = $this->get_activated_countries();
                                $address_fields = $countries->get_address_fields(WC()->customer->get_shipping_country(), 'shipping_');
                                if((!$address_fields['shipping_state']['required'] || ($address_fields['shipping_state']['required'] && WC()->customer->get_shipping_state()))
                                    && (!$address_fields['shipping_postcode']['required'] || ($address_fields['shipping_postcode']['required'] && WC()->customer->get_shipping_postcode()))) {
                                    $chosen_pickup_point = '';
                                    if (WC()->session) {
                                        $chosen_pickup_point = WC()->session->get('pickup_point_name_'.$method->id);
                                    }
                                    $full_label .=  '<br/><span class="select-parcel" id="parcel_'.$method->id.'">'.__( 'Choose a parcel point', 'envoimoinscher' ).'</span>';
                                    $full_label .=  '<br/><span>'.__( 'Selected ', 'envoimoinscher' ).' : <span id="emc-parcel-client">'.$chosen_pickup_point.'</span></span>';
                                }
                            }
						}
					}
				}

				return $full_label;
			}


			/**
			 * is_available function.
			 *
			 * @access public
			 * @param array $package
			 * @return bool
			 */
			public function is_available( $package ) {
				global $woocommerce;
				$is_available = true;

				if ( $this->enabled == 'no' ) {
					$is_available = false;
				}
				else {

				}

				return apply_filters( 'woocommerce_shipping_' . $this->id . '_is_available', $is_available, $package );
			}

            /**
			 * Adds single order actions
			 */
			public function add_order_action( $actions ) {
				global $post_id;

				// add send action
				$actions['send_orders'] = __( 'Send using Boxtal', 'envoimoinscher' );

				return $actions;
			}

            /**
			 * Sends a single order
			 */
			public function process_send_order( $order ) {

                // ready to process order
                // anti infinite loop back hole system
                remove_action('woocommerce_order_action_send_orders', array( $this, 'process_send_order' ));

                // we send only suborders which have not already been sent
                $already_sent = array();
                $sent = array();
                $errors = array();

                $return = envoimoinscher_model::make_order( $order, null, true );
                $order_id = envoimoinscher_model::envoimoinscher_get_order_id($order);

                $multiparcels = get_post_meta($order_id, '_multiparcels', true);
                foreach ($multiparcels as $multiparcel_info) {
                    $suborder_key = $multiparcel_info['suborder_key'];
                    if (isset($return[$suborder_key]['success'])) {
                        array_push($sent, $suborder_key);
                    } elseif (isset($return[$suborder_key]['already'])) {
                        array_push($already_sent, $suborder_key);
                    } else {
                        array_push(
                            $errors,
                            array(
                                'suborder_key' => $suborder_key,
                                'message' => $return[$suborder_key]['error']
                            )
                        );
                    }
                }

                // adding action back
                add_action('woocommerce_order_action_send_orders', array( &$this, 'process_send_order' ));

                if (!empty($sent)) {
                    $_emc_ref = get_post_meta( $order_id, '_emc_ref', true );
                    foreach ($sent as $suborder_key) {
                        $notice = array(
                            'type' => "simple-notice",
                            'status' => 'success',
                            'message' => 'Suborder %1$s passed with the reference %2$s.',
                            'placeholders' => array(
                                $suborder_key,
                                $_emc_ref[$suborder_key]
                            ),
                            'autodestruct' => 1
                        );
                        emc_admin_notices::add_custom_notice('send_order'.$suborder_key, $notice);
                    }
                }
                if (!empty($already_sent)) {
                    foreach ($already_sent as $suborder_key) {
                        $notice = array(
                            'type' => "simple-notice",
                            'status' => 'notice',
                            'message' => 'Suborder %s was already sent and hasn\'t been resent. You cannot resend suborders, but you can add new shipments using the "Add a shipment" button.',
                            'placeholders' => array(
                                $suborder_key
                            ),
                            'autodestruct' => 1
                        );
                        emc_admin_notices::add_custom_notice('send_order'.$suborder_key, $notice);
                    }
                }
                if (!empty($errors)) {
                    foreach ($errors as $error) {
                        $notice = array(
                            'type' => "simple-notice",
                            'status' => 'failure',
                            'message' => 'Suborder %1$s: %2$s.',
                            'placeholders' => array(
                                $error['suborder_key'],
                                $error['message']
                            ),
                            'autodestruct' => 1
                        );
                        emc_admin_notices::add_custom_notice('send_order'.$suborder_key, $notice);
                    }
                }
			}

			/**
			 * Add EMC meta box on order page
			 */
			public function envoimoinscher_metabox() {
                global $post_id;
                global $pagenow;
                global $post_type;

                if(function_exists('wc_get_order_types')) {
                    foreach ( wc_get_order_types( 'order-meta-boxes' ) as $type ) {
                        $order_type_object = get_post_type_object( $type );
                        add_meta_box( 'envoimoinscher-order-shipping', __( 'Shipping Options - Boxtal', 'envoimoinscher' ), 'EMC_Meta_Box_Order_Shipping::output', $type, 'normal', 'high' );
                    }
                } else {
                    add_meta_box( 'envoimoinscher-order-shipping', __( 'Shipping Options - Boxtal', 'envoimoinscher' ), 'EMC_Meta_Box_Order_Shipping::output', 'shop_order', 'normal', 'high' );
                }

                // address error message on order edition page if address is no longer active
                // TO DO: find a better hook for this
                if ($pagenow == "post.php" && $post_type == "shop_order") {
                    $multiparcels = get_post_meta($post_id, '_multiparcels', true);
                    $is_active = true;
                    if ( $multiparcels != '' ) {
                        foreach ($multiparcels as $multiparcel_info) {
                            if (!envoimoinscher_model::is_address_active($multiparcel_info['address_id'])) {
                                $is_active = false;
                            }
                        }
                        if (!$is_active) {
                            $notice = array(
                                'type' => "simple-notice",
                                'status' => 'failure',
                                'message' => 'One or more of your shipping addresses is no longer active. Please choose a new address.',
                                'placeholders' => array(),
                                'autodestruct' => 1
                            );
                            emc_admin_notices::add_custom_notice("inactive_address", $notice);
                        }
                    }
                }
			}

			/**
			 * Loads parcel point js on checkout page if at least one shipping service has parcel points
			**/
			public function load_pickup_point_js( $checkout ) {
				$lang = array(
					'Unable to load parcel points' => __( 'Unable to load parcel points', 'envoimoinscher' ),
					'I want this pickup point' => __( 'I want this pickup point', 'envoimoinscher' ),
					'From %1 to %2' => __( 'From %1 to %2', 'envoimoinscher' ),
					' and %1 to %2' => __( ' and %1 to %2', 'envoimoinscher' ),
					'day_1' => __( 'monday', 'envoimoinscher' ),
					'day_2' => __( 'tuesday', 'envoimoinscher' ),
					'day_3' => __( 'wednesday', 'envoimoinscher' ),
					'day_4' => __( 'thursday', 'envoimoinscher' ),
					'day_5' => __( 'friday', 'envoimoinscher' ),
					'day_6' => __( 'saturday', 'envoimoinscher' ),
					'day_7' => __( 'sunday', 'envoimoinscher' ),
                    'Opening hours' => __( 'Opening hours', 'envoimoinscher' ),
                    'relayName' => array(
                        'CHRP' => __( 'Choose this Chrono Relais', 'envoimoinscher'),
                        'MONR' => __( 'Choose this Point Relais', 'envoimoinscher'),
                        'IMXE' => __( 'Choose this Point Relais', 'envoimoinscher'),
                        'SOGP' => __( 'Choose this Relais Colis', 'envoimoinscher'),
                        'UPSE' => __( 'Choose this Access Point', 'envoimoinscher'),
                        'PUNT' => __( 'Choose this Punto Pack', 'envoimoinscher'),
                        'default' => __( 'Choose this Relay Point', 'envoimoinscher')
                    ),
                    'noPP' => __( 'Could not find any parcel point for this address', 'envoimoinscher' )
				);

                $map_container = '<div id="emcMap"><div id="emcMapInner"><div class="emcClose" title="'.__( 'Close map', 'envoimoinscher' ).'"></div>'
                  . '<div id="mapContainer"><div id="mapCanvas"></div></div><div id="prContainer"></div></div></div>';
				wp_enqueue_script( 'jquery' );
				wp_enqueue_script( 'gmap', 'https://maps.googleapis.com/maps/api/js?key=AIzaSyBocsqKYLegu9n_3ymbES6az51croGYYOs' );
				wp_enqueue_script( 'emc_shipping', plugins_url( '/assets/js/shipping.js', EMC_PLUGIN_FILE ), array( 'jquery', 'gmap' ), EMC_VERSION , true);
				wp_localize_script( 'emc_shipping', 'plugin_url', plugins_url('', EMC_PLUGIN_FILE) );
				wp_localize_script( 'emc_shipping', 'lang', $lang );
				wp_localize_script( 'emc_shipping', 'map_container', $map_container );
				wp_localize_script( 'emc_shipping', 'ajaxurl', admin_url( 'admin-ajax.php' ) );

				// Add nonce for security
				$ajax_nonce = wp_create_nonce( 'boxtale_emc' );
				wp_localize_script( 'emc_shipping', 'ajax_nonce', $ajax_nonce );
			}

            /**
			 * Ajax callback to get relay points map (admin)
			 */
            public function get_choix_relais(){

                include_once('includes/choix-relais.php');

                wp_die();
            }

            /**
			 * Ajax callback to get pickup points (front)
			 */
			public function get_points_callback(){
				check_ajax_referer( 'boxtale_emc', 'security' );

				echo json_encode($this->get_points($_GET['carrier_code']));

				wp_die();
			}
            
            /**
             * Ajax callback to set pickup point (front)
             */
            public function set_point_callback(){
                check_ajax_referer( 'boxtale_emc', 'security' );

                if (WC()->session) {
                    WC()->session->set("pickup_point_code_".$_REQUEST['carrier_code'], $_REQUEST['pickup_point_code']);
                    WC()->session->set("pickup_point_name_".$_REQUEST['carrier_code'], $_REQUEST['pickup_point_name']);
                    echo true;
                } else {
                    echo false;
                }

                wp_die();
            }

            /**
			 * Ajax callback to add rate line (carrier settings)
			 */
            public function add_rate_line_callback(){
                $row = isset($_GET['row'])?(int)$_GET['row']:0;
                $carrier_code = isset($_GET['carrier_code'])?$_GET['carrier_code']:'';
                $this->add_rate_line($carrier_code, true, array(), $row);
                wp_die();
            }

            /**
			 * Function to add rate line (carrier settings)
			 */
            public function add_rate_line($carrier_code, $ajax = true, $pricing_item = array(), $row = 0){
                if (envoimoinscher_model::is_zones_enabled()) {
                    $zones_countries = envoimoinscher_model::get_zones($carrier_code);
                }
                else {
                    $zones_countries = $this->get_all_countries();
                }
                $shipping_classes = $this->get_shipping_classes();
                include('assets/views/rate_line.php');
            }

            /**
			 * Ajax callback to format an address (account settings)
			 */
            public function format_address_callback(){
                check_ajax_referer( 'boxtale_emc', 'security' );
                // get address by id
                $index = $_POST['index'];
                $address = envoimoinscher_model::get_address_by_id($index);
                echo $this->format_address($address);
                wp_die();
            }

            /**
			 * Function to format an address (account settings)
			 */
            public function format_address($address) {
                echo '<div id="address-'.$address['id'].'" class="address-block" rel="'.$address['id'].'" style="margin-bottom:15px;padding-top: 5px;">';
                echo '<div><b style="font-size:16px;">'.stripslashes($address['label']).'</b>';
                if ($address['type'] == "additional") {
                    echo '<a class="edit-additional-address pointer" title="'. __( 'Edit address', 'envoimoinscher' ).'">';
                    echo '<img alt="'.__( 'Edit', 'envoimoinscher' ).'" src="' . WC()->plugin_url() . '/assets/images/icons/edit.png' . '" style="vertical-align:middle;margin-left:10px;margin-right:10px;" width="20" />';
                    echo '</a>';
                    echo '<a class="remove-additional-address pointer" title="'. __( 'Remove address', 'envoimoinscher' ).'">';
                    echo '<img alt="'.__( 'Remove', 'envoimoinscher' ).'" src="' . plugins_url( '/assets/img/ico-bin.png', EMC_PLUGIN_FILE ) . '" style="vertical-align:middle;margin-right:10px;" width="16" />';
                    echo '</a>';
                } elseif ($address['type'] == "default") {
                    echo '<a class="edit-default-address pointer" title="'. __( 'Edit default address', 'envoimoinscher' ).'">';
                    echo '<img alt="'.__( 'Edit', 'envoimoinscher' ).'" src="' . WC()->plugin_url() . '/assets/images/icons/edit.png' . '" style="vertical-align:middle;margin-left:10px;" width="20" />';
                    echo '</a>';
                }
                echo '</div>';
                echo '<div style="display:inline-block;vertical-align:top;width:300px;margin-right:20px;">';
                echo stripslashes($address['last_name']).' '.stripslashes($address['first_name']).'<br/>';
                echo stripslashes($address['company']).'<br/>';
                echo stripslashes($address['address']).'<br/>';
                echo $address['zipcode'].' '.stripslashes($address['city']).'<br/>';
                $countries = $this->get_all_countries();
                $shipping_country = get_option('EMC_COUNTRY', 'FR');
                if (isset($countries[$shipping_country])) {
                    echo $countries[$shipping_country];
                } else {
                    echo $shipping_country;
                }
                if ($address['add_info'] != "") {
                    echo '<span class="add-info-ellipsis">'.stripslashes($address['add_info']).'</span>';
                }
                echo '</div><div style="display:inline-block;vertical-align:top;margin-right:20px;width:400px;">';
                echo '<img alt="'.__('phone', 'envoimoinscher').'" src="' . plugins_url( '/assets/img/ico-phone.png', EMC_PLUGIN_FILE ) . '" style="vertical-align:middle;margin-right:5px;" />';
                echo $address['phone'].'<br/>';
                echo '<img alt="'.__('email', 'envoimoinscher').'" src="' . plugins_url( '/assets/img/ico-email.png', EMC_PLUGIN_FILE ) . '" style="vertical-align:middle;margin-right:5px;padding-bottom:1px" />';
                echo $address['email'].'<br/>';
                echo '<img alt="'.__('opening and closing hours', 'envoimoinscher').'" src="' . plugins_url( '/assets/img/ico-clock.png', EMC_PLUGIN_FILE ) . '" style="vertical-align:top;margin-right:5px;" />';
                $dispo_hde = explode(':', $address['dispo_hde']);
                $dispo_hle = explode(':', $address['dispo_hle']);
                echo sprintf(__('%1$s:%2$s', 'envoimoinscher'), $dispo_hde[0], $dispo_hde[1]).'-'.sprintf(__('%1$s:%2$s', 'envoimoinscher'), $dispo_hle[0], $dispo_hle[1]).'<br/>';
                echo __('Departure date:', 'envoimoinscher').' ';
                echo '<span style="font-weight:bold">'.sprintf(__('D+%s', 'envoimoinscher'), $address['pickup_day_1']).'</span> ('.sprintf(__('before %s:00', 'envoimoinscher'), $address['pickup_split']).'), ';
                echo '<span style="font-weight:bold">'.sprintf(__('D+%s', 'envoimoinscher'), $address['pickup_day_2']).'</span> ('.sprintf(__('after %s:00', 'envoimoinscher'), $address['pickup_split']).')';
                if ($address['type'] == "additional") {
                    $display_names = array();
                    if ($address['shipping_classes'] != "") {
                        $shipping_classes = EMC()->get_shipping_classes();
                        $shipping_classes_names = array();
                        foreach ($shipping_classes as $class) {
                            $shipping_classes_names[$class->slug] = $class->name;
                        }
                        $current_classes = explode(',', $address['shipping_classes']);
                        foreach ($current_classes as $slug) {
                            $display_names[] = $shipping_classes_names[$slug];
                        }
                    }
                    echo '<br/><span class="add-info-ellipsis">'.__('Shipping classes:', 'envoimoinscher').' '.implode(', ', $display_names).'</span>';
                }
                echo '<input type="hidden" name="hidden-label" value="'.stripslashes($address['label']).'" />';
                echo '<input type="hidden" name="hidden-gender" value="'.$address['gender'].'" />';
                echo '<input type="hidden" name="hidden-first_name" value="'.stripslashes($address['first_name']).'" />';
                echo '<input type="hidden" name="hidden-last_name" value="'.stripslashes($address['last_name']).'" />';
                echo '<input type="hidden" name="hidden-company" value="'.stripslashes($address['company']).'" />';
                echo '<input type="hidden" name="hidden-address" value="'.stripslashes($address['address']).'" />';
                echo '<input type="hidden" name="hidden-zipcode" value="'.$address['zipcode'].'" />';
                echo '<input type="hidden" name="hidden-city" value="'.stripslashes($address['city']).'" />';
                echo '<input type="hidden" name="hidden-phone" value="'.$address['phone'].'" />';
                echo '<input type="hidden" name="hidden-email" value="'.$address['email'].'" />';
                echo '<input type="hidden" name="hidden-add_info" value="'.stripslashes($address['add_info']).'" />';
                echo '<input type="hidden" name="hidden-dispo_hde" value="'.$address['dispo_hde'].'" />';
                echo '<input type="hidden" name="hidden-dispo_hle" value="'.$address['dispo_hle'].'" />';
                echo '<input type="hidden" name="hidden-pickup_day_1" value="'.$address['pickup_day_1'].'" />';
                echo '<input type="hidden" name="hidden-pickup_split" value="'.$address['pickup_split'].'" />';
                echo '<input type="hidden" name="hidden-pickup_day_2" value="'.$address['pickup_day_2'].'" />';
                if ($address['type'] == "additional") {
                    echo '<input type="hidden" name="hidden-shipping_classes" value="'.$address['shipping_classes'].'" />';
                }
                echo '</div>';

                // show parcel points for each operator
                $carrier_list = envoimoinscher_model::get_services();
                $lang = get_locale();
                $dropoff_points = @unserialize($address['dropoff_points']);
                $admin_url = admin_url();
                $baseUrl = $admin_url."/admin-ajax.php?action=get_choix_relais&";
                echo '<div style="margin-top:10px;"><img alt="'.__('dropoff points', 'envoimoinscher').'" src="' . plugins_url( '/assets/img/ico-marker-grey.png', EMC_PLUGIN_FILE ) . '" style="vertical-align:top;margin-right:5px;" />';
                echo '<span>'.__('Default dropoff points:', 'envoimoinscher').'</span>';

                $already_added = array();
                foreach ( $carrier_list as $carrier ) {
                    if ($carrier->srv_dropoff_point == 1 && !in_array($carrier->ope_code, $already_added)) {
                        $PP_link = $baseUrl.'cp='.$address['zipcode'].'&ville='.stripslashes($address['city']).'&country='.$shipping_country.'&ope='.$carrier->ope_code.'&srv='.$carrier->srv_code.'&emc=1&inputCallBack=';
                        echo '<div>';
                        echo envoimoinscher_model::get_translation($carrier->srv_name, $lang, true);
                        echo ' : <input type="text" name="PP-'.$address['id'].'-'.$carrier->ope_code.'" id="PP-'.$address['id'].'-'.$carrier->ope_code.'" value="';
                        echo isset($dropoff_points[$carrier->ope_code]) ? $dropoff_points[$carrier->ope_code]:'';
                        echo '" size="6"/>';
                        echo '<a href="'.$PP_link.'PP-'.$address['id'].'-'.$carrier->ope_code.'" onclick="colorbox(this.href);return false;">';
                        echo '<img alt="'.__( 'Edit', 'envoimoinscher' ).'" src="' . WC()->plugin_url() . '/assets/images/icons/edit.png' . '" style="vertical-align:middle;margin-left:10px;" width="15" />';
                        echo '</a>';
                        echo '</div>';
                        array_push($already_added, $carrier->ope_code);
                    }
                }
                echo '</div></div>';
            }

            /**
			 * Ajax callback to add an address (account settings)
			 */
            public function add_address_callback(){
                check_ajax_referer( 'boxtale_emc', 'security' );
                global $wpdb;
                $index = $_POST['index'];
                $type = $_POST['type'];
                $address = $_POST['address'];
                // case of new address
                if ($index == "") {
                    // prevent default address duplicates
                    if ($type == "default" && envoimoinscher_model::has_default_address() === true) {
                        echo "{'error': 'add_address_fail'}";
                        wp_die();
                    }

                    $result = $wpdb->insert(
                        $wpdb->prefix.'emc_addresses',
                        array(
                            'type' => $type,
                            'active' => 1,
                            'label' => $address['label'],
                            'gender' => $address['gender'],
                            'first_name' => $address['first_name'],
                            'last_name' => $address['last_name'],
                            'company' => $address['company'],
                            'address' => $address['address'],
                            'zipcode' => $address['zipcode'],
                            'city' => $address['city'],
                            'phone' => $address['phone'],
                            'email' => $address['email'],
                            'add_info' => $address['add_info'],
                            'dispo_hde' => $address['dispo_hde'],
                            'dispo_hle' => $address['dispo_hle'],
                            'pickup_day_1' => $address['pickup_day_1'],
                            'pickup_day_2' => $address['pickup_day_2'],
                            'pickup_split' => $address['pickup_split'],
                            'shipping_classes' => isset($address['shipping_classes']) ? $address['shipping_classes']:"",
                            'dropoff_points' => 'a:0:{}',
                        ),
                        array(
                            '%s',
                            '%d',
                            '%s',
                            '%s',
                            '%s',
                            '%s',
                            '%s',
                            '%s',
                            '%s',
                            '%s',
                            '%s',
                            '%s',
                            '%s',
                            '%s',
                            '%s',
                            '%d',
                            '%d',
                            '%d',
                            '%s',
                            '%s',
                        )
                    );
                    if (false == $result) {
                        echo "{'error': 'add_address_fail'}";
                        wp_die();
                    } else {
                        $address['id'] = $wpdb->insert_id;
                        echo json_encode($address);
                        wp_die();
                    }
                // case of existing address
                } else {
                    $result = $wpdb->update(
                        $wpdb->prefix.'emc_addresses',
                        array(
                            'type' => $type,
                            'active' => 1,
                            'label' => $address['label'],
                            'gender' => $address['gender'],
                            'first_name' => $address['first_name'],
                            'last_name' => $address['last_name'],
                            'company' => $address['company'],
                            'address' => $address['address'],
                            'zipcode' => $address['zipcode'],
                            'city' => $address['city'],
                            'phone' => $address['phone'],
                            'email' => $address['email'],
                            'add_info' => $address['add_info'],
                            'dispo_hde' => $address['dispo_hde'],
                            'dispo_hle' => $address['dispo_hle'],
                            'pickup_day_1' => $address['pickup_day_1'],
                            'pickup_day_2' => $address['pickup_day_2'],
                            'pickup_split' => $address['pickup_split'],
                            'shipping_classes' => isset($address['shipping_classes']) ? $address['shipping_classes']:"",
                        ),
                        array(
                            'id' => $index,
                        ),
                        array(
                            '%s',
                            '%d',
                            '%s',
                            '%s',
                            '%s',
                            '%s',
                            '%s',
                            '%s',
                            '%s',
                            '%s',
                            '%s',
                            '%s',
                            '%s',
                            '%s',
                            '%s',
                            '%d',
                            '%d',
                            '%d',
                            '%s',
                        ),
                        array(
                            '%d'
                        )
                    );
                    if ($result === false) {
                        echo "{'error': 'edit_address_fail'}";
                        wp_die();
                    } else {
                        $address['id'] = $index;
                        echo json_encode($address);
                        wp_die();
                    }
                }
            }

            /**
			 * Ajax callback to remove an address (account settings)
			 */
            public function remove_address_callback(){
                check_ajax_referer( 'boxtale_emc', 'security' );
                global $wpdb;
                $index = $_POST['index'];
                $result = $wpdb->update(
                    $wpdb->prefix.'emc_addresses',
                    array(
                        'active' => 0
                    ),
                    array(
                        'id' => $index
                    ),
                    array(
                        '%d'
                    )
                );
                if (false === $result) {
                    echo "{'error': 'remove_address_fail'}";
                    wp_die();
                } else {
                    echo json_encode(array('id' => $index));
                    wp_die();
                }
            }

            /**
			 * Ajax callback to add a shipment (order edition page)
			 */
            public function add_shipment_callback(){
                check_ajax_referer( 'boxtale_emc', 'security' );
                global $wpdb;
                $order_id = $_POST['order_id'];
                $address_id = $_POST['address_id'];
                $products = $_POST['products'];
                $new_suborder_key = uniqid($address_id.'_');
                $multiparcel = array(
                    "suborder_key" => $new_suborder_key,
                    "address_id" => $address_id,
                    "parcels" => array(),
                    "products" => array()
                );

                $weight = 0;
                foreach ($products as $product) {
                    array_push($multiparcel["products"], array('id' => $product['id'], 'qty' => $product['qty']));
                    $product_weight = envoimoinscher_model::get_product_weight($product['id']);
                    $weight += (float)$product_weight * (int)$product['qty'];
                }

                if ($weight == 0) {
                    echo json_encode(array(
                        "error" => __('Your total product weight is 0kg. Shipments cannot be added with zero weight.', 'envoimoinscher'),
                    ));
                    wp_die();
                }

                $dims = envoimoinscher_model::get_dim_from_weight($weight);

                if ($dims === false) {
                    echo json_encode(array(
                        "error" => sprintf(__('Couldn\'t find any dimensions related to shipment weight %s.', 'envoimoinscher'), $weight)
                    ));
                    wp_die();
                }

                $multiparcel['parcels'][1] = array(
                    'weight' => $weight,
                    'length' => $dims->length,
                    'width' => $dims->width,
                    'height' => $dims->height,
                );

                $multiparcels = get_post_meta($order_id, '_multiparcels', true);
                array_push($multiparcels, $multiparcel);
                update_post_meta( $order_id, '_multiparcels', $multiparcels );

                $order = envoimoinscher_model::envoimoinscher_get_order( $order_id );
                envoimoinscher_model::initialize_default_params( $order, $new_suborder_key );

                echo json_encode(true);
                wp_die();
            }
            
            /**
			 * Ajax callback to reset shipping options for order (order edition page)
			 */
            public function reset_shipping_callback(){
                check_ajax_referer( 'boxtale_emc', 'security' );
                global $wpdb;
                $order_id = $_POST['order_id'];
                $order = envoimoinscher_model::envoimoinscher_get_order( $order_id );
                envoimoinscher_model::initialize_default_params($order, null, true);
                echo json_encode(true);
                wp_die();
            }

            /**
			 * Ajax callback to delete a shipment (order edition page)
			 */
            public function delete_shipment_callback(){
                check_ajax_referer( 'boxtale_emc', 'security' );
                global $wpdb;
                $order_id = $_POST['order_id'];
                $suborder_key = $_POST['suborder_key'];
                $multiparcels = get_post_meta($order_id, '_multiparcels', true);

                foreach($multiparcels as $multiparcel_index => $multiparcel_info) {
                    if ($multiparcel_info['suborder_key'] == $suborder_key) {
                        unset($multiparcels[$multiparcel_index]);
                    }
                }
                update_post_meta( $order_id, '_multiparcels', $multiparcels );

                wp_die();
            }

            /**
			 * Ajax callback to send a shipment (order edition page)
			 */
            public function send_shipment_callback() {
                check_ajax_referer( 'boxtale_emc', 'security' );
                $order_id = $_POST['order_id'];
                $suborder_key = $_POST['suborder_key'];

                $order = envoimoinscher_model::envoimoinscher_get_order( $order_id );
                $return = envoimoinscher_model::make_order( $order, $suborder_key, true );

                if( isset($return[$suborder_key]['success']) ) {
                    $_emc_ref = get_post_meta( $order_id, '_emc_ref', true );
                    
                    $notice = array(
                        'type' => "simple-notice",
                        'status' => 'success',
                        'message' => 'Suborder %1$s passed with the reference %2$s.',
                        'placeholders' => array(
                            $suborder_key,
                            $_emc_ref[$suborder_key]
                        ),
                        'autodestruct' => 1
                    );
                    emc_admin_notices::add_custom_notice('send_shipment'.$suborder_key, $notice);
                } elseif(isset($return[$suborder_key]['already'])) {
                    $notice = array(
                        'type' => "simple-notice",
                        'status' => 'notice',
                        'message' => 'Suborder %s was already sent and hasn\'t been resent. You cannot resend suborders, but you can add new shipments using the button below.',
                        'placeholders' => array(
                            $suborder_key
                        ),
                        'autodestruct' => 1
                    );
                    emc_admin_notices::add_custom_notice('send_shipment'.$suborder_key, $notice);
                } else {
                    $notice = array(
                        'type' => "simple-notice",
                        'status' => 'failure',
                        'message' => 'Suborder %1$s: %2$s.',
                        'placeholders' => array(
                            $suborder_key,
                            $return[$suborder_key]['error']
                        ),
                        'autodestruct' => 1
                    );
                    emc_admin_notices::add_custom_notice('send_shipment'.$suborder_key, $notice);
                }
                return true;
            }

            /**
			 * Ajax callback to get updated relay map link (order edition page)
			 */
            public function get_relay_map_link_callback() {
                check_ajax_referer( 'boxtale_emc', 'security' );
                $carrier_code = $_REQUEST['carrier_code'];
                $type = $_REQUEST['type'];
                $service = envoimoinscher_model::get_service_by_carrier_code($carrier_code);
                if ($type == 'dropoff') {
                    if ( $service->srv_dropoff_point == 1 ) {
                        $address_id = $_REQUEST['address_id'];
                        $suborder_key = $_REQUEST['suborder_key'];
                        $addressbook = envoimoinscher_model::get_addresses("all", true);
                        foreach($addressbook as $item) {
                            if ($item['id'] == $address_id) {
                                $address = $item;
                            }
                        }
                        $admin_url = admin_url();
                        $base_url = $admin_url."/admin-ajax.php?action=get_choix_relais&";
                        $link = $base_url.'cp='.$address['zipcode'].'&ville='.stripslashes($address['city']).'&country='.get_option('EMC_COUNTRY', 'FR').'&srv='.$service->srv_code.'&ope='.$service->ope_code.'&inputCallBack=_dropoff_point_'.$suborder_key;
                    } else {
                        $link = false;
                    }
                } else {
                    if ( $service->srv_pickup_point == 1 ) {
                        $suborder_key = $_REQUEST['suborder_key'];
                        $order_id = $_REQUEST['order_id'];
                        $order = envoimoinscher_model::envoimoinscher_get_order($order_id);
                        $admin_url = admin_url();
                        $base_url = $admin_url."/admin-ajax.php?action=get_choix_relais&";
                        $link = $base_url.'cp='.$order->shipping_postcode.'&ville='.stripslashes($order->shipping_city).'&country='.$order->shipping_country.'&srv='.$service->srv_code.'&ope='.$service->ope_code.'&inputCallBack=_pickup_point_'.$suborder_key;
                    } else {
                        $link = false;
                    }
                }
                echo json_encode($link);
                wp_die();
            }

            /**
			 * Ajax callback to get parcel point (order edition page)
			 */
            public function get_parcel_point_callback() {
                check_ajax_referer( 'boxtale_emc', 'security' );
                $carrier_code = $_REQUEST['carrier_code'];
                $order_id = $_REQUEST['order_id'];
                $suborder_key = $_REQUEST['suborder_key'];
                $type = $_REQUEST['type'];
                $service = envoimoinscher_model::get_service_by_carrier_code($carrier_code);
                if ($type == 'dropoff') {
                    if ( $service->srv_dropoff_point == 1 ) {
                        $dropoff_point_carrier = get_post_meta( $order_id, '_dropoff_point_' . $carrier_code , true );
                        $address_id = $_REQUEST['address_id'];

                        // check if address has changed
                        $multiparcels = get_post_meta( $order_id, '_multiparcels', true );
                        $address_changed = false;
                        foreach ($multiparcels as $multiparcel_info) {
                            if ($multiparcel_info['suborder_key'] == $suborder_key) {
                                if ($multiparcel_info['address_id'] != $address_id) {
                                    $address_changed = true;
                                }
                            }
                        }

                        if ( $dropoff_point_carrier != '' && isset($dropoff_point_carrier[$suborder_key])
                            && $dropoff_point_carrier[$suborder_key] != '' && !$address_changed) {
                            $dropoff_point = $dropoff_point_carrier[$suborder_key];
                        } else {
                            $addressbook = envoimoinscher_model::get_addresses("all", true);
                            foreach($addressbook as $item) {
                                if ($item['id'] == $address_id) {
                                    $address = $item;
                                }
                            }
                            $dropoff_points = @unserialize($address['dropoff_points']);
                            list($operator, $service) = explode('_', $carrier_code);
                            $dropoff_point = isset($dropoff_points[$operator]) ? $dropoff_points[$operator] : '';
                        }
                    } else {
                        $dropoff_point = false;
                    }
                    echo json_encode($dropoff_point);
                } else {
                    if ( $service->srv_pickup_point == 1 ) {
                        $pickup_point_carrier = get_post_meta( $order_id, '_pickup_point_' . $carrier_code , true );

                        if ( $pickup_point_carrier != '' && isset($pickup_point_carrier[$suborder_key])
                            && $pickup_point_carrier[$suborder_key] != '' ) {
                            $pickup_point = $pickup_point_carrier[$suborder_key];
                        } else {
                            $pickup_point = '';
                        }
                    } else {
                        $pickup_point = false;
                    }
                    echo json_encode($pickup_point);
                }
                wp_die();
            }

            /**
			 * Ajax callback to add a product line (simulator page)
			 */
            public function add_product_line_callback() {
                check_ajax_referer( 'boxtale_emc', 'security' );
                $product_id = $_POST['product_id'];
                $qty = $_POST['qty'];
                echo self::add_product_line($product_id, $qty);
                wp_die();
            }

            /**
			 * Function to add a product line (simulator page)
			 */
            public function add_product_line($product_id, $qty) {
                global $wpdb;
                $html = '';
                if (function_exists('wc_get_product')) {
                    $product = wc_get_product( $product_id );
                } else {
                    // fix for WC < 2.5
                    $product = WC()->product_factory->get_product( $product_id );
                }

                if ( $product ) {
                    // get title
                    $title = $product->get_title();
                    $product_type = envoimoinscher_model::envoimoinscher_get_product_property($product, 'type');
                    if ( $product_type == 'variation' ) {
                        $parent_id = $product->parent->id;
                        if (function_exists('wc_get_product')) {
                            $parent_product = wc_get_product( $parent_id );
                        } else {
                            // fix for WC < 2.5
                            $parent_product = WC()->product_factory->get_product( $parent_id );
                        }
                        foreach($parent_product->get_available_variations() as $variation) {
                            if ($variation['variation_id'] == $product_id) {
                                foreach($variation['attributes'] as $attributes){
                                    $title .= ' ' . $attributes;
                                }
                            }
                        }
                    }
                    $html .= '<div class="product product-'.$product_id.'" rel="'.$product_id.'">';
                    $html .= '<span class="thumb">'.$product->get_image( 'shop_thumbnail', array( 'title' => '' ) ).'</span>';
                    $html .= $title.' x <span class="qty">'.$qty.'</span>';
                    $html .= '<input type="hidden" name="product-'.$product_id.'" value="'.$product_id.'" />';
                    $html .= '<input type="hidden" name="qty-'.$product_id.'" class="hidden-qty" value="'.$qty.'" />';
                    $html .= '<a class="remove-product-from-simulator" title="'. __( 'Remove product', 'envoimoinscher' ).'" style="cursor:pointer">';
                    $html .= '<img alt="'.__( 'Remove product', 'envoimoinscher' ).'" src="' . plugins_url( '/assets/img/ico-bin.png', EMC_PLUGIN_FILE ) . '" style="vertical-align:middle;margin-left:3px;" width="14" />';
                    $html .= '</a>';
                    $html .= '</div>';
                }
                return $html;
            }

            /**
			 * Ajax callback to get simulator cart info (simulator page)
			 */
            public function get_simulator_cart_info_callback() {
                check_ajax_referer( 'boxtale_emc', 'security' );
                $products = $_POST['products'];
                $return = self::get_simulator_cart_info($products);
                echo json_encode($return);
                wp_die();
            }

            /**
			 * Ajax callback to return states from country
			 */
            public function return_states_callback() {
                check_ajax_referer( 'boxtale_emc', 'security' );
                $id_country = $_POST['id_country'];
                $states = WC()->countries->get_states($id_country);
                if ($states === false) {
                    echo "false";
                    wp_die();
                }
                $html = '';
                foreach ($states as $st => $state) {
                    $html .= '<option value="'.$st.'">'.$state.'</option>';
                }
                if ($html == '') {
                    echo "false";
                } else {
                    echo $html;
                }
                wp_die();
            }

            /**
			 * Ajax callback to create EMC account
			 */
            public function create_account_callback() {
                check_ajax_referer( 'boxtale_emc', 'security' );

                $lib = new Emc\User();
                $lib->setEnv("prod");
                $lib->setLogin("");
                $lib->setPassword("");
                $lib->setKey("");
                $upload_dir = wp_upload_dir();
                $lib->setUploadDir($upload_dir['basedir']);
                $lib->setPlatformParams(EMC_PLATFORM, WC_VERSION, EMC_VERSION);
                $language = get_locale();
                $lib->setLocale(str_replace('_', '-', $language));

                $post_data = array();
                foreach ($_POST['data'] as $field) {
                    $post_data[$field['name']] = $field['value'];
                }

                $password = isset($post_data['user-password']) ? $lib->encryptPassword($post_data['user-password']) : '';

                $params = array(
                    'module' => 'woocommerce',
                    'moduleEMC' => 'on',
                    'facturation.contact_civ' => isset($post_data['facturation-contact_civ']) ? $post_data['facturation-contact_civ'] : '',
                    'facturation.contact_nom' =>isset($post_data['facturation-contact_nom']) ? $post_data['facturation-contact_nom'] : '',
                    'facturation.contact_prenom' => isset($post_data['facturation-contact_prenom']) ? $post_data['facturation-contact_prenom'] : '',
                    'user.logiciel' => 'woocommerce',
                    'user.site_online' => isset($post_data['user-site-online']) ? $post_data['user-site-online'] : '',
                    'user.volumetrie' => isset($post_data['user-volumetrie']) && (isset($post_data['user-site-online']) && $post_data['user-site-online'] == 1) ? $post_data['user-volumetrie'] : '',
                    'user.partner_code' => isset($post_data['user-partner_code']) ? $post_data['user-partner_code'] : '',
                    'user.default_shipping_country' => isset($post_data['user-default_shipping_country']) ? $post_data['user-default_shipping_country'] : '',
                    'facturation.url' => isset($post_data['facturation-url']) ? $post_data['facturation-url'] : '',
                    'facturation.contact_email' => isset($post_data['facturation-contact_email']) ? $post_data['facturation-contact_email'] : '',
                    'facturation.contact_email2' => isset($post_data['facturation-contact_email2']) ? $post_data['facturation-contact_email2'] : '',
                    'user.login' => isset($post_data['user-login']) ? $post_data['user-login'] : '',
                    'user.password' => urlencode($password),
                    'facturation.contact_ste' => isset($post_data['facturation-contact_ste']) ? $post_data['facturation-contact_ste'] : '',
                    'facturation.adresse1' => isset($post_data['facturation-adresse1']) ? $post_data['facturation-adresse1'] : '',
                    'facturation.adresse2' => isset($post_data['facturation-adresse2']) ? $post_data['facturation-adresse2'] : '',
                    'facturation.adresse3' => isset($post_data['facturation-adresse3']) ? $post_data['facturation-adresse3'] : '',
                    'facturation.pays_iso' => isset($post_data['facturation-pays_iso']) ? $post_data['facturation-pays_iso'] : '',
                    'facturation.contact_etat' => isset($post_data['facturation-contact_etat']) ? $post_data['facturation-contact_etat'] : '',
                    'facturation.codepostal' => isset($post_data['facturation-codepostal']) ? $post_data['facturation-codepostal'] : '',
                    'facturation.ville' => isset($post_data['facturation-ville']) ? $post_data['facturation-ville'] : '',
                    'facturation.contact_tel' => isset($post_data['facturation-contact_tel']) ? $post_data['facturation-contact_tel'] : '',
                    'facturation.contact_stesiret' => isset($post_data['facturation-contact_stesiret']) ? $post_data['facturation-contact_stesiret'] : '',
                    'facturation.contact_tvaintra' => isset($post_data['facturation-contact_tvaintra']) ? $post_data['facturation-contact_tvaintra'] : '',
                    'facturation.contact_locale' => isset($post_data['facturation-contact_locale']) ? $post_data['facturation-contact_locale'] : '',
                    'cgv' => isset($post_data['cgv']) ? $post_data['cgv'] : '',
                    'newsletterEmc' => isset($post_data['newsletterEmc']) ? $post_data['newsletterEmc'] : '',
                    'facturation.defaut_enl' => isset($post_data['facturation-defaut_enl']) ? $post_data['facturation-defaut_enl'] : '',
                );
                $response = $lib->postUserSignup($params);
                echo $response;
                wp_die();
            }

            /**
			 * Ajax callback to generate API keys
			 */
            public function generate_keys_callback() {
                check_ajax_referer( 'boxtale_emc', 'security' );

                $lib = new Emc\User();
                $lib->setEnv("prod");
                $lib->setLogin("");
                $lib->setPassword("");
                $lib->setKey("");
                $upload_dir = wp_upload_dir();
                $lib->setUploadDir($upload_dir['basedir']);
                $lib->setPlatformParams(EMC_PLATFORM, WC_VERSION, EMC_VERSION);
                $language = get_locale();
                $lib->setLocale(str_replace('_', '-', $language));

                $post_data = array();
                foreach ($_POST['data'] as $field) {
                    $post_data[$field['name']] = $field['value'];
                }

                $params = array(
                    'facturation.logiciel' => 'woocommerce',
                    'user.site_online' => isset($post_data['user-site-online']) ? $post_data['user-site-online'] : '',
                    'user.volumetrie' => isset($post_data['user-volumetrie']) && (isset($post_data['user-site-online']) && $post_data['user-site-online'] == 1) ? $post_data['user-volumetrie'] : '',
                    'user.login' => isset($post_data['user-login']) ? $post_data['user-login'] : '',
                    'cgv' => isset($post_data['cgv']) ? $post_data['cgv'] : '',
                );
                $response = $lib->postUserKeys($params);
                echo $response;
                wp_die();
            }

            /**
             * Ajax callback to format price
             */
            public function format_price_callback(){
                check_ajax_referer( 'boxtale_emc', 'security' );

                if( isset($_POST['value']) ){
                    if( isset($_POST['tax']) && $_POST['tax'] == "notax" ) {
                        ob_clean();
                        echo wc_price( $_POST['value'], array('ex_tax_label'=>true) );
                    }
                }
                wp_die();
            }

            /**
             * Ajax callback to check documents
             */
            public function check_labels_callback(){
                check_ajax_referer( 'boxtale_emc', 'security' );
                $order_id = $_POST['order_id'];
                $suborder_key = $_POST['suborder_key'];
                $result = array();

                $_label_url = get_post_meta( $order_id, '_label_url', true );
                if (isset($_label_url[$suborder_key])) {
                    $result['label_url'] = $_label_url[$suborder_key];
                }

                $_remise = get_post_meta( $order_id, '_remise', true );
                if (isset($_remise[$suborder_key])) {
                    $result['remise'] = $_remise[$suborder_key];
                }

                $_manifest = get_post_meta( $order_id, '_manifest', true );
                if (isset($_manifest[$suborder_key])) {
                    $result['manifest'] = $_manifest[$suborder_key];
                }

                $_connote = get_post_meta( $order_id, '_connote', true );
                if (isset($_connote[$suborder_key])) {
                    $result['connote'] = $_connote[$suborder_key];
                }

                $_proforma = get_post_meta( $order_id, '_proforma', true );
                if (isset($_proforma[$suborder_key])) {
                    $result['proforma'] = $_proforma[$suborder_key];
                }

                $_b13a = get_post_meta( $order_id, '_b13a', true );
                if (isset($_b13a[$suborder_key])) {
                    $result['b13a'] = $_b13a[$suborder_key];
                }

                if ( !empty($result) ) {
                    echo json_encode($result);
                }
                wp_die();
            }

            /**
             * Ajax callback for download document (order edition page)
             */
            public function download_document_callback() {
                $order_id = isset($_GET['order_id']) ? $_GET['order_id'] : '';
                $suborder_key = isset($_GET['suborder']) ? $_GET['suborder'] : '';
                $type = isset($_GET['type']) ? $_GET['type'] : '';
                if ($order_id == '' || $type == '' || $suborder_key == '') {
                    return;
                }

                $_emc_ref = get_post_meta($order_id, '_emc_ref', true);
                $multiparcels = get_post_meta( $order_id, '_multiparcels', true);

                switch($type) {
                    case 'waybill':
                        $document = get_post_meta($order_id, '_label_url', true);
                        break;

                    case 'delivery_waybill':
                        $document = get_post_meta($order_id, '_remise', true);
                        break;
                }
                $references = array();
                foreach ($multiparcels as $multiparcel_info) {
                    if (isset($multiparcel_info['suborder_key']) && $multiparcel_info['suborder_key'] == $suborder_key
                        && isset($_emc_ref[$multiparcel_info['suborder_key']]) && isset($document[$multiparcel_info['suborder_key']])) {
                        array_push($references, $_emc_ref[$multiparcel_info['suborder_key']]);
                    }
                }
                $this->download_documents( $references, $type);
            }

            /**
             * Ajax callback for mass order
             */
            public function mass_order_callback(){
                check_ajax_referer( 'boxtale_emc', 'security' );

                if( isset($_REQUEST['order_id'])) {
                    $order_id = $_REQUEST['order_id'];

                    $order = envoimoinscher_model::envoimoinscher_get_order( $order_id );

                    $result = envoimoinscher_model::make_order($order, null, true);

                    echo json_encode($result);
                }
                wp_die();
            }

            /**
			 * Function to get simulator cart info (simulator page)
			 */
            public function get_simulator_cart_info($products) {
                $return = array();
                $return['package']['contents'] = array();
                $return['cart_weight'] = 0;
                $return['cart_price'] = 0;
                $return['cart_shipping_classes'] = array();
                foreach ($products as $product_item) {
                    $product_id = $product_item["product_id"];
                    if (function_exists('wc_get_product')) {
                        $product = wc_get_product( $product_id );
                    } else {
                        // fix for WC < 2.5
                        $product = WC()->product_factory->get_product( $product_id );
                    }
                    $mock_item = array(
                        'product_id' => $product_id,
                        'variation_id' => 0,
                        'quantity' => $product_item["qty"],
                        'data' => $product
                    );
                    array_push($return['package']['contents'], $mock_item);

                    // add product weight to cart weight
                    $return['cart_weight'] += envoimoinscher_model::get_product_weight($product_id)*$product_item["qty"];

                    // add product price to cart price
                    $return['cart_price'] += $product->get_price()*$product_item["qty"];

                    // add shipping class to cart_shipping_classes
                    if ($product->get_shipping_class() == '' && !in_array('none', $return['cart_shipping_classes'])) {
                        array_push($return['cart_shipping_classes'], 'none');
                    } elseif(!in_array($product->get_shipping_class(), $return['cart_shipping_classes']) && $product->get_shipping_class() != '') {
                        array_push($return['cart_shipping_classes'], $product->get_shipping_class());
                    }
                }
                return $return;
            }

			/**
			 * Validate the process form
			**/
			public function hook_checkout_process() {
				// Check if the parcel point is needed and if it has been chosen
				if (isset($_REQUEST['shipping_method']))	{
					foreach($_REQUEST['shipping_method'] as $shipping_method) {
						$service = envoimoinscher_model::get_service_by_carrier_code($shipping_method);
						if ($service != false && $service->srv_pickup_point) {
                            if (WC()->session) {
                                $session_pickup_point = WC()->session->get('pickup_point_code_'.$shipping_method, false);
                                if ($session_pickup_point) {
                                    $chosen_pickup_point = $session_pickup_point;
                                }
                            } else if (isset($_REQUEST['_pickup_point'])) {
                                $chosen_pickup_point = $_REQUEST['_pickup_point'];
                            }
							if (!isset($chosen_pickup_point)) {
								wc_add_notice(__('Please select a parcel point','envoimoinscher'),'error');
							}

						}
					}
				}
			}

			/**
			 * Add all necessary data on the order creation after the checkout page
			 */
			public function hook_new_order($order_id) {
				if (isset($_REQUEST['shipping_method']))	{
					foreach($_REQUEST['shipping_method'] as $shipping_method) {
						$service = envoimoinscher_model::get_service_by_carrier_code($shipping_method);
						if ( $service != false ) {
                            // get stored multiparcel info in session
                            $multiparcels = WC()->session->get("multiparcels");
                            update_post_meta($order_id, '_multiparcels', $multiparcels);
                            $emc_carrier = array();
                            $pickup_point = array();

                            foreach ($multiparcels as $multiparcel_info) {

                                $suborder_key = $multiparcel_info['suborder_key'];
                                $emc_carrier[$suborder_key] = wc_clean( $shipping_method );

                                if ($service->srv_pickup_point) {
                                    if (WC()->session) {
                                        $session_pickup_point = WC()->session->get('pickup_point_code_'.$shipping_method, false);
                                        if ($session_pickup_point) {
                                            $chosen_pickup_point = $session_pickup_point;
                                        }
                                    } else if (isset($_REQUEST['_pickup_point'])) {
                                        $chosen_pickup_point = $_REQUEST['_pickup_point'];
                                    }
                                    if (isset($chosen_pickup_point)) {
                                        $pickup_point[$suborder_key] = $chosen_pickup_point;
                                    }
                                }
                            }
                            update_post_meta( $order_id, '_emc_carrier',$emc_carrier );
                            if (!empty($pickup_point)) {
                                update_post_meta( $order_id, '_pickup_point', $pickup_point);
                                // we store client choice permanently in order to initialize new shipments if needed
                                update_post_meta( $order_id, '_pickup_point_client_choice', $chosen_pickup_point);
                                // initialize pickup point for carrier as well
                                update_post_meta( $order_id, '_pickup_point_'.$shipping_method, $pickup_point );
                            }
                            $order = envoimoinscher_model::envoimoinscher_get_order( $order_id );
                            envoimoinscher_model::initialize_default_params($order);
						}
					}
				}
			}


			/**
			 * Activate the module, install it if its tables do not exist
			 */
			public static function activate($network_wide) {
				update_site_option('EMC_NETWORK_ACTIVATED', 'test');
				// multisite activation. See if being activated on the entire network or one site.
				if ( function_exists('is_multisite') && is_multisite() && $network_wide ) {
					global $wpdb;

					// Get this so we can switch back to it later
					$current_blog = $wpdb->blogid;
					// For storing the list of activated blogs
					$activated = array();

					// Get all blogs in the network and activate plugin on each one
					$blog_ids = $wpdb->get_col("SELECT blog_id FROM ".$wpdb->blogs);
					foreach ($blog_ids as $blog_id) {
						switch_to_blog($blog_id);
						self::activate_simple(); // The normal activation function
						$activated[] = $blog_id;
					}

					// Switch back to the current blog
					switch_to_blog($current_blog);

					// Store the array for a later function
					update_site_option('EMC_NETWORK_ACTIVATED', $activated);
				}

				// normal activation
				else {
					self::activate_simple();
				}
			}

			/**
			 * Activate the module, install it if its tables do not exist
			 */
			public static function activate_simple() {

                if ( ! defined( 'EMC_INSTALLING' ) ) {
                    define( 'EMC_INSTALLING', true );
                }

				envoimoinscher_model::create_database();
                envoimoinscher_model::add_database_default_info();
				envoimoinscher_model::create_options();

                // Queue upgrades/setup wizard
                $emc_version = get_option('EMC_VERSION', null);
                $current_db_version = get_option('EMC_DB_VERSION');

                /* for users with no current db version, we assume that their db is up to date, so set it to previous emc_version */
                if ($current_db_version === false) {
                    add_option( 'EMC_DB_VERSION', $emc_version );
                    $current_db_version = $emc_version;
                }

                emc_admin_notices::remove_all_notices();

                // No versions? This is a new install
                if (is_null($emc_version) && is_null($current_db_version)) {
                    emc_admin_notices::add_notice( 'install' );
                    set_transient( '_emc_activation_redirect', 1, 30 );
                }

                if (!is_null($current_db_version) && version_compare($current_db_version, max(array_keys(envoimoinscher_model::$db_updates)), '<')) {
                    emc_admin_notices::add_notice( 'update' );
                } else {
                    envoimoinscher_model::update_db_version();
                }

                update_option('EMC_VERSION', EMC()->version);
			}

			/**
			 * Deactivate the module and flush the offers cache
			 */
			public static function deactivate($network_wide) {
				// multisite deactivation. See if being deactivated on the entire network or one site.
				if ( function_exists('is_multisite') && is_multisite() && $network_wide ) {
					global $wpdb;

					// Get this so we can switch back to it later
					$current_blog = $wpdb->blogid;
					// For storing the list of activated blogs
					$activated = array();

					// Get all blogs in the network and activate plugin on each one
					$blog_ids = $wpdb->get_col("SELECT blog_id FROM ".$wpdb->blogs);
					foreach ($blog_ids as $blog_id) {
						switch_to_blog($blog_id);
						self::deactivate_simple(); // The normal activation function
						$activated[] = $blog_id;
					}

					// Switch back to the current blog
					switch_to_blog($current_blog);

					// Store the array for a later function
					update_site_option('EMC_NETWORK_ACTIVATED', $activated);
				}

				// normal activation
				else {
					self::deactivate_simple();
				}
			}

			/**
			 * Deactivate the module and flush the offers cache
			 */
			public static function deactivate_simple() {
				require_once(WP_PLUGIN_DIR.'/envoimoinscher/envoimoinscher_model.php');
                envoimoinscher_model::flush_user_details_cache();
				envoimoinscher_model::flush_rates_cache();
			}

			/**
			 * Remove completely the plugin from woocommerce
			 */
			public static function uninstall($network_wide) {
				// multisite deactivation. See if being deactivated on the entire network or one site.
				if ( function_exists('is_multisite') && is_multisite() && $network_wide ) {
					global $wpdb;

					// Get this so we can switch back to it later
					$current_blog = $wpdb->blogid;

					// Get all blogs in the network and activate plugin on each one
					$blog_ids = $wpdb->get_col("SELECT blog_id FROM ".$wpdb->blogs);
					foreach ($blog_ids as $blog_id) {
						switch_to_blog($blog_id);
						self::uninstall_simple(); // The normal activation function
					}

					// Switch back to the current blog
					switch_to_blog($current_blog);
				}

				// normal activation
				else {
					self::uninstall_simple();
				}
			}

			/**
			 * Remove completely the plugin from woocommerce
			 */
			public static function uninstall_simple() {
				require_once(WP_PLUGIN_DIR.'/envoimoinscher/envoimoinscher_model.php');

				return envoimoinscher_model::delete_database();
			}

			/**
			 * Runs activation for a plugin on a new site if plugin is already set as network activated on multisite
			 */
			public static function network_activated( $blog_id, $user_id, $domain, $path, $site_id, $meta ) {
				if ( is_plugin_active_for_network( 'envoimoinscher/envoimoinscher.php' ) ) {
					switch_to_blog( $blog_id );
					self::activate_simple();
					restore_current_blog();
				}
			}

			/**
			 * Runs uninstall for a plugin on a multisite site if site is deleted
			 */
			public static function uninstall_multisite_instance( $tables ) {
				global $wpdb;
				$tables[] = $wpdb->prefix . 'emc_categories';
				$tables[] = $wpdb->prefix . 'emc_dimensions';
				$tables[] = $wpdb->prefix . 'emc_operators';
				$tables[] = $wpdb->prefix . 'emc_services';
				return $tables;
			}

            /**
             * Install actions when a update button is clicked within the admin area.
             *
             * This function is hooked into admin_init to affect admin only.
             */
            public static function install_actions() {
                if ( ! empty( $_GET['do_update_envoimoinscher'] ) ) {
                    self::update();
                }
                if ( ! empty( $_GET['force_update_woocommerce'] ) ) {
                    do_action( 'wp_emc_updater_cron' );
                    wp_safe_redirect( admin_url( 'admin.php?page=envoimoinscher-settings' ) );
                }
            }

            /**
             * Push all needed DB updates to the queue for processing.
             */
            private static function update() {
                $current_db_version = get_option( 'EMC_DB_VERSION' );
                $update_queued = false;

                foreach ( envoimoinscher_model::$db_updates as $version => $update_callbacks ) {
                    if ( version_compare( $current_db_version, $version, '<' ) ) {
                        foreach ( $update_callbacks as $update_callback ) {
                            envoimoinscher_model::log( sprintf( 'Emc updates: queuing %s - %s', $version, $update_callback ), true );
                            self::$background_updater->push_to_queue( $update_callback );
                            $update_queued = true;
                        }
                    }
                }

                if ( $update_queued ) {
                    self::$background_updater->save()->dispatch();
                }
            }

            /**
             * Handle redirects to setup/welcome page after install and updates.
             *
             * For setup wizard, transient must be present and we must ignore the network/bulk plugin updaters.
             */
            public function admin_redirects() {
                // Nonced plugin install redirects (whitelisted)
                /*if ( ! empty( $_GET['wc-install-plugin-redirect'] ) ) {
                    $plugin_slug = wc_clean( $_GET['wc-install-plugin-redirect'] );

                    if ( current_user_can( 'install_plugins' ) && in_array( $plugin_slug, array( 'woocommerce-gateway-stripe' ) ) ) {
                        $nonce = wp_create_nonce( 'install-plugin_' . $plugin_slug );
                        $url   = self_admin_url( 'update.php?action=install-plugin&plugin=' . $plugin_slug . '&_wpnonce=' . $nonce );
                    } else {
                        $url = admin_url( 'plugin-install.php?tab=search&type=term&s=' . $plugin_slug );
                    }

                    wp_safe_redirect( $url );
                    exit;
                }*/

                // Setup wizard redirect
                if ( get_transient( '_emc_activation_redirect' ) ) {
                    delete_transient( '_emc_activation_redirect' );

                    if ( ( ! empty( $_GET['page'] ) && in_array( $_GET['page'], array( 'emc-setup' ) ) ) || is_network_admin() || isset( $_GET['activate-multi'] ) ) {
                        return;
                    }

                    // If the user needs to install, send them to the setup wizard
                    if ( emc_admin_notices::has_notice( 'install' ) ) {
                        wp_safe_redirect( admin_url( 'index.php?page=emc-setup' ) );
                        exit;
                    }
                }
            }

			/**
			 * Hook menu shipping backoffice
			 */
			public function envoimoinscher_filter_shipping_methods( $methods ) {

                require_once(WP_PLUGIN_DIR.'/envoimoinscher/includes/envoimoinscher_carrier.php');

				$emc_services = envoimoinscher_model::get_emc_active_shipping_methods();

				foreach ( $emc_services as $key => $value ) {
					if ( ! class_exists($value)){
						eval("class $value extends envoimoinscher_carrier{}");
					}
					if ( !in_array( $value, $methods ) ) {
						$methods[$value] = $value;
					}
				}

				return $methods;
			}

		    /**
			 * Register new status "shipped"
			 */
			public static function register_shipped_order_status() {

				register_post_status( 'wc-awaiting-shipment', array(
					'label'                     => _x( 'Awaiting Shipment', 'Order status', 'envoimoinscher' ),
					'public'                    => true,
					'exclude_from_search'       => false,
					'show_in_admin_all_list'    => true,
					'show_in_admin_status_list' => true,
					'label_count'               => _n_noop( 'Awaiting Shipment <span class="count">(%s)</span>', 'Awaiting Shipment <span class="count">(%s)</span>', 'envoimoinscher' )
				) );
				register_post_status( 'wc-shipped', array(
					'label'                     => _x( 'Shipped', 'Order status', 'envoimoinscher' ),
					'public'                    => true,
					'exclude_from_search'       => false,
					'show_in_admin_all_list'    => true,
					'show_in_admin_status_list' => true,
					'label_count'               => _n_noop( 'Shipped <span class="count">(%s)</span>', 'Shipped <span class="count">(%s)</span>', 'envoimoinscher' )
				) );
			}

			/**
			 * Add 2 new status "shipped" and "awaiting shipment" to list of WC Order statuses
			 */
			public function add_shipped_to_order_statuses( $order_statuses ) {
				$new_order_statuses = array();
				// add new order status after processing
				foreach ( $order_statuses as $key => $status ) {
					$new_order_statuses[ $key ] = $status;
					if ( 'wc-processing' === $key ) {
						$new_order_statuses['wc-awaiting-shipment'] = _x( 'Awaiting Shipment', 'Order status', 'envoimoinscher' );
						$new_order_statuses['wc-shipped'] = _x( 'Shipped', 'Order status', 'envoimoinscher' );
					}
				}
				return $new_order_statuses;
			}

			/**
			 * Add extra bulk action options to mark shipment as complete or processing
			 *
			 * Using Javascript until WordPress core fixes: http://core.trac.wordpress.org/ticket/16031
			 */
			public function add_bulk_action_options() {
				global $post_type;
				if($post_type == 'shop_order') {

					?>
					<script type="text/javascript">
						jQuery(document).ready(function() {
							jQuery('<option>').val('mark_awaiting-shipment').text('<?php echo addslashes(__('Mark awaiting shipment', 'envoimoinscher'));?>').appendTo("select[name='action']");
							jQuery('<option>').val('mark_awaiting-shipment').text('<?php echo addslashes(__('Mark awaiting shipment', 'envoimoinscher'));?>').appendTo("select[name='action2']");

							jQuery('<option>').val('mark_shipped').text('<?php echo addslashes(__('Mark shipped', 'envoimoinscher'));?>').appendTo("select[name='action']");
							jQuery('<option>').val('mark_shipped').text('<?php echo addslashes(__('Mark shipped', 'envoimoinscher'));?>').appendTo("select[name='action2']");

							jQuery('<option>').val('mass_order').text('<?php echo addslashes(__('Send using Boxtal', 'envoimoinscher'));?>').appendTo("select[name='action']");
							jQuery('<option>').val('mass_order').text('<?php echo addslashes(__('Send using Boxtal', 'envoimoinscher'));?>').appendTo("select[name='action2']");

							jQuery('<option>').val('waybills').text('<?php echo addslashes(__('Download all waybills', 'envoimoinscher'));?>').appendTo("select[name='action']");
							jQuery('<option>').val('waybills').text('<?php echo addslashes(__('Download all waybills', 'envoimoinscher'));?>').appendTo("select[name='action2']");

							jQuery('<option>').val('remises').text('<?php echo addslashes(__('Download all delivery waybills', 'envoimoinscher'));?>').appendTo("select[name='action']");
							jQuery('<option>').val('remises').text('<?php echo addslashes(__('Download all delivery waybills', 'envoimoinscher'));?>').appendTo("select[name='action2']");
						});
					</script>
					<?php
				}
			}

			/**
			 * Defines bulk actions
			 */
			function emc_bulk_actions() {

				global $typenow;
				$post_type = $typenow;

				if($post_type == 'shop_order') {

					// check if we're in one of emc bulk actions
					$wp_list_table = _get_list_table( 'WP_Posts_List_Table' );
					$action = $wp_list_table->current_action();
					$allowed_actions = array("waybills", "remises", "mass_order");
					if(!in_array($action, $allowed_actions)) return;

					// check if admin panel
					check_admin_referer('bulk-posts');

					// get ids
					if(isset($_REQUEST['post'])) {
						$post_ids = array_map('intval', $_REQUEST['post']);
					}

					// if no id end there
					if(empty($post_ids)) return;

					// execute action
					switch($action) {
						case 'waybills':
                            $references = array();
                            foreach( $post_ids as $value ) {
                                $_emc_ref = get_post_meta($value, '_emc_ref', true);
                                $_label_url = get_post_meta($value, '_label_url', true);
                                $multiparcels = get_post_meta( $value, '_multiparcels', true);
                                foreach ($multiparcels as $multiparcel_info) {
                                    if (isset($_emc_ref[$multiparcel_info['suborder_key']]) && isset($_label_url[$multiparcel_info['suborder_key']])) {
                                        array_push($references, $_emc_ref[$multiparcel_info['suborder_key']]);
                                    }
                                }
                            }
                            if (!empty($references)) {
                                $this->download_documents( $references, 'waybill');
                            } else {
                                $notice = array(
                                    'type' => "simple-notice",
                                    'status' => 'failure',
                                    'message' => 'Waybills for your orders are not available yet.',
                                    'placeholders' => array(),
                                    'autodestruct' => 1
                                );
                                emc_admin_notices::add_custom_notice('waybill', $notice);
                            }
							break;

						case 'remises':
                            $references = array();
                            foreach( $post_ids as $value ) {
                                $_emc_ref = get_post_meta($value, '_emc_ref', true);
                                $_remise = get_post_meta($value, '_remise', true);
                                $multiparcels = get_post_meta( $value, '_multiparcels', true);
                                foreach ($multiparcels as $multiparcel_info) {
                                    if (isset($_emc_ref[$multiparcel_info['suborder_key']]) && isset($_remise[$multiparcel_info['suborder_key']])) {
                                        array_push($references, $_emc_ref[$multiparcel_info['suborder_key']]);
                                    }
                                }
                            }
                            if (!empty($references)) {
                                $this->download_documents( $references, 'delivery_waybill');
                            } else {
                                $notice = array(
                                    'type' => "simple-notice",
                                    'status' => 'failure',
                                    'message' => 'Delivery waybills for your orders are not available yet.',
                                    'placeholders' => array(),
                                    'autodestruct' => 1
                                );
                                emc_admin_notices::add_custom_notice('delivery_waybill', $notice);
                            }
							break;

						case 'mass_order':
                            if (!empty($post_ids)) {
                                foreach($post_ids as $id) {
                                    $order = envoimoinscher_model::envoimoinscher_get_order($id);
                                    envoimoinscher_model::initialize_default_params($order);
                                }

                                $notice = array(
                                    'type' => "mass-order",
                                    'order_ids' => $post_ids,
                                    'autodestruct' => 1
                                );
                                emc_admin_notices::add_custom_notice('mass_order', $notice);
                            }
							break;

						default: return;
					}
				}
			}

            /**
			 * Downloads documents from order ids.
			 * @param array $references array of Boxtal order references
			 * @param string $type (waybill or delivery_waybill)
			 * @return void
			 */
			public function download_documents($references, $type)  {
                $lib = new Emc\OrderStatus();
                $upload_dir = wp_upload_dir();
                $lib->setUploadDir($upload_dir['basedir']);
                $lib->setPlatformParams(EMC_PLATFORM, WC_VERSION, EMC_VERSION);
                $language = get_locale();
                $lib->setLocale(str_replace('_', '-', $language));
                $filename = ($type == 'delivery_waybill') ? __('delivery-waybill', 'envoimoinscher') : __('waybill', 'envoimoinscher');
                $lib->getOrderDocuments($references, $type);
                header('Content-type: application/pdf');
                header('Content-Disposition: attachment; filename="'.$filename.'.pdf"');
                echo $lib->document;
                die();
            }

			/**
			 * Output custom columns action
			 * @param  array $actions
			 * @return array $new_actions
			 */
			public function custom_add_orders_actions( $actions )  {

				global $post, $the_order;
                $order_id = envoimoinscher_model::envoimoinscher_get_order_id($the_order);

				$new_actions = array();

				if ( empty( $the_order ) || $order_id != $post->ID ) {
                    $the_order = envoimoinscher_model::envoimoinscher_get_order( $post->ID );
				}

                if (isset($actions['processing'])) {
                    $new_actions['processing'] = $actions['processing'];
                }

                if (envoimoinscher_model::order_has_status($the_order, array('pending', 'on-hold', 'processing'))) {
                    if (WC_VERSION >= "2.2.0") {
                        $new_actions['awaiting-shipment'] = array(
                            'url'       => wp_nonce_url( admin_url( 'admin-ajax.php?action=woocommerce_mark_order_status&status=awaiting-shipment&order_id=' . $post->ID ), 'woocommerce-mark-order-status' ),
                            'name'      => __( 'Awaiting Shipment', 'envoimoinscher' ),
                            'action'    => "awaiting-shipment"
                        );
                    }
				}

                if (envoimoinscher_model::order_has_status($the_order, array('pending', 'on-hold', 'processing', 'awaiting-shipment'))) {
					if (WC_VERSION >= "2.2.0") {
                        $new_actions['shipped'] = array(
                            'url'       => wp_nonce_url( admin_url( 'admin-ajax.php?action=woocommerce_mark_order_status&status=shipped&order_id=' . $post->ID ), 'woocommerce-mark-order-status' ),
                            'name'      => __( 'Shipped', 'envoimoinscher' ),
                            'action'    => "shipped"
                        );
                    }
				}

                if (envoimoinscher_model::order_has_status($the_order, array('pending', 'on-hold', 'processing', 'awaiting-shipment', 'shipped'))) {
					if (WC_VERSION >= "2.2.0") {
                        $new_actions['complete'] = array(
                            'url'       => wp_nonce_url( admin_url( 'admin-ajax.php?action=woocommerce_mark_order_status&status=completed&order_id=' . $post->ID ), 'woocommerce-mark-order-status' ),
                            'name'      => __( 'Complete', 'envoimoinscher' ),
                            'action'    => "complete"
                        );
                    }
				}

                // add other custom actions
                foreach ($actions as $key => $action) {
                    if (!isset($new_actions[$key])) {
                        $new_actions[$key] = $actions[$key];
                    }
                }

				return $new_actions;

			}

            /**
			 * Add emc custom statuses to woocommerce reports
			 */
            public function emc_reports_order_statuses ($statuses) {

                if (!is_array($statuses)) {
                    return $statuses;
                }

                $order_status = array();
                $order_status[] = 'awaiting-shipment';
                $order_status[] = 'shipped';
                $new_order_status = array_merge($statuses, $order_status);

                return $new_order_status;
			}

			/**
			 * Get a list of parcel points for the given carrier code
			 * The adress used is the actual recipient address
			 *
			 * @return list of parcel points for this carrier
			 */
			public function get_points($carrier_code) {
				$address = envoimoinscher_model::get_recipient_address();
				return envoimoinscher_model::get_pickup_points($carrier_code,$address['city'],$address['zipcode'],$address['country']);
			}

			/**
			 * check the version of the EMC module.
			 * if not up-to-date, launches activation once again
			 * @access public
			 * @return void
			 */
			public function check_version() {
                if(get_option('EMC_VERSION') !== EMC()->version) {
                    self::activate_simple();
                }
            }

            /**
			 * check API for news, once every 24hrs.
			 * @access public
			 * @return void
			 */
			public function check_news() {

                // check news from EMC on order pages and module pages
                $news = emc_admin_notices::get_news();
                $last_update = get_option('EMC_NEWS_UPDATE', array());
                $language = get_locale();

                if (!isset($last_update[$language]) || time() > ($last_update[$language]+(24 * 60 * 60))) {
                    $lib = new Emc\News();
                    $lib->setLocale(str_replace('_', '-', $language));
                    $upload_dir = wp_upload_dir();
                    $lib->setUploadDir($upload_dir['basedir']);
                    $lib->loadNews(EMC_PLATFORM, EMC_VERSION);

                    foreach ($lib->news as $news_item) {
                        // check if news is already added
                        $md5 = emc_admin_notices::get_news_md5($news_item);
                        $continue = false;
                        foreach ($news as $id => $existing) {
                            $existing_md5 = emc_admin_notices::get_news_md5($existing);
                            if ($existing_md5 == $md5) {
                                // remove from list of messages to check from no longer valid messages
                                unset($news[$id]);
                                $continue = true;
                            }
                        }
                        if ($continue) continue;

                        $notice = array(
                            'type' => 'news',
                            'language' => $language,
                            'status' => $news_item['type'],
                            'message' => $news_item['message'],
                            'message_short' => $news_item['message_short'],
                            'show' => true

                        );
                        emc_admin_notices::add_custom_notice($md5, $notice);
                    }

                    // remove no longer valid news for this language
                    foreach ($news as $id => $left) {
                        if ($left['language'] == $language && $left['type'] == "news") {
                            emc_admin_notices::remove_notice($id);
                        }
                    }

                    // time last update
                    $last_update[$language] = time();
                    update_option('EMC_NEWS_UPDATE', $last_update);
                }
			}

			/**
			 * Get push notifications via woocommerce api
			 */
            public function api_callback_handler() {
				$query_string = $_SERVER['QUERY_STRING'];

				if ( stristr( $query_string, 'type=status' ) ) {
					envoimoinscher_model::insert_documents_links( $query_string );
                } elseif( stristr( $query_string, 'type=tracking' )) {
					envoimoinscher_model::insert_tracking_informations( $query_string );
                }
			}

			/**
			 * Call to api only when city, country and postcode are complete
			 */
			public function limit_cost_calculation() {
				if ( ( (isset($_SERVER['REQUEST_URI']) && stristr(envoimoinscher_model::get_checkout_url(), $_SERVER['REQUEST_URI']))
							|| (isset($_SERVER['HTTP_REFERER']) && stristr(envoimoinscher_model::get_checkout_url(), $_SERVER['HTTP_REFERER'])) ) ) {
                    if ( ! WC()->customer->get_shipping_country() || ! WC()->customer->get_shipping_city()) {
                        return false;
                    } else {
                        $countries = $this->get_activated_countries();
                        $address_fields = $countries->get_address_fields(WC()->customer->get_shipping_country(), 'shipping_');
                        if($address_fields['shipping_state']['required'] && ! WC()->customer->get_shipping_state()) {
                            return false;
                        }
                        if($address_fields['shipping_postcode']['required'] && ! WC()->customer->get_shipping_postcode()) {
                            return false;
                        }
                    }
				}
				return true;
			}

			/**
			 * Get push notifications via woocommerce api
			 */
            public function add_tracking_in_FO ($order) {
                $order_id = envoimoinscher_model::envoimoinscher_get_order_id($order);
                $_emc_carrier = get_post_meta( $order_id, '_emc_carrier', true );

				if( $_emc_carrier != '' ) {
                    $i = 1;
                    $_carrier_ref = get_post_meta( $order_id, '_carrier_ref', true );
                    if ( $_carrier_ref != '' ) {
                        foreach($_emc_carrier as $suborkey => $code) {
                            $carrier_settings = get_option('woocommerce_'.$code.'_settings');
                            if( isset( $carrier_settings['carrier_tracking_url'] ) && $carrier_settings['carrier_tracking_url'] != ''
                                && isset($_carrier_ref[$suborkey]) ) {
                                echo '<div class="carrier_ref">';
                                $link = str_replace ( '@', $_carrier_ref[$suborkey], $carrier_settings['carrier_tracking_url'] );
                                $text = sprintf(__( 'Shipment n°%s: your carrier tracking reference is %s.', 'envoimoinscher' ), $i, '<a href="' . $link . '" target="_blank">' . $_carrier_ref[$suborkey] . '</a>' );
                                echo $text . '</div>';
                                $i++;
                            }
                        }
                    }
				}
			}

            /**
			 * Get activated countries
			 */
            public function get_activated_countries() {
                if ($this->activated_countries == null) {
                    $this->activated_countries = new WC_Countries();
                }
                return $this->activated_countries;
            }

            /**
			 * Get all countries
			 */
            public function get_all_countries() {
                if ($this->all_countries == null) {
                    if(method_exists(WC()->countries, 'get_countries')) {
                        $countries =  WC()->countries->get_countries();
                    } else {
                        $countries =  WC()->countries->countries;
                    }
                }
                return $countries;
            }

            /**
			 * Get shipping classes
			 */
            public function get_shipping_classes() {
                if ($this->shipping_classes == null) {
                    if(method_exists(WC()->shipping, 'get_shipping_classes')) {
                        $shipping_classes =  WC()->shipping->get_shipping_classes();
                    } else {
                        $shipping_classes =  WC()->shipping->shipping_classes;
                    }
                }
                return $shipping_classes;
            }

            /**
             * Get the plugin path.
             * @return string
             */
            public function plugin_path() {
                return untrailingslashit( plugin_dir_path( __FILE__ ) );
            }


		}/* end of class */
}
//end not exist class envoimoinscher


/**
 * Returns the main instance of envoimoinscher.
 * @return envoimoinscher
 */
function EMC() {
	return envoimoinscher::instance();
}

EMC();
