<?php

if (!function_exists('array_column1')) {
    function array_column1($array,$column_name)
    {
        return array_map(function($element) use($column_name){return $element[$column_name];}, $array);
    }
}

class ModelExtensionTotalPromotionTotal extends Model {
    private $grandTotal = 0;
    private $promotion_id = 0;
    private $user;
    private $totalName = 'promotion_total';

    private $conditionChecks = array();

    private $usedProducts = array();

    public static $total = 0;
    public static $taxes = 0;
    public static $total_data = array();
    public static $ProductsIDS = array();

    //ModuleVariables
    private $modulePath = 'extension/module/promotion';
    private $callModel = 'model_extension_module_promotion';

    public function __construct($registry) {
        parent::__construct($registry);

        $this->load->model($this->modulePath);

    }

    private function getBasicPriorities($action){
        if($action == 'discount_products_from_conditions') return 1;
        elseif($action == 'discount_on_products') return 2;
        elseif ($action == 'discount_on_categories') return 3;
        elseif ($action == 'discount_cheapest') return 4;
        elseif ($action == 'discount_expensive') return 5;
        elseif ($action == 'order_discount_range') return 6;
        elseif ($action == 'order_discount') return 7;
        elseif ($action == 'free_shipping')	return 8;
        else return -1;
    }

    public function checkGeneralSetting($general_settings){

        //STARTDATE
        if(isset($general_settings['StartDate']) && !empty($general_settings['StartDate'])){
            if(strtotime($general_settings['StartDate']) > strtotime(date('Y-m-d'))){
                return false;
            }
        }
        //ENDDATE
        if(isset($general_settings['EndDate']) && !empty($general_settings['EndDate'])){
            if(strtotime($general_settings['EndDate']) < strtotime(date('Y-m-d'))){
                return false;
            }
        }

        //COUPON CODE
        if(isset($general_settings['PromotionType']) && $general_settings['PromotionType'] == 0){
            if(!isset($this->session->data['coupon_promotion_module'])) return false;
            if($this->session->data['coupon_promotion_module'] != $general_settings['CouponCode']) return false;
        }

        //TOTAL USES
        $uses = $this->{$this->callModel}->getTotalUses($this->promotion_id);
        if(isset($general_settings['TotalUses']) && $general_settings['TotalUses'] != 0){
            if($uses['total'] >= $general_settings['TotalUses']) return false;
            // TOTAL CUSTOMER USES
            if($this->getConditionValues('customer_name') != NULL && in_array($this->getConditionValues('customer_name'), $uses['customers'])){
                $count = array_count_values($uses['customers']);
                if($count[$this->getConditionValues('customer_name')] >= $general_settings['TotalUsesCustomer']) return false;
            }
        }

        //CUSTOMER GROUP
        if(!isset($general_settings['CustGroup'])) {
            $general_settings['CustGroup'] = array();
        }

        //NeedLOGIN
        if ($general_settings['need_login'] == 1) {
            if($this->getConditionValues('customer_group_id') == NULL) return false;
            if(!in_array($this->getConditionValues('customer_group_id'), $general_settings['CustGroup'])) return false;
        }

        //STORE
        if(isset($general_settings['Stores'])) {
            if(!in_array($this->config->get('config_store_id'), $general_settings['Stores'])) return false;
        }

        return true;
    }

