<?php

use Automattic\WooCommerce\Utilities\NumberUtil;
defined('ABSPATH') || exit;
require_once WC_ABSPATH . 'includes/legacy/class-wc-legacy-cart.php';
require_once WC_ABSPATH . 'includes/class-wc-cart-fees.php';
require_once WC_ABSPATH . 'includes/class-wc-cart-session.php';
class WC_Cart extends WC_Legacy_Cart
{
    public $cart_contents = array();
    public $removed_cart_contents = array();
    public $applied_coupons = array();
    protected $shipping_methods;
    protected $default_totals = array('subtotal' => 0, 'subtotal_tax' => 0, 'shipping_total' => 0, 'shipping_tax' => 0, 'shipping_taxes' => array(), 'discount_total' => 0, 'discount_tax' => 0, 'cart_contents_total' => 0, 'cart_contents_tax' => 0, 'cart_contents_taxes' => array(), 'fee_total' => 0, 'fee_tax' => 0, 'fee_taxes' => array(), 'total' => 0, 'total_tax' => 0);
    protected $totals = array();
    protected $session;
    protected $fees_api;
    public function __construct()
    {
        $this->session = new WC_Cart_Session($this);
        $this->fees_api = new WC_Cart_Fees($this);
        $this->session->init();
        add_action('woocommerce_add_to_cart', array($this, 'calculate_totals'), 20, 0);
        add_action('woocommerce_applied_coupon', array($this, 'calculate_totals'), 20, 0);
        add_action('woocommerce_cart_item_removed', array($this, 'calculate_totals'), 20, 0);
        add_action('woocommerce_cart_item_restored', array($this, 'calculate_totals'), 20, 0);
        add_action('woocommerce_check_cart_items', array($this, 'check_cart_items'), 1);
        add_action('woocommerce_check_cart_items', array($this, 'check_cart_coupons'), 1);
        add_action('woocommerce_after_checkout_validation', array($this, 'check_customer_coupons'), 1, 2);
    }
    public function __clone()
    {
        $this->session = clone $this->session;
        $this->fees_api = clone $this->fees_api;
    }
    public function get_cart_contents()
    {
        return apply_filters('woocommerce_get_cart_contents', (array) $this->cart_contents);
    }
    public function get_removed_cart_contents()
    {
        return (array) $this->removed_cart_contents;
    }
    public function get_applied_coupons()
    {
        return (array) $this->applied_coupons;
    }
    public function get_coupon_discount_totals()
    {
        return (array) $this->coupon_discount_totals;
    }
    public function get_coupon_discount_tax_totals()
    {
        return (array) $this->coupon_discount_tax_totals;
    }
    public function get_totals()
    {
        return empty($this->totals) ? $this->default_totals : $this->totals;
    }
    protected function get_totals_var($key)
    {
        return isset($this->totals[$key]) ? $this->totals[$key] : $this->default_totals[$key];
    }
    public function get_subtotal()
    {
        return apply_filters('woocommerce_cart_' . __FUNCTION__, $this->get_totals_var('subtotal'));
    }
    public function get_subtotal_tax()
    {
        return apply_filters('woocommerce_cart_' . __FUNCTION__, $this->get_totals_var('subtotal_tax'));
    }
    public function get_discount_total()
    {
        return apply_filters('woocommerce_cart_' . __FUNCTION__, $this->get_totals_var('discount_total'));
    }
    public function get_discount_tax()
    {
        return apply_filters('woocommerce_cart_' . __FUNCTION__, $this->get_totals_var('discount_tax'));
    }
    public function get_shipping_total()
    {
        return apply_filters('woocommerce_cart_' . __FUNCTION__, $this->get_totals_var('shipping_total'));
    }
    public function get_shipping_tax()
    {
        return apply_filters('woocommerce_cart_' . __FUNCTION__, $this->get_totals_var('shipping_tax'));
    }
    public function get_cart_contents_total()
    {
        return apply_filters('woocommerce_cart_' . __FUNCTION__, $this->get_totals_var('cart_contents_total'));
    }
    public function get_cart_contents_tax()
    {
        return apply_filters('woocommerce_cart_' . __FUNCTION__, $this->get_totals_var('cart_contents_tax'));
    }
    public function get_total($context = 'view')
    {
        $total = apply_filters('woocommerce_cart_' . __FUNCTION__, $this->get_totals_var('total'));
        return 'view' === $context ? apply_filters('woocommerce_cart_total', wc_price($total)) : $total;
    }
    public function get_total_tax()
    {
        return apply_filters('woocommerce_cart_' . __FUNCTION__, $this->get_totals_var('total_tax'));
    }
    public function get_fee_total()
    {
        return apply_filters('woocommerce_cart_' . __FUNCTION__, $this->get_totals_var('fee_total'));
    }
    public function get_fee_tax()
    {
        return apply_filters('woocommerce_cart_' . __FUNCTION__, $this->get_totals_var('fee_tax'));
    }
    public function get_shipping_taxes()
    {
        return apply_filters('woocommerce_cart_' . __FUNCTION__, $this->get_totals_var('shipping_taxes'));
    }
    public function get_cart_contents_taxes()
    {
        return apply_filters('woocommerce_cart_' . __FUNCTION__, $this->get_totals_var('cart_contents_taxes'));
    }
    public function get_fee_taxes()
    {
        return apply_filters('woocommerce_cart_' . __FUNCTION__, $this->get_totals_var('fee_taxes'));
    }
    public function display_prices_including_tax()
    {
        return apply_filters('woocommerce_cart_' . __FUNCTION__, 'incl' === $this->get_tax_price_display_mode());
    }
    public function set_cart_contents($value)
    {
        $this->cart_contents = (array) $value;
    }
    public function set_removed_cart_contents($value = array())
    {
        $this->removed_cart_contents = (array) $value;
    }
    public function set_applied_coupons($value = array())
    {
        $this->applied_coupons = (array) $value;
    }
    public function set_coupon_discount_totals($value = array())
    {
        $this->coupon_discount_totals = (array) $value;
    }
    public function set_coupon_discount_tax_totals($value = array())
    {
        $this->coupon_discount_tax_totals = (array) $value;
    }
    public function set_totals($value = array())
    {
        $this->totals = wp_parse_args($value, $this->default_totals);
    }
    public function set_subtotal($value)
    {
        $this->totals['subtotal'] = wc_format_decimal($value);
    }
    public function set_subtotal_tax($value)
    {
        $this->totals['subtotal_tax'] = $value;
    }
    public function set_discount_total($value)
    {
        $this->totals['discount_total'] = $value;
    }
    public function set_discount_tax($value)
    {
        $this->totals['discount_tax'] = $value;
    }
    public function set_shipping_total($value)
    {
        $this->totals['shipping_total'] = wc_format_decimal($value);
    }
    public function set_shipping_tax($value)
    {
        $this->totals['shipping_tax'] = $value;
    }
    public function set_cart_contents_total($value)
    {
        $this->totals['cart_contents_total'] = wc_format_decimal($value);
    }
    public function set_cart_contents_tax($value)
    {
        $this->totals['cart_contents_tax'] = $value;
    }
    public function set_total($value)
    {
        $this->totals['total'] = wc_format_decimal($value, wc_get_price_decimals());
    }
    public function set_total_tax($value)
    {
        $this->totals['total_tax'] = wc_round_tax_total($value);
    }
    public function set_fee_total($value)
    {
        $this->totals['fee_total'] = wc_format_decimal($value);
    }
    public function set_fee_tax($value)
    {
        $this->totals['fee_tax'] = $value;
    }
    public function set_shipping_taxes($value)
    {
        $this->totals['shipping_taxes'] = (array) $value;
    }
    public function set_cart_contents_taxes($value)
    {
        $this->totals['cart_contents_taxes'] = (array) $value;
    }
    public function set_fee_taxes($value)
    {
        $this->totals['fee_taxes'] = (array) $value;
    }
    public function get_taxes()
    {
        return apply_filters('woocommerce_cart_get_taxes', wc_array_merge_recursive_numeric($this->get_shipping_taxes(), $this->get_cart_contents_taxes(), $this->get_fee_taxes()), $this);
    }
    public function get_cart()
    {
        if (!did_action('wp_loaded')) {
            wc_doing_it_wrong(__FUNCTION__, __('Get cart should not be called before the wp_loaded action.', 'woocommerce'), '2.3');
        }
        if (!did_action('woocommerce_load_cart_from_session')) {
            $this->session->get_cart_from_session();
        }
        return array_filter($this->get_cart_contents());
    }
    public function get_cart_item($item_key)
    {
        return isset($this->cart_contents[$item_key]) ? $this->cart_contents[$item_key] : array();
    }
    public function is_empty()
    {
        return 0 === count($this->get_cart());
    }
    public function empty_cart($clear_persistent_cart = true)
    {
        do_action('woocommerce_before_cart_emptied', $clear_persistent_cart);
        $this->cart_contents = array();
        $this->removed_cart_contents = array();
        $this->shipping_methods = array();
        $this->coupon_discount_totals = array();
        $this->coupon_discount_tax_totals = array();
        $this->applied_coupons = array();
        $this->totals = $this->default_totals;
        if ($clear_persistent_cart) {
            $this->session->persistent_cart_destroy();
        }
        $this->fees_api->remove_all_fees();
        do_action('woocommerce_cart_emptied', $clear_persistent_cart);
    }
    public function get_cart_contents_count()
    {
        return apply_filters('woocommerce_cart_contents_count', array_sum(wp_list_pluck($this->get_cart(), 'quantity')));
    }
    public function get_cart_contents_weight()
    {
        $weight = 0.0;
        foreach ($this->get_cart() as $cart_item_key => $values) {
            if ($values['data']->has_weight()) {
                $weight += (float) $values['data']->get_weight() * $values['quantity'];
            }
        }
        return apply_filters('woocommerce_cart_contents_weight', $weight);
    }
    public function get_cart_item_quantities()
    {
        $quantities = array();
        foreach ($this->get_cart() as $cart_item_key => $values) {
            $product = $values['data'];
            $quantities[$product->get_stock_managed_by_id()] = isset($quantities[$product->get_stock_managed_by_id()]) ? $quantities[$product->get_stock_managed_by_id()] + $values['quantity'] : $values['quantity'];
        }
        return $quantities;
    }
    public function check_cart_items()
    {
        $return = true;
        $result = $this->check_cart_item_validity();
        if (is_wp_error($result)) {
            wc_add_notice($result->get_error_message(), 'error');
            $return = false;
        }
        $result = $this->check_cart_item_stock();
        if (is_wp_error($result)) {
            wc_add_notice($result->get_error_message(), 'error');
            $return = false;
        }
        return $return;
    }
    public function check_cart_coupons()
    {
        foreach ($this->get_applied_coupons() as $code) {
            $coupon = new WC_Coupon($code);
            if (!$coupon->is_valid()) {
                $coupon->add_coupon_message(WC_Coupon::E_WC_COUPON_INVALID_REMOVED);
                $this->remove_coupon($code);
            }
        }
    }
    public function check_cart_item_validity()
    {
        $return = true;
        foreach ($this->get_cart() as $cart_item_key => $values) {
            $product = $values['data'];
            if (!$product || !$product->exists() || 'trash' === $product->get_status()) {
                $this->set_quantity($cart_item_key, 0);
                $return = new WP_Error('invalid', __('An item which is no longer available was removed from your cart.', 'woocommerce'));
            }
        }
        return $return;
    }
    public function check_cart_item_stock()
    {
        $error = new WP_Error();
        $product_qty_in_cart = $this->get_cart_item_quantities();
        $current_session_order_id = isset(WC()->session->order_awaiting_payment) ? absint(WC()->session->order_awaiting_payment) : 0;
        foreach ($this->get_cart() as $cart_item_key => $values) {
            $product = $values['data'];
            if (!$product->is_in_stock()) {
                $error->add('out-of-stock', sprintf(__('Sorry, "%s" is not in stock. Please edit your cart and try again. We apologize for any inconvenience caused.', 'woocommerce'), $product->get_name()));
                return $error;
            }
            if (!$product->managing_stock() || $product->backorders_allowed()) {
                continue;
            }
            $held_stock = wc_get_held_stock_quantity($product, $current_session_order_id);
            $required_stock = $product_qty_in_cart[$product->get_stock_managed_by_id()];
            if (apply_filters('woocommerce_cart_item_required_stock_is_not_enough', $product->get_stock_quantity() < $held_stock + $required_stock, $product, $values)) {
                $error->add('out-of-stock', sprintf(__('Sorry, we do not have enough "%1$s" in stock to fulfill your order (%2$s available). We apologize for any inconvenience caused.', 'woocommerce'), $product->get_name(), wc_format_stock_quantity_for_display($product->get_stock_quantity() - $held_stock, $product)));
                return $error;
            }
        }
        return true;
    }
    public function get_item_data($cart_item, $flat = false)
    {
        wc_deprecated_function('WC_Cart::get_item_data', '3.3', 'wc_get_formatted_cart_item_data');
        return wc_get_formatted_cart_item_data($cart_item, $flat);
    }
    public function get_cross_sells()
    {
        $cross_sells = array();
        $in_cart = array();
        if (!$this->is_empty()) {
            foreach ($this->get_cart() as $cart_item_key => $values) {
                if ($values['quantity'] > 0) {
                    $cross_sells = array_merge($values['data']->get_cross_sell_ids(), $cross_sells);
                    $in_cart[] = $values['product_id'];
                }
            }
        }
        $cross_sells = array_diff($cross_sells, $in_cart);
        return apply_filters('woocommerce_cart_crosssell_ids', wp_parse_id_list($cross_sells), $this);
    }
    public function get_remove_url($cart_item_key)
    {
        wc_deprecated_function('WC_Cart::get_remove_url', '3.3', 'wc_get_cart_remove_url');
        return wc_get_cart_remove_url($cart_item_key);
    }
    public function get_undo_url($cart_item_key)
    {
        wc_deprecated_function('WC_Cart::get_undo_url', '3.3', 'wc_get_cart_undo_url');
        return wc_get_cart_undo_url($cart_item_key);
    }
    public function get_tax_totals()
    {
        $shipping_taxes = $this->get_shipping_taxes();
        $taxes = $this->get_taxes();
        $tax_totals = array();
        foreach ($taxes as $key => $tax) {
            $code = WC_Tax::get_rate_code($key);
            if ($code || apply_filters('woocommerce_cart_remove_taxes_zero_rate_id', 'zero-rated') === $key) {
                if (!isset($tax_totals[$code])) {
                    $tax_totals[$code] = new stdClass();
                    $tax_totals[$code]->amount = 0;
                }
                $tax_totals[$code]->tax_rate_id = $key;
                $tax_totals[$code]->is_compound = WC_Tax::is_compound($key);
                $tax_totals[$code]->label = WC_Tax::get_rate_label($key);
                if (isset($shipping_taxes[$key])) {
                    $tax -= $shipping_taxes[$key];
                    $tax = wc_round_tax_total($tax);
                    $tax += NumberUtil::round($shipping_taxes[$key], wc_get_price_decimals());
                    unset($shipping_taxes[$key]);
                }
                $tax_totals[$code]->amount += wc_round_tax_total($tax);
                $tax_totals[$code]->formatted_amount = wc_price($tax_totals[$code]->amount);
            }
        }
        if (apply_filters('woocommerce_cart_hide_zero_taxes', true)) {
            $amounts = array_filter(wp_list_pluck($tax_totals, 'amount'));
            $tax_totals = array_intersect_key($tax_totals, $amounts);
        }
        return apply_filters('woocommerce_cart_tax_totals', $tax_totals, $this);
    }
    public function get_cart_item_tax_classes()
    {
        $found_tax_classes = array();
        foreach (WC()->cart->get_cart() as $item) {
            if ($item['data'] && ($item['data']->is_taxable() || $item['data']->is_shipping_taxable())) {
                $found_tax_classes[] = $item['data']->get_tax_class();
            }
        }
        return array_unique($found_tax_classes);
    }
    public function get_cart_item_tax_classes_for_shipping()
    {
        $found_tax_classes = array();
        foreach (WC()->cart->get_cart() as $item) {
            if ($item['data'] && $item['data']->is_shipping_taxable()) {
                $found_tax_classes[] = $item['data']->get_tax_class();
            }
        }
        return array_unique($found_tax_classes);
    }
    public function get_displayed_subtotal()
    {
        return $this->display_prices_including_tax() ? $this->get_subtotal() + $this->get_subtotal_tax() : $this->get_subtotal();
    }
    public function find_product_in_cart($cart_id = false)
    {
        if (false !== $cart_id) {
            if (is_array($this->cart_contents) && isset($this->cart_contents[$cart_id])) {
                return $cart_id;
            }
        }
        return '';
    }
    public function generate_cart_id($product_id, $variation_id = 0, $variation = array(), $cart_item_data = array())
    {
        $id_parts = array($product_id);
        if ($variation_id && 0 !== $variation_id) {
            $id_parts[] = $variation_id;
        }
        if (is_array($variation) && !empty($variation)) {
            $variation_key = '';
            foreach ($variation as $key => $value) {
                $variation_key .= trim($key) . trim($value);
            }
            $id_parts[] = $variation_key;
        }
        if (is_array($cart_item_data) && !empty($cart_item_data)) {
            $cart_item_data_key = '';
            foreach ($cart_item_data as $key => $value) {
                if (is_array($value) || is_object($value)) {
                    $value = http_build_query($value);
                }
                $cart_item_data_key .= trim($key) . trim($value);
            }
            $id_parts[] = $cart_item_data_key;
        }
        return apply_filters('woocommerce_cart_id', md5(implode('_', $id_parts)), $product_id, $variation_id, $variation, $cart_item_data);
    }
    public function add_to_cart($product_id = 0, $quantity = 1, $variation_id = 0, $variation = array(), $cart_item_data = array())
    {
        try {
            $product_id = absint($product_id);
            $variation_id = absint($variation_id);
            if ('product_variation' === get_post_type($product_id)) {
                $variation_id = $product_id;
                $product_id = wp_get_post_parent_id($variation_id);
            }
            $product_data = wc_get_product($variation_id ? $variation_id : $product_id);
            $quantity = apply_filters('woocommerce_add_to_cart_quantity', $quantity, $product_id);
            if ($quantity <= 0 || !$product_data || 'trash' === $product_data->get_status()) {
                return false;
            }
            if ($product_data->is_type('variation')) {
                $missing_attributes = array();
                $parent_data = wc_get_product($product_data->get_parent_id());
                $variation_attributes = $product_data->get_variation_attributes();
                $variation_attributes = array_filter($variation_attributes);
                $posted_attributes = array();
                foreach ($parent_data->get_attributes() as $attribute) {
                    if (!$attribute['is_variation']) {
                        continue;
                    }
                    $attribute_key = 'attribute_' . sanitize_title($attribute['name']);
                    if (isset($variation[$attribute_key])) {
                        if ($attribute['is_taxonomy']) {
                            $value = sanitize_title(wp_unslash($variation[$attribute_key]));
                        } else {
                            $value = html_entity_decode(wc_clean(wp_unslash($variation[$attribute_key])), ENT_QUOTES, get_bloginfo('charset'));
                        }
                        if (!empty($value) || '0' === $value) {
                            $posted_attributes[$attribute_key] = $value;
                        }
                    }
                }
                $posted_and_variation_attributes = array_merge($variation_attributes, $posted_attributes);
                if (empty($variation_id)) {
                    $data_store = WC_Data_Store::load('product');
                    $variation_id = $data_store->find_matching_product_variation($parent_data, $posted_attributes);
                }
                if (empty($variation_id)) {
                    throw new Exception(__('Please choose product options&hellip;', 'woocommerce'));
                }
                $variation_data = wc_get_product_variation_attributes($variation_id);
                $attributes = array();
                foreach ($parent_data->get_attributes() as $attribute) {
                    if (!$attribute['is_variation']) {
                        continue;
                    }
                    $attribute_key = 'attribute_' . sanitize_title($attribute['name']);
                    $valid_value = isset($variation_data[$attribute_key]) ? $variation_data[$attribute_key] : '';
                    if (isset($posted_and_variation_attributes[$attribute_key])) {
                        $value = $posted_and_variation_attributes[$attribute_key];
                        if ($valid_value === $value) {
                            $attributes[$attribute_key] = $value;
                        } elseif ('' === $valid_value && in_array($value, $attribute->get_slugs(), true)) {
                            $attributes[$attribute_key] = $value;
                        } else {
                            throw new Exception(sprintf(__('Invalid value posted for %s', 'woocommerce'), wc_attribute_label($attribute['name'])));
                        }
                    } elseif ('' === $valid_value) {
                        $missing_attributes[] = wc_attribute_label($attribute['name']);
                    }
                    $variation = $attributes;
                }
                if (!empty($missing_attributes)) {
                    throw new Exception(sprintf(_n('%s is a required field', '%s are required fields', count($missing_attributes), 'woocommerce'), wc_format_list_of_items($missing_attributes)));
                }
            }
            if (0 < $variation_id && (!$product_data->is_type('variation') || $product_data->get_parent_id() !== $product_id)) {
                $product = wc_get_product($product_id);
                throw new Exception(sprintf(__('The selected product isn\'t a variation of %2$s, please choose product options by visiting <a href="%1$s" title="%2$s">%2$s</a>.', 'woocommerce'), esc_url($product->get_permalink()), esc_html($product->get_name())));
            }
            $cart_item_data = (array) apply_filters('woocommerce_add_cart_item_data', $cart_item_data, $product_id, $variation_id, $quantity);
            $cart_id = $this->generate_cart_id($product_id, $variation_id, $variation, $cart_item_data);
            $cart_item_key = $this->find_product_in_cart($cart_id);
            if ($product_data->is_sold_individually()) {
                $quantity = apply_filters('woocommerce_add_to_cart_sold_individually_quantity', 1, $quantity, $product_id, $variation_id, $cart_item_data);
                $found_in_cart = apply_filters('woocommerce_add_to_cart_sold_individually_found_in_cart', $cart_item_key && $this->cart_contents[$cart_item_key]['quantity'] > 0, $product_id, $variation_id, $cart_item_data, $cart_id);
                if ($found_in_cart) {
                    $message = sprintf(__('You cannot add another "%s" to your cart.', 'woocommerce'), $product_data->get_name());
                    $message = apply_filters('woocommerce_cart_product_cannot_add_another_message', $message, $product_data);
                    throw new Exception(sprintf('<a href="%s" class="button wc-forward">%s</a> %s', wc_get_cart_url(), __('View cart', 'woocommerce'), $message));
                }
            }
            if (!$product_data->is_purchasable()) {
                $message = __('Sorry, this product cannot be purchased.', 'woocommerce');
                $message = apply_filters('woocommerce_cart_product_cannot_be_purchased_message', $message, $product_data);
                throw new Exception($message);
            }
            if (!$product_data->is_in_stock()) {
                $message = sprintf(__('You cannot add &quot;%s&quot; to the cart because the product is out of stock.', 'woocommerce'), $product_data->get_name());
                $message = apply_filters('woocommerce_cart_product_out_of_stock_message', $message, $product_data);
                throw new Exception($message);
            }
            if (!$product_data->has_enough_stock($quantity)) {
                $stock_quantity = $product_data->get_stock_quantity();
                $message = sprintf(__('You cannot add that amount of &quot;%1$s&quot; to the cart because there is not enough stock (%2$s remaining).', 'woocommerce'), $product_data->get_name(), wc_format_stock_quantity_for_display($stock_quantity, $product_data));
                $message = apply_filters('woocommerce_cart_product_not_enough_stock_message', $message, $product_data, $stock_quantity);
                throw new Exception($message);
            }
            if ($product_data->managing_stock()) {
                $products_qty_in_cart = $this->get_cart_item_quantities();
                if (isset($products_qty_in_cart[$product_data->get_stock_managed_by_id()]) && !$product_data->has_enough_stock($products_qty_in_cart[$product_data->get_stock_managed_by_id()] + $quantity)) {
                    $stock_quantity = $product_data->get_stock_quantity();
                    $stock_quantity_in_cart = $products_qty_in_cart[$product_data->get_stock_managed_by_id()];
                    $message = sprintf('<a href="%s" class="button wc-forward">%s</a> %s', wc_get_cart_url(), __('View cart', 'woocommerce'), sprintf(__('You cannot add that amount to the cart &mdash; we have %1$s in stock and you already have %2$s in your cart.', 'woocommerce'), wc_format_stock_quantity_for_display($stock_quantity, $product_data), wc_format_stock_quantity_for_display($stock_quantity_in_cart, $product_data)));
                    $message = apply_filters('woocommerce_cart_product_not_enough_stock_already_in_cart_message', $message, $product_data, $stock_quantity, $stock_quantity_in_cart);
                    throw new Exception($message);
                }
            }
            if ($cart_item_key) {
                $new_quantity = $quantity + $this->cart_contents[$cart_item_key]['quantity'];
                $this->set_quantity($cart_item_key, $new_quantity, false);
            } else {
                $cart_item_key = $cart_id;
                $this->cart_contents[$cart_item_key] = apply_filters('woocommerce_add_cart_item', array_merge($cart_item_data, array('key' => $cart_item_key, 'product_id' => $product_id, 'variation_id' => $variation_id, 'variation' => $variation, 'quantity' => $quantity, 'data' => $product_data, 'data_hash' => wc_get_cart_item_data_hash($product_data))), $cart_item_key);
            }
            $this->cart_contents = apply_filters('woocommerce_cart_contents_changed', $this->cart_contents);
            do_action('woocommerce_add_to_cart', $cart_item_key, $product_id, $quantity, $variation_id, $variation, $cart_item_data);
            return $cart_item_key;
        } catch (Exception $e) {
            if ($e->getMessage()) {
                wc_add_notice($e->getMessage(), 'error');
            }
            return false;
        }
    }
    public function remove_cart_item($cart_item_key)
    {
        if (isset($this->cart_contents[$cart_item_key])) {
            $this->removed_cart_contents[$cart_item_key] = $this->cart_contents[$cart_item_key];
            unset($this->removed_cart_contents[$cart_item_key]['data']);
            do_action('woocommerce_remove_cart_item', $cart_item_key, $this);
            unset($this->cart_contents[$cart_item_key]);
            do_action('woocommerce_cart_item_removed', $cart_item_key, $this);
            return true;
        }
        return false;
    }
    public function restore_cart_item($cart_item_key)
    {
        if (isset($this->removed_cart_contents[$cart_item_key])) {
            $restore_item = $this->removed_cart_contents[$cart_item_key];
            $this->cart_contents[$cart_item_key] = $restore_item;
            $this->cart_contents[$cart_item_key]['data'] = wc_get_product($restore_item['variation_id'] ? $restore_item['variation_id'] : $restore_item['product_id']);
            do_action('woocommerce_restore_cart_item', $cart_item_key, $this);
            unset($this->removed_cart_contents[$cart_item_key]);
            do_action('woocommerce_cart_item_restored', $cart_item_key, $this);
            return true;
        }
        return false;
    }
    public function set_quantity($cart_item_key, $quantity = 1, $refresh_totals = true)
    {
        if (0 === $quantity || $quantity < 0) {
            wc_do_deprecated_action('woocommerce_before_cart_item_quantity_zero', array($cart_item_key, $this), '3.7.0', 'woocommerce_remove_cart_item');
            return $this->remove_cart_item($cart_item_key);
        }
        $old_quantity = $this->cart_contents[$cart_item_key]['quantity'];
        $this->cart_contents[$cart_item_key]['quantity'] = $quantity;
        do_action('woocommerce_after_cart_item_quantity_update', $cart_item_key, $quantity, $old_quantity, $this);
        if ($refresh_totals) {
            $this->calculate_totals();
        }
        do_action('woocommerce_cart_item_set_quantity', $cart_item_key, $quantity, $this);
        return true;
    }
    public function get_customer()
    {
        return WC()->customer;
    }
    public function calculate_totals()
    {
        $this->reset_totals();
        if ($this->is_empty()) {
            $this->session->set_session();
            return;
        }
        do_action('woocommerce_before_calculate_totals', $this);
        new WC_Cart_Totals($this);
        do_action('woocommerce_after_calculate_totals', $this);
    }
    public function needs_payment()
    {
        return apply_filters('woocommerce_cart_needs_payment', 0 < $this->get_total('edit'), $this);
    }
    public function calculate_shipping()
    {
        $this->shipping_methods = $this->needs_shipping() ? $this->get_chosen_shipping_methods(WC()->shipping()->calculate_shipping($this->get_shipping_packages())) : array();
        $shipping_taxes = wp_list_pluck($this->shipping_methods, 'taxes');
        $merged_taxes = array();
        foreach ($shipping_taxes as $taxes) {
            foreach ($taxes as $tax_id => $tax_amount) {
                if (!isset($merged_taxes[$tax_id])) {
                    $merged_taxes[$tax_id] = 0;
                }
                $merged_taxes[$tax_id] += $tax_amount;
            }
        }
        $this->set_shipping_total(array_sum(wp_list_pluck($this->shipping_methods, 'cost')));
        $this->set_shipping_tax(array_sum($merged_taxes));
        $this->set_shipping_taxes($merged_taxes);
        return $this->shipping_methods;
    }
    protected function get_chosen_shipping_methods($calculated_shipping_packages = array())
    {
        $chosen_methods = array();
        foreach ($calculated_shipping_packages as $key => $package) {
            $chosen_method = wc_get_chosen_shipping_method_for_package($key, $package);
            if ($chosen_method) {
                $chosen_methods[$key] = $package['rates'][$chosen_method];
            }
        }
        return $chosen_methods;
    }
    protected function filter_items_needing_shipping($item)
    {
        $product = $item['data'];
        return $product && $product->needs_shipping();
    }
    protected function get_items_needing_shipping()
    {
        return array_filter($this->get_cart(), array($this, 'filter_items_needing_shipping'));
    }
    public function get_shipping_packages()
    {
        return apply_filters('woocommerce_cart_shipping_packages', array(array('contents' => $this->get_items_needing_shipping(), 'contents_cost' => array_sum(wp_list_pluck($this->get_items_needing_shipping(), 'line_total')), 'applied_coupons' => $this->get_applied_coupons(), 'user' => array('ID' => get_current_user_id()), 'destination' => array('country' => $this->get_customer()->get_shipping_country(), 'state' => $this->get_customer()->get_shipping_state(), 'postcode' => $this->get_customer()->get_shipping_postcode(), 'city' => $this->get_customer()->get_shipping_city(), 'address' => $this->get_customer()->get_shipping_address(), 'address_1' => $this->get_customer()->get_shipping_address(), 'address_2' => $this->get_customer()->get_shipping_address_2()), 'cart_subtotal' => $this->get_displayed_subtotal())));
    }
    public function needs_shipping()
    {
        if (!wc_shipping_enabled() || 0 === wc_get_shipping_method_count(true)) {
            return false;
        }
        $needs_shipping = false;
        foreach ($this->get_cart_contents() as $cart_item_key => $values) {
            if ($values['data']->needs_shipping()) {
                $needs_shipping = true;
                break;
            }
        }
        return apply_filters('woocommerce_cart_needs_shipping', $needs_shipping);
    }
    public function needs_shipping_address()
    {
        return apply_filters('woocommerce_cart_needs_shipping_address', true === $this->needs_shipping() && !wc_ship_to_billing_address_only());
    }
    public function show_shipping()
    {
        if (!wc_shipping_enabled() || !$this->get_cart_contents()) {
            return false;
        }
        if ('yes' === get_option('woocommerce_shipping_cost_requires_address')) {
            $country = $this->get_customer()->get_shipping_country();
            if (!$country) {
                return false;
            }
            $country_fields = WC()->countries->get_address_fields($country, 'shipping_');
            if (isset($country_fields['shipping_state']) && $country_fields['shipping_state']['required'] && !$this->get_customer()->get_shipping_state()) {
                return false;
            }
            if (isset($country_fields['shipping_postcode']) && $country_fields['shipping_postcode']['required'] && !$this->get_customer()->get_shipping_postcode()) {
                return false;
            }
        }
        return apply_filters('woocommerce_cart_ready_to_calc_shipping', true);
    }
    public function get_cart_shipping_total()
    {
        $total = __('Free!', 'woocommerce');
        if (0 < $this->get_shipping_total()) {
            if ($this->display_prices_including_tax()) {
                $total = wc_price($this->shipping_total + $this->shipping_tax_total);
                if ($this->shipping_tax_total > 0 && !wc_prices_include_tax()) {
                    $total .= ' <small class="tax_label">' . WC()->countries->inc_tax_or_vat() . '</small>';
                }
            } else {
                $total = wc_price($this->shipping_total);
                if ($this->shipping_tax_total > 0 && wc_prices_include_tax()) {
                    $total .= ' <small class="tax_label">' . WC()->countries->ex_tax_or_vat() . '</small>';
                }
            }
        }
        return apply_filters('woocommerce_cart_shipping_total', $total, $this);
    }
    public function check_customer_coupons($posted)
    {
        foreach ($this->get_applied_coupons() as $code) {
            $coupon = new WC_Coupon($code);
            if ($coupon->is_valid()) {
                $current_user = wp_get_current_user();
                $billing_email = isset($posted['billing_email']) ? $posted['billing_email'] : '';
                $check_emails = array_unique(array_filter(array_map('strtolower', array_map('sanitize_email', array($billing_email, $current_user->user_email)))));
                $restrictions = $coupon->get_email_restrictions();
                if (is_array($restrictions) && 0 < count($restrictions) && !$this->is_coupon_emails_allowed($check_emails, $restrictions)) {
                    $coupon->add_coupon_message(WC_Coupon::E_WC_COUPON_NOT_YOURS_REMOVED);
                    $this->remove_coupon($code);
                }
                $coupon_usage_limit = $coupon->get_usage_limit_per_user();
                if (0 < $coupon_usage_limit && 0 === get_current_user_id()) {
                    $coupon_data_store = $coupon->get_data_store();
                    $billing_email = strtolower(sanitize_email($billing_email));
                    if ($coupon_data_store && $coupon_data_store->get_usage_by_email($coupon, $billing_email) >= $coupon_usage_limit) {
                        if ($coupon_data_store->get_tentative_usages_for_user($coupon->get_id(), array($billing_email))) {
                            $coupon->add_coupon_message(WC_Coupon::E_WC_COUPON_USAGE_LIMIT_COUPON_STUCK_GUEST);
                        } else {
                            $coupon->add_coupon_message(WC_Coupon::E_WC_COUPON_USAGE_LIMIT_REACHED);
                        }
                    }
                }
            }
        }
    }
    public function is_coupon_emails_allowed($check_emails, $restrictions)
    {
        foreach ($check_emails as $check_email) {
            if (in_array($check_email, $restrictions, true)) {
                return true;
            }
            foreach ($restrictions as $restriction) {
                $regex = '/^' . str_replace('*', '(.+)?', $restriction) . '$/';
                preg_match($regex, $check_email, $match);
                if (!empty($match)) {
                    return true;
                }
            }
        }
        return false;
    }
    public function has_discount($coupon_code = '')
    {
        return $coupon_code ? in_array(wc_format_coupon_code($coupon_code), $this->applied_coupons, true) : count($this->applied_coupons) > 0;
    }
    public function apply_coupon($coupon_code)
    {
        if (!wc_coupons_enabled()) {
            return false;
        }
        $coupon_code = wc_format_coupon_code($coupon_code);
        $the_coupon = new WC_Coupon($coupon_code);
        if ($the_coupon->get_code() !== $coupon_code) {
            $the_coupon->set_code($coupon_code);
            $the_coupon->add_coupon_message(WC_Coupon::E_WC_COUPON_NOT_EXIST);
            return false;
        }
        if (!$the_coupon->is_valid()) {
            wc_add_notice($the_coupon->get_error_message(), 'error');
            return false;
        }
        if ($this->has_discount($coupon_code)) {
            $the_coupon->add_coupon_message(WC_Coupon::E_WC_COUPON_ALREADY_APPLIED);
            return false;
        }
        if ($the_coupon->get_individual_use()) {
            $coupons_to_keep = apply_filters('woocommerce_apply_individual_use_coupon', array(), $the_coupon, $this->applied_coupons);
            foreach ($this->applied_coupons as $applied_coupon) {
                $keep_key = array_search($applied_coupon, $coupons_to_keep, true);
                if (false === $keep_key) {
                    $this->remove_coupon($applied_coupon);
                } else {
                    unset($coupons_to_keep[$keep_key]);
                }
            }
            if (!empty($coupons_to_keep)) {
                $this->applied_coupons += $coupons_to_keep;
            }
        }
        if ($this->applied_coupons) {
            foreach ($this->applied_coupons as $code) {
                $coupon = new WC_Coupon($code);
                if ($coupon->get_individual_use() && false === apply_filters('woocommerce_apply_with_individual_use_coupon', false, $the_coupon, $coupon, $this->applied_coupons)) {
                    $coupon->add_coupon_message(WC_Coupon::E_WC_COUPON_ALREADY_APPLIED_INDIV_USE_ONLY);
                    return false;
                }
            }
        }
        $this->applied_coupons[] = $coupon_code;
        if ($the_coupon->get_free_shipping()) {
            $packages = WC()->shipping()->get_packages();
            $chosen_shipping_methods = WC()->session->get('chosen_shipping_methods');
            foreach ($packages as $i => $package) {
                $chosen_shipping_methods[$i] = 'free_shipping';
            }
            WC()->session->set('chosen_shipping_methods', $chosen_shipping_methods);
        }
        $the_coupon->add_coupon_message(WC_Coupon::WC_COUPON_SUCCESS);
        do_action('woocommerce_applied_coupon', $coupon_code);
        return true;
    }
    public function get_coupons($deprecated = null)
    {
        $coupons = array();
        if ('order' === $deprecated) {
            return $coupons;
        }
        foreach ($this->get_applied_coupons() as $code) {
            $coupon = new WC_Coupon($code);
            $coupons[$code] = $coupon;
        }
        return $coupons;
    }
    public function get_coupon_discount_amount($code, $ex_tax = true)
    {
        $totals = $this->get_coupon_discount_totals();
        $discount_amount = isset($totals[$code]) ? $totals[$code] : 0;
        if (!$ex_tax) {
            $discount_amount += $this->get_coupon_discount_tax_amount($code);
        }
        return wc_cart_round_discount($discount_amount, wc_get_price_decimals());
    }
    public function get_coupon_discount_tax_amount($code)
    {
        $totals = $this->get_coupon_discount_tax_totals();
        return wc_cart_round_discount(isset($totals[$code]) ? $totals[$code] : 0, wc_get_price_decimals());
    }
    public function remove_coupons($deprecated = null)
    {
        $this->set_coupon_discount_totals(array());
        $this->set_coupon_discount_tax_totals(array());
        $this->set_applied_coupons(array());
        $this->session->set_session();
    }
    public function remove_coupon($coupon_code)
    {
        $coupon_code = wc_format_coupon_code($coupon_code);
        $position = array_search($coupon_code, array_map('wc_format_coupon_code', $this->get_applied_coupons()), true);
        if (false !== $position) {
            unset($this->applied_coupons[$position]);
        }
        WC()->session->set('refresh_totals', true);
        do_action('woocommerce_removed_coupon', $coupon_code);
        return true;
    }
    public function calculate_fees()
    {
        do_action('woocommerce_cart_calculate_fees', $this);
    }
    public function fees_api()
    {
        return $this->fees_api;
    }
    public function add_fee($name, $amount, $taxable = false, $tax_class = '')
    {
        $this->fees_api()->add_fee(array('name' => $name, 'amount' => (float) $amount, 'taxable' => $taxable, 'tax_class' => $tax_class));
    }
    public function get_fees()
    {
        $fees = $this->fees_api()->get_fees();
        if (property_exists($this, 'fees')) {
            $fees = $fees + (array) $this->fees;
        }
        return $fees;
    }
    public function get_total_ex_tax()
    {
        return apply_filters('woocommerce_cart_total_ex_tax', wc_price(max(0, $this->get_total('edit') - $this->get_total_tax())));
    }
    public function get_cart_total()
    {
        return apply_filters('woocommerce_cart_contents_total', wc_price(wc_prices_include_tax() ? $this->get_cart_contents_total() + $this->get_cart_contents_tax() : $this->get_cart_contents_total()));
    }
    public function get_cart_subtotal($compound = false)
    {
        if ($compound) {
            $cart_subtotal = wc_price($this->get_cart_contents_total() + $this->get_shipping_total() + $this->get_taxes_total(false, false));
        } elseif ($this->display_prices_including_tax()) {
            $cart_subtotal = wc_price($this->get_subtotal() + $this->get_subtotal_tax());
            if ($this->get_subtotal_tax() > 0 && !wc_prices_include_tax()) {
                $cart_subtotal .= ' <small class="tax_label">' . WC()->countries->inc_tax_or_vat() . '</small>';
            }
        } else {
            $cart_subtotal = wc_price($this->get_subtotal());
            if ($this->get_subtotal_tax() > 0 && wc_prices_include_tax()) {
                $cart_subtotal .= ' <small class="tax_label">' . WC()->countries->ex_tax_or_vat() . '</small>';
            }
        }
        return apply_filters('woocommerce_cart_subtotal', $cart_subtotal, $compound, $this);
    }
    public function get_product_price($product)
    {
        if ($this->display_prices_including_tax()) {
            $product_price = wc_get_price_including_tax($product);
        } else {
            $product_price = wc_get_price_excluding_tax($product);
        }
        return apply_filters('woocommerce_cart_product_price', wc_price($product_price), $product);
    }
    public function get_product_subtotal($product, $quantity)
    {
        $price = $product->get_price();
        if ($product->is_taxable()) {
            if ($this->display_prices_including_tax()) {
                $row_price = wc_get_price_including_tax($product, array('qty' => $quantity));
                $product_subtotal = wc_price($row_price);
                if (!wc_prices_include_tax() && $this->get_subtotal_tax() > 0) {
                    $product_subtotal .= ' <small class="tax_label">' . WC()->countries->inc_tax_or_vat() . '</small>';
                }
            } else {
                $row_price = wc_get_price_excluding_tax($product, array('qty' => $quantity));
                $product_subtotal = wc_price($row_price);
                if (wc_prices_include_tax() && $this->get_subtotal_tax() > 0) {
                    $product_subtotal .= ' <small class="tax_label">' . WC()->countries->ex_tax_or_vat() . '</small>';
                }
            }
        } else {
            $row_price = $price * $quantity;
            $product_subtotal = wc_price($row_price);
        }
        return apply_filters('woocommerce_cart_product_subtotal', $product_subtotal, $product, $quantity, $this);
    }
    public function get_cart_tax()
    {
        $cart_total_tax = wc_round_tax_total($this->get_cart_contents_tax() + $this->get_shipping_tax() + $this->get_fee_tax());
        return apply_filters('woocommerce_get_cart_tax', $cart_total_tax ? wc_price($cart_total_tax) : '');
    }
    public function get_tax_amount($tax_rate_id)
    {
        $taxes = wc_array_merge_recursive_numeric($this->get_cart_contents_taxes(), $this->get_fee_taxes());
        return isset($taxes[$tax_rate_id]) ? $taxes[$tax_rate_id] : 0;
    }
    public function get_shipping_tax_amount($tax_rate_id)
    {
        $taxes = $this->get_shipping_taxes();
        return isset($taxes[$tax_rate_id]) ? $taxes[$tax_rate_id] : 0;
    }
    public function get_taxes_total($compound = true, $display = true)
    {
        $total = 0;
        $taxes = $this->get_taxes();
        foreach ($taxes as $key => $tax) {
            if (!$compound && WC_Tax::is_compound($key)) {
                continue;
            }
            $total += $tax;
        }
        if ($display) {
            $total = wc_format_decimal($total, wc_get_price_decimals());
        }
        return apply_filters('woocommerce_cart_taxes_total', $total, $compound, $display, $this);
    }
    public function get_total_discount()
    {
        return apply_filters('woocommerce_cart_total_discount', $this->get_discount_total() ? wc_price($this->get_discount_total()) : false, $this);
    }
    private function reset_totals()
    {
        $this->totals = $this->default_totals;
        $this->fees_api->remove_all_fees();
        do_action('woocommerce_cart_reset', $this, false);
    }
    public function get_tax_price_display_mode()
    {
        if ($this->get_customer() && $this->get_customer()->get_is_vat_exempt()) {
            return 'excl';
        }
        return get_option('woocommerce_tax_display_cart');
    }
    public function get_cart_hash()
    {
        $cart_session = $this->session->get_cart_for_session();
        $hash = $cart_session ? md5(wp_json_encode($cart_session) . $this->get_total('edit')) : '';
        $hash = apply_filters_deprecated('woocommerce_add_to_cart_hash', array($hash, $cart_session), '3.6.0', 'woocommerce_cart_hash');
        return apply_filters('woocommerce_cart_hash', $hash, $cart_session);
    }
}