    private function getConditionValues($field) { // TOTAL, QUANTITY, ..etc
        switch ($field) {
            case 'cart_total':
                return $this->cart->getTotal() - $this->grandTotal;

                break;
            case 'cart_total_no_changes':
                return $this->cart->getTotal();

                break;
            case 'cart_quantity':
                $product_total = 0;
                $products = $this->cart->getProducts();
                foreach ($products as $product) {
                    //if(!in_array($product['product_id'], array(140165, 140166, 140167, 140168, 140169))){
                        $product_total += $product['quantity'];
                    //}
                }
                return $product_total;
                break;

            case 'cart_weight':
                return $this->cart->getWeight();

                break;
            case 'customer_group_id':
                return $this->customer->getGroupId();

                break;
            case 'customer_name':
                return $this->customer->getId();

                break;
            case 'customer_reg_date':
                $this->load->model('account/customer');
                $customer_data = $this->model_account_customer->getCustomer($this->customer->getId());

                if(empty($customer_data)) return false;
                else return $customer_data['date_added'];

                break;
            case 'customer_order':

                if($this->customer->getId() == 0) return 0;

                $this->load->model('account/order');
                return $this->model_account_order->getTotalOrders();
                break;
            case 'get_cheapest_product':
                $cartProducts = $this->cart->getProducts();
                usort($cartProducts, function($a, $b) {
                    //if(!in_array($b['product_id'], array('140165', '140166', '140167', '140168', '140169'))){
                        return $a['price'] > $b['price'] ? 1 : -1;
                    //}

                });
                $product = reset($cartProducts);

                return $product['product_id'];
                break;
            case 'get_expensive_product':
                $cartProducts = $this->cart->getProducts();

                usort($cartProducts, function($a, $b) {
                    return $b['price'] < $a['price'] ? 1 : -1;
                });

                $product = reset($cartProducts);
                return $product['product_id'];
                break;
            case 'manufac_name':
                $products = $this->cart->getProducts();

                usort($products, function($a, $b) {
                    return $a['price'] > $b['price'] ? 1 : -1;
                });

                $ids = array();
                foreach ($products as $key => $product) {
                    $ids[$product['product_id']] = $this->{$this->callModel}->getManufacIDbyProdID($product['product_id']);
                }
                return $ids;
                break;
            case 'category_name':
                $this->load->model('catalog/product');
                $products = $this->cart->getProducts();

                usort($products, function($a, $b) {
                    return $a['price'] > $b['price'] ? 1 : -1;
                });

                $ids = array();
                foreach ($products as $key => $product) {
                    $cats = $this->model_catalog_product->getCategories($product['product_id']);
                    foreach ($cats as $category) {
                        $ids[$product['product_id']][] = $category['category_id'];

                        $parent_cats = $this->{$this->callModel}->getCategoryParent($category['category_id']);
                        foreach ($parent_cats as $category_id) {
                            $ids[$product['product_id']][] = $category_id;
                        }

                        $child_cats = $this->{$this->callModel}->getCategories($category['category_id']);
                        foreach ($child_cats as $category_id) {
                            $ids[$product['product_id']][] = $category_id;
                        }

                    }
                }
                return $ids;
                break;
            case 'day':
                return date('w') - 1;
                break;
            default:
                return 'error (no such condition )';
                break;
        }
    }

    private function isConditionProduct($product_id) {
        return isset($this->usedProducts[$product_id]) ? array_sum($this->usedProducts[$product_id]['condition']) : 0;
    }

    private function getActionProductQty($product_id) {
        return isset($this->usedProducts[$product_id]) ? !empty($this->usedProducts[$product_id]['action']) ? $this->usedProducts[$product_id]['action'] : 0 : 0;
    }

    private function addConditionProducts($product_id, $quantity, $condition) {
        if(isset($this->usedProducts[$product_id])) {
            if(!in_array($condition, array_keys($this->usedProducts[$product_id]['condition']))) {
                $this->usedProducts[$product_id]['condition'][$condition] =  $quantity;
            }
        } else {
            $all_qty = $this->{$this->callModel}->getProductInCartQuantity($product_id);
            $this->usedProducts[$product_id] = array(
                'product_id' => $product_id,
                'all_qty' => $all_qty,
                'is_discounted' => false,
                'condition' => array($condition => $quantity),
                'action' => 0,
            );
        }
    }

    private function addActionProducts($product_id, $quantity) {
        if(isset($this->usedProducts[$product_id])) {
            $this->usedProducts[$product_id]['is_discounted']   = true;
            $this->usedProducts[$product_id]['action']  += $quantity;
        } else {
            $all_qty = $this->{$this->callModel}->getProductInCartQuantity($product_id);
            $this->usedProducts[$product_id] = array(
                'product_id' => $product_id,
                'all_qty' => $all_qty,
                'is_discounted' => true,
                'condition' => array(),
                'action' => $quantity,
            );
        }
    }

    private function compareCondtitionAndRequiredQty($products, $qty, $condition) {
        $count = 0;

        foreach ($products as $product) {
            if(in_array($condition, array_keys($product['condition']))) {
                $count += $product['all_qty'];
            }
        }

        return $count >= $qty;
    }

    private function addQtyToCondProducts($qty, $condition) {
        $product = current($this->usedProducts);

        if($qty == 0) {
            foreach ($this->usedProducts as $product_id => &$product) {
                if(in_array($condition, array_keys($product['condition']))) {
                    $this->usedProducts[$product['product_id']]['condition'][$condition] = $product['all_qty'];
                }
            }
        } else  {
            $qty_to_add = $qty;
            while ($qty_to_add > 0) {
                if(in_array($condition, array_keys($product['condition']))) {
                    if($product['condition'][$condition] < $product['all_qty']) {
                        $this->usedProducts[$product['product_id']]['condition'][$condition] += 1;
                        $qty_to_add -= 1;
                    }
                }

                $product = next($this->usedProducts);
                if($product == false) {
                    $product = reset($this->usedProducts);
                }
            }
        }
    }

    private function discountProduct($data) {
        /*
            $data = array(
                'product_id' => $product_id,
                'quantity' => $quantity,
                'disc_type' => $disc_type,
                'amount' => $amount,
                'max_quantity' => $max_quantity,
                'skip_condition_product' => $skip_condition_product,
                'skip_special_products' => $skip_special_products,
                'exclude_products' => $exclude_products,
                'exclude_categories' => $exclude_categories,
            );
        */


        if(isset($data['skip_special_products']) && $data['skip_special_products'] == true) {
            if($this->{$this->callModel}->isSpecialProduct($data['product_id'])) return 0;
        }

        if(isset($data['skip_condition_product']) && $data['skip_condition_product'] == true) {
            if($this->isConditionProduct($data['product_id'])) return 0;
        }

        if(!empty($data['exclude_products'])) {
            if(in_array($data['product_id'], $data['exclude_products'])) return 0;
        }

        if(!isset($data['max_quantity'])) {
            $data['max_quantity'] = $data['quantity'];
        }

        if(!empty($data['exclude_categories'])) {
            $this->load->model('catalog/product');
            $categories_product = $this->model_catalog_product->getCategories($data['product_id']);
            foreach ($categories_product as $cats) {
                if(in_array($cats['category_id'], $data['exclude_categories'])) return 0;
            }
        }

        $total = 0;

        $data['quantity'] = ($data['quantity'] > $data['max_quantity']) ? $data['max_quantity'] : $data['quantity'];

        if( $data['disc_type'] == 'percentage') {
            $total += $data['amount']*$this->{$this->callModel}->getProductInCartPrice($data['product_id']) * $data['quantity']/100;
            self::$taxes += $this->{$this->callModel}->getProductTax($data['product_id'], $data['amount']*$this->{$this->callModel}->getProductInCartPrice($data['product_id'])/100, $data['quantity']);
        } elseif ( $data['disc_type'] == 'fixed_price') {
            $tax = $this->{$this->callModel}->getProductTax($data['product_id'], $this->{$this->callModel}->getProductInCartPrice($data['product_id'], false) - $data['amount']);
            $total += ($this->{$this->callModel}->getProductInCartPrice($data['product_id'], false) + $tax - $data['amount']) * $data['quantity'];
            self::$taxes += $this->{$this->callModel}->getProductTax($data['product_id'], $this->{$this->callModel}->getProductInCartPrice($data['product_id'], false) - $data['amount'], $data['quantity']);
        } else {
            $total += $data['amount'] * $data['quantity'];
            self::$taxes += $this->{$this->callModel}->getProductTax($data['product_id'], $data['amount'], $data['quantity']);
        }

        return $total;
    }


    public function checkConditions($conditions, $condition_rules){

        if(empty($conditions)){
            return true;
        }

        $this->conditionChecks = array();
        $condition_checks = array();

        foreach ($conditions as $key => $promo_cond) {

            switch ($promo_cond['condSelect']) {
                case 'product_name':
                    if( $this->{$this->callModel}->checkProductInCart($promo_cond['product_id'], $promo_cond['product_quantity']) ) {
                        $this->addConditionProducts($promo_cond['product_id'], 0, $promo_cond['condSelect'].$key);
                        $this->addQtyToCondProducts($promo_cond['product_quantity'], $promo_cond['condSelect'].$key);

                        $condition_checks['product_name'.$key] = true;

                    } else {
                        $condition_checks['product_name'.$key] = false;
                    }
                    break;
                case 'product_option':
                    if( $this->{$this->callModel}->checkProductInCartWithOptions($promo_cond['product_id'], $promo_cond['product_option_quantity'], $promo_cond['option_id'], $promo_cond['option_value_id']) ) {
                        $this->addConditionProducts($promo_cond['product_id'], 0, $promo_cond['condSelect'].$key);
                        $this->addQtyToCondProducts($promo_cond['product_option_quantity'], $promo_cond['condSelect'].$key);

                        $condition_checks['product_option'.$key] = true;

                    } else {
                        $condition_checks['product_option'.$key] = false;
                    }
                    break;
                case 'product_list':
                    $product_ids = $promo_cond['product_list'];

                    $min_qty = $promo_cond['product_list_quantity'];

                    $condition_checks[$promo_cond['condSelect'].$key] = false;

                    foreach ($product_ids as $product_id) {
                        if($this->{$this->callModel}->checkProductInCart($product_id, $min_qty)) {
                            $this->addConditionProducts($product_id, 0, $promo_cond['condSelect'].$key);
                        }
                    }


                    $hasQty = $this->compareCondtitionAndRequiredQty($this->usedProducts, $min_qty, $promo_cond['condSelect'].$key);
                    if($hasQty) {
                        $this->addQtyToCondProducts($min_qty, $promo_cond['condSelect'].$key);
                        $condition_checks[$promo_cond['condSelect'].$key] = true;
                    }
                    break;
                case 'category_name':
                    $categories = $this->getConditionValues($promo_cond['condSelect']);

                    $this->load->model('catalog/category');

                    $promo_cat_ids = array();
                    $promo_cat_ids[] = $promo_cond['category_id'];

                    //Child Categories
                    if(isset($promo_cond['category_child']) && $promo_cond['category_child'] == 'yes') {
                        $child_cats = $this->{$this->callModel}->getCategories($promo_cond['category_id']);
                        foreach ($child_cats as $category_id) {
                            $promo_cat_ids[] = $category_id;
                        }
                    }

                    //Parent Categories
                    if(isset($promo_cond['category_parent']) && $promo_cond['category_parent'] == 'yes') {
                        $parent_cats = $this->{$this->callModel}->getCategoryParent($promo_cond['category_id']);
                        foreach ($parent_cats as $category_id) {
                            $promo_cat_ids[] = $category_id;
                        }
                    }

                    $condition_checks['category_name'.$key] = false;

                    foreach ($categories as $product_id => $category_ids) {
                        if (count(array_intersect($category_ids,$promo_cat_ids)) > 0) {
                            $this->addConditionProducts($product_id, 0, $promo_cond['condSelect'].$key);
                        }
                    }


                    $hasQty = $this->compareCondtitionAndRequiredQty($this->usedProducts, $promo_cond['category_quantity'], $promo_cond['condSelect'].$key);
                    if($hasQty) {
                        $this->addQtyToCondProducts($promo_cond['category_quantity'], $promo_cond['condSelect'].$key);
                        $condition_checks['category_name'.$key] = true;
                    }
                    break;
                case 'manufac_name':
                    $manufacturers = $this->getConditionValues($promo_cond['condSelect']);
                    $count = 0;

                    $condition_checks['manufac_name'.$key] = false;

                    foreach ($manufacturers as $product_id => $manufacturer_ids) {
                        if ( $manufacturer_ids == $promo_cond['manufacturer_id']) {
                            $this->addConditionProducts($product_id, 0, $promo_cond['condSelect'].$key);
                        }
                    }

                    $hasQty = $this->compareCondtitionAndRequiredQty($this->usedProducts, $promo_cond['manufacturer_quantity'], $promo_cond['condSelect'].$key);
                    if($hasQty) {
                        $this->addQtyToCondProducts($promo_cond['manufacturer_quantity'], $promo_cond['condSelect'].$key);
                        $condition_checks['manufac_name'.$key] = true;
                    }

                    break;
                case 'customer_name':
                    $customer_now = $this->getConditionValues($promo_cond['condSelect']);
                    if( $customer_now == $promo_cond['customer_id']){
                        $condition_checks['customer_name'.$key] = true;
                    } else {
                        $condition_checks['customer_name'.$key] = false;
                    }
                    break;
                case 'day':
                    $daynow = $this->getConditionValues($promo_cond['condSelect']);
                    if ($promo_cond['dayofweek'] == strtolower(jddayofweek($daynow, 2)) ) {
                        $condition_checks['day'.$key] = true;
                    } else {
                        $condition_checks['day'.$key] = false;
                    }
                    break;
                case 'customer_reg_date':
                    $customer_reg_since = $this->getConditionValues($promo_cond['condSelect']);

                    $isTrue = false;
                    if($customer_reg_since) {
                        $isTrue = strtotime($customer_reg_since) - strtotime($promo_cond['customer_date']);
                    }

                    if ($isTrue > 0) {
                        $condition_checks['customer_reg_date'.$key] = true;
                    } else {
                        $condition_checks['customer_reg_date'.$key] = false;
                    }
                    break;
                case 'customer_order':
                    $val = $this->getConditionValues($promo_cond['condSelect']);
                    $condition_input = $promo_cond['condition_input'];
                    $condEquals = $promo_cond['condEquals'];

                    if($condEquals == 'eq'){
                        if($val == $condition_input) $condition_checks[$promo_cond['condSelect'].$key] = true;
                        else $condition_checks[$promo_cond['condSelect'].$key] = false;
                    } else if($condEquals == 'neq'){
                        if($val != $condition_input) $condition_checks[$promo_cond['condSelect'].$key] = true;
                        else $condition_checks[$promo_cond['condSelect'].$key] = false;
                    } else if($condEquals == 'gtr'){
                        if($val > $condition_input) $condition_checks[$promo_cond['condSelect'].$key] = true;
                        else $condition_checks[$promo_cond['condSelect'].$key] = false;
                    } else if($condEquals == 'lth'){
                        if($val < $condition_input) $condition_checks[$promo_cond['condSelect'].$key] = true;
                        else $condition_checks[$promo_cond['condSelect'].$key] = false;
                    }else {
                        $condition_checks[$promo_cond['condSelect'].$key] = false;
                    }
                    break;
                default:
                    $val = $this->getConditionValues($promo_cond['condSelect']);
                    $condEquals = $promo_cond['condEquals'];
                    $condition_input = $promo_cond['condition_input'];
                    if($condEquals == 'eq'){
                        if($val == $condition_input) $condition_checks[$promo_cond['condSelect'].$key] = true;
                        else $condition_checks[$promo_cond['condSelect'].$key] = false;
                    } else if($condEquals == 'neq'){
                        if($val != $condition_input) $condition_checks[$promo_cond['condSelect'].$key] = true;
                        else $condition_checks[$promo_cond['condSelect'].$key] = false;
                    } else if($condEquals == 'gtr'){
                        if($val > $condition_input) $condition_checks[$promo_cond['condSelect'].$key] = true;
                        else $condition_checks[$promo_cond['condSelect'].$key] = false;
                    } else if($condEquals == 'lth'){
                        if($val < $condition_input) $condition_checks[$promo_cond['condSelect'].$key] = true;
                        else $condition_checks[$promo_cond['condSelect'].$key] = false;
                    }else {
                        $condition_checks[$promo_cond['condSelect'].$key] = false;
                    }
                    break;
            }
        }

        $this->conditionChecks = $condition_checks;

        if($condition_rules['condition_bool'] == 'False') {
            if($condition_rules['condition_combine'] == 'All'){
                if(!in_array(true, $condition_checks)){
                    return true;
                }
            } else {
                if(in_array(false, $condition_checks)){
                    return true;
                }
            }
        } else {
            if($condition_rules['condition_combine'] == 'All'){
                if(!in_array(false, $condition_checks)){
                    return true;
                }
            } else {
                if(in_array(true, $condition_checks)){
                    return true;
                }
            }
        }

        return false;

    }

    private function applyActions($actions, &$total, $reapply = false){
        if(empty($actions)){
            return;
        }

        switch ($actions[1]['mainSelect']) {
            case 'order_discount':
                $disc_type  = $actions[1]['disc_type'];
                $amount = $actions[1]['amount'];

                if( $disc_type == 'percentage') {
                    $total += $amount*$this->getConditionValues('cart_total')/100;
                } elseif ( $disc_type == 'fixed_price') {
                    $total += $this->getConditionValues('cart_total') - $amount;
                } else {
                    $total += $amount;
                }
                break;
            case 'free_shipping':
                if(isset($this->session->data['shipping_method'])){
                    $total += $this->tax->calculate($this->session->data['shipping_method']['cost'], $this->session->data['shipping_method']['tax_class_id'], $this->config->get('config_tax'));
                }
                break;
            case 'discount_cheapest':
                $disc_type  = $actions[1]['disc_type'];
                $amount = $actions[1]['amount'];
                $product_id = $this->getConditionValues('get_cheapest_product');

                $discount_data = array(
                    'product_id' => $product_id,
                    'quantity' => 1,
                    'disc_type' => $disc_type,
                    'amount' => $amount,
                );
                $total += $this->discountProduct($discount_data);

                break;
            case 'discount_expensive':
                $disc_type  = $actions[1]['disc_type'];
                $amount = $actions[1]['amount'];
                $product_id = $this->getConditionValues('get_expensive_product');

                $discount_data = array(
                    'product_id' => $product_id,
                    'quantity' => 1,
                    'disc_type' => $disc_type,
                    'amount' => $amount,
                );
                $total += $this->discountProduct($discount_data);

                break;
            case 'discount_on_products':
                $disc_type  = $actions[1]['disc_type'];
                $amount = $actions[1]['amount'];
                $products = $actions[1]['product'];

                $max_quantity = !empty($actions[1]['max_quantity_specific_product']) ? $actions[1]['max_quantity_specific_product'] : 99999;

                $skip_condition_products = (isset($actions[1]['skip_condition_products']) && $actions[1]['skip_condition_products'] == 'yes') ? true : false;

                $skip_special_products = (isset($actions[1]['skip_special_products']) && $actions[1]['skip_special_products'] == 'yes') ? true : false;

                $count = $max_quantity;
                foreach ($products as $product_id) {

                    $quantity = $this->{$this->callModel}->getProductInCartQuantity($product_id);

                    if($reapply) {
                        $actionQty = $this->getActionProductQty($product_id);
                        $quantity -= $actionQty;
                    }

                    if($count < $quantity) $quantity = $count;

                    $discount_data = array(
                        'product_id' => $product_id,
                        'quantity' => $quantity,
                        'disc_type' => $disc_type,
                        'amount' => $amount,
                        'max_quantity' => $max_quantity,
                        'skip_condition_product' => $skip_condition_products,
                        'skip_special_products' => $skip_special_products,
                    );

                    $discount = $this->discountProduct($discount_data);
                    if($discount) {

                        if($max_quantity > $quantity) {
                            $count -= $quantity;
                        } else {
                            $count -= $max_quantity;
                        }

                        $total += $discount;
                        $this->addActionProducts($product_id, $max_quantity > $quantity ? $quantity : $max_quantity);
                    }

                    if($count <= 0) break;
                }
                break;
            case 'discount_all_products':
                $disc_type  = $actions[1]['disc_type'];
                $amount = $actions[1]['amount'];


                $max_quantity = isset($actions[1]['max_quantity_all_products']) && $actions[1]['max_quantity_all_products'] > 0  ? $actions[1]['max_quantity_all_products'] : 99999;

                $skip_condition_products = (isset($actions[1]['skip_condition_products']) && $actions[1]['skip_condition_products'] == 'yes') ? true : false;

                $skip_special_products = (isset($actions[1]['skip_special_products']) && $actions[1]['skip_special_products'] == 'yes') ? true : false;

                $exclude_products = array(); $exclude_category = array();
                if(isset($actions[1]['exclude_product'])) {
                    $exclude_products = $actions[1]['exclude_product'];
                }
                if(isset($actions[1]['exclude_category'])) {
                    $exclude_category = $actions[1]['exclude_category'];
                }

                $cartProducts = $this->cart->getProducts();
                $count = $max_quantity;
                foreach ($cartProducts as $key => $cart_product) {

                    if($reapply) {
                        $actionQty = $this->getActionProductQty($cart_product['product_id']);
                        $cart_product['quantity'] -= $actionQty;
                    }

                    if($count < $cart_product['quantity']) $cart_product['quantity'] = $count;

                    $discount_data = array(
                        'product_id' => $cart_product['product_id'],
                        'quantity' => $cart_product['quantity'],
                        'disc_type' => $disc_type,
                        'amount' => $amount,
                        'max_quantity' => $max_quantity,
                        'skip_condition_product' => $skip_condition_products,
                        'skip_special_products' => $skip_special_products,
                        'exclude_products' => $exclude_products,
                        'exclude_categories' => $exclude_category,
                    );

                    $discount = $this->discountProduct($discount_data);
                    if($discount) {

                        if($max_quantity > $cart_product['quantity']) {
                            $count -= $cart_product['quantity'];
                        } else {
                            $count -= $max_quantity;
                        }

                        $total += $discount;
                        $this->addActionProducts($cart_product['product_id'], $max_quantity > $cart_product['quantity'] ? $cart_product['quantity'] : $max_quantity);
                    }

                    if($count <= 0) break;
                }
                break;
            case 'discount_products_from_conditions':
                $disc_type  = $actions[1]['disc_type'];
                $amount = $actions[1]['amount'];

                foreach ($this->usedProducts as $product_id => $product_data) {

                    $discount_data = array(
                        'product_id' => $product_data['product_id'],
                        'quantity' => $product_data['all_qty'],
                        'disc_type' => $disc_type,
                        'amount' => $amount,
                    );

                    $total += $this->discountProduct($discount_data);
                    $this->addActionProducts($product_data['product_id'], $product_data['all_qty']);
                }
                break;
            case 'discount_on_categories':
                $disc_type  = $actions[1]['disc_type'];
                $amount = $actions[1]['amount'];

                $max_quantity = isset($actions[1]['max_quantity_category']) && $actions[1]['max_quantity_category'] > 0 ? $actions[1]['max_quantity_category'] : 99999;

                $categories = $actions[1]['category'];

                $products = $this->getConditionValues('category_name');

                $skip_condition_products = (isset($actions[1]['skip_condition_products']) && $actions[1]['skip_condition_products'] == 'yes') ? true : false;

                $skip_special_products = (isset($actions[1]['skip_special_products']) && $actions[1]['skip_special_products'] == 'yes') ? true : false;

                $exclude_products = array();
                if(isset($actions[1]['exclude_product_from_cat'])) {
                    $exclude_products = $actions[1]['exclude_product_from_cat'];
                }

                $count = $max_quantity;
                foreach ($categories as $desired_cat_id) {
                    foreach ($products as $product_id => $category_id) {
                        if(array_search($desired_cat_id, $category_id) !== false) {

                            $quantity_product = $this->{$this->callModel}->getProductInCartQuantity($product_id);

                            if($reapply) {
                                $actionQty = $this->getActionProductQty($product_id);
                                $quantity_product -= $actionQty;
                            }

                            if($count < $quantity_product) $quantity_product = $count;

                            $discount_data = array(
                                'product_id' => $product_id,
                                'quantity' => $quantity_product,
                                'disc_type' => $disc_type,
                                'amount' => $amount,
                                'max_quantity' => $max_quantity,
                                'skip_condition_product' => $skip_condition_products,
                                'skip_special_products' => $skip_special_products,
                                'exclude_products' => $exclude_products,
                            );
                            $discount = $this->discountProduct($discount_data);

                            if($discount) {

                                if($max_quantity > $quantity_product) {
                                    $count -= $quantity_product;
                                } else {
                                    $count -= $max_quantity;
                                }

                                $total += $discount;
                                $this->addActionProducts($product_id, $max_quantity > $quantity_product ? $quantity_product : $max_quantity);
                            }

                            if($count <= 0) break 2;
                        }
                    }
                }
                break;
            case 'discount_on_manufacturers':
                $disc_type  = $actions[1]['disc_type'];
                $amount = $actions[1]['amount'];

                $max_quantity = isset($actions[1]['max_quantity_manufacturer']) && $actions[1]['max_quantity_manufacturer'] > 0 ? $actions[1]['max_quantity_manufacturer'] : 99999;

                $manufacturers = $actions[1]['manufacturer'];

                $products = $this->getConditionValues('manufac_name');

                $skip_condition_products = (isset($actions[1]['skip_condition_products']) && $actions[1]['skip_condition_products'] == 'yes') ? true : false;

                $skip_special_products = (isset($actions[1]['skip_special_products']) && $actions[1]['skip_special_products'] == 'yes') ? true : false;

                $exclude_products = array();
                if(isset($actions[1]['exclude_product_from_manufacturer'])) {
                    $exclude_products = $actions[1]['exclude_product_from_manufacturer'];
                }

                $count = $max_quantity;
                foreach ($products as $product_id => $manufac_id) {

                    if(in_array($manufac_id, $manufacturers)) {

                        $quantity_product = $this->{$this->callModel}->getProductInCartQuantity($product_id);

                        if($reapply) {
                            $actionQty = $this->getActionProductQty($product_id);
                            $quantity_product -= $actionQty;
                        }

                        if($count < $quantity_product) $quantity_product = $count;

                        $discount_data = array(
                            'product_id' => $product_id,
                            'quantity' => $quantity_product,
                            'disc_type' => $disc_type,
                            'amount' => $amount,
                            'max_quantity' => $max_quantity,
                            'skip_condition_product' => $skip_condition_products,
                            'skip_special_products' => $skip_special_products,
                            'exclude_products' => $exclude_products,
                        );
                        $discount = $this->discountProduct($discount_data);

                        if($discount) {

                            if($max_quantity > $quantity_product) {
                                $count -= $quantity_product;
                            } else {
                                $count -= $max_quantity;
                            }

                            $total += $discount;
                            $this->addActionProducts($product_id, $max_quantity > $quantity_product ? $quantity_product : $max_quantity);
                        }

                        if($count <= 0) break 2;
                    }
                }
                break;
            case 'order_discount_range':
                usort($actions[1]['ranges'], function($a, $b) {
                    return $b['total'] - $a['total'];
                });

                foreach ($actions[1]['ranges'] as $range) {
                    $disc_type  = $range['type'];
                    $amount = $range['amount'];
                    $total_to_reach = $range['total'];

                    if($total_to_reach > $this->getConditionValues('cart_total')) continue;

                    if( $disc_type == 'percentage') {
                        $total += $amount*$this->getConditionValues('cart_total')/100;
                        break;
                    } else {
                        $total += $amount;
                        break;
                    }
                }
                break;
            default:
                # code...
                break;
        }

    }

    private function checkIfPromotionCanBeReaplied() {

        if($this->cart->countProducts() == 1) return 0;

        $condition_qtys = array();
        $action_qtys = array();
        foreach ($this->usedProducts as $product_id => $product) {
            foreach ($product['condition'] as $key => $condition_qty) {
                $condition_qtys[$key]['qty_sum'] = isset($condition_qtys[$key]['qty_sum']) ? $condition_qtys[$key]['qty_sum'] + $product['all_qty'] : $product['all_qty'];
                $condition_qtys[$key]['used_qty_in_condition'] = isset($condition_qtys[$key]['used_qty_in_condition']) ? $condition_qtys[$key]['used_qty_in_condition'] + $condition_qty : $condition_qty;
            }
            if(isset($product['action'])) {
                $action_qtys['qty_sum'] = isset($action_qtys['qty_sum']) ? $action_qtys['qty_sum'] + $product['all_qty'] : $product['all_qty'];
                $action_qtys['used_qty_in_action'] = isset($action_qtys['used_qty_in_action']) ? $action_qtys['used_qty_in_action'] + $product['action'] : $product['action'];
            }
        }

        if(isset($action_qtys['used_qty_in_action']) && $action_qtys['used_qty_in_action'] == $this->cart->countProducts()) return 0;

        $reapply = false;
        foreach ($condition_qtys as $key => $value) {
            if($value['used_qty_in_condition'] > 0 && floor($value['qty_sum'] / $value['used_qty_in_condition']) > 1) $reapply = floor($value['qty_sum'] / $value['used_qty_in_condition']);
            else $reapply = false;
        }

        if($reapply) {
            if($reapply * $action_qtys['used_qty_in_action'] <= $this->cart->countProducts() ) return $reapply - 1;
        }

        return false;
    }

    public function getTotal($totals) {

        //$this->product->dump($totals);

        $total = &$totals['total'];
        $taxes = &$totals['taxes'];
        $total_data = &$totals['totals'];


        //Admin check
        $moduleSettings = $this->config->get('promotion');
        if(isset($moduleSettings['development_mode']) && $moduleSettings['development_mode'] == 'yes') {
            if(!$this->user->isLogged()) return;
        }

        if (empty(self::$total)) {

            $promotions = $this->{$this->callModel}->getPromotions();

            unset($this->session->data['promotions']);

            foreach ($promotions as $key => $value) { $key+=1;

                $status = $value['status'];

                $this->promotion_id = $value['module_id'];

                //If promotion is disabled it will skip
                if($status == 'no') continue;


                //Settings, conditions and actions
                $this->load->language('common/language');
                $data['code'] = $this->session->data['language'];

                $general_settings = $value['general_settings'];

                /*$promotion_name = isset($general_settings['name'][$data['code']]) ? $general_settings['name'][$data['code']] : is_array($general_settings['name']) ? $general_settings['name'][$data['code']] : '';*/

                $promotion_name = (isset($general_settings['name'][$data['code']]) ? $general_settings['name'][$data['code']] : is_array($general_settings['name'])) ? end($general_settings['name']) : '';

                $conditions = $value['conditions'];
                $condition_rules = $value['condition_rules'];
                $actions = $value['actions'];
                $total_promotion = 0;
                $this->usedProducts = array();

                //First we check general settings if they fail we skip everything
                $checkGeneralSetting = $this->checkGeneralSetting($general_settings);
                if($checkGeneralSetting) {
                    // Then we check conditions
                    $isContitionsTrue = $this->checkConditions($conditions, $condition_rules);

                    if($isContitionsTrue) {
                        $this->applyActions($actions, $total_promotion, true);

                        if(isset($general_settings['multiply']) && $actions[1]['mainSelect'] != 'free_shipping' && $actions[1]['mainSelect'] != 'discount_expensive' && $actions[1]['mainSelect'] != 'discount_cheapest'  && $actions[1]['mainSelect'] != 'discount_products_from_conditions') {
                            if($general_settings['multiply'] == 'yes') {
                                for ($i=0; $i < $this->checkIfPromotionCanBeReaplied(); $i++) {
                                    $this->applyActions($actions, $total_promotion, true);
                                }
                            }
                        }
                    }
                }

                //If there is no change in price we do not show the promotion total.
                if($total_promotion > 0){

                    $total_data[] = array(
                        'code'       =>  $this->totalName,
                        'title'      =>  $promotion_name,
                        'value'      => -$total_promotion,
                        'sort_order' => -7777
                    );

                    $this->session->data['promotions'][] = $value['module_id'];

                    $this->grandTotal += $total_promotion;

                    if($general_settings['stackable'] == 'no') break;
                }

                self::$ProductsIDS = array_merge(self::$ProductsIDS,array_keys($this->usedProducts));

            }
            self::$total = (float)$this->grandTotal;
        }


        //Apply discount to taxes
        if(isset($moduleSettings['discount_to_taxes']) && $moduleSettings['discount_to_taxes'] == 'yes') {
            if (!empty($taxes)) {
                reset($taxes);
                $key = key($taxes);
                foreach ($total_data as $iteration_total) {
                    if ($iteration_total['code'] == 'sub_total') {
                        $taxes[$key] += self::$taxes;
                    }
                }
            }
        }


        //$total_data = array_merge($total_data, self::$total_data);
        $total -= self::$total;


        if ($total < 0) {
            //$total = 0;
        }

       /* if (!empty($this->session->data['order_id'])) {
            $orderData = array();
            $orderData['promotions'] = $this->session->data['promotions'];
            $orderData['coupon_promotion_module'] = !empty($this->session->data['coupon_promotion_module']) ? $this->session->data['coupon_promotion_module'] : '';
            file_put_contents (DIR_LOGS. $this->session->data['order_id'].'.txt', json_encode($orderData));
        }*/
    }

}
