<?php

/**
 * Installment Plan Info Module
 * 
 * @author  Kyo (AKA Yasuhiro Sota)
 * @version 1.1.3
 * @license Commercial License
 * @package system
 * @subpackage  system.library.installment_plan_info
 */

require_once DIR_SYSTEM . 'library/installment_plan_info/matheval.class.php';

class InstallmentPlanInfo {

/**
 * Holds payment calculation formulas.
 * 
 * IMPORTANT: In order to customize the formulas, please define them in system/library/installment_plan_info/custom_formulas.php
 *
 * The following tokens are available to use: and are replaced with values when the template is processed.
 * 
 *  - [disc]    : Discount %
 *  - [int]     : Interest %
 *  - [instal]  : Number of installments
 *  - [price]   : Price
 *  - [prin]    : Principal
 * 
 * @var array
 */     
  private static $formulas = array(
      'bcash' => array(
          'equal' => array(
              '[int] <= 0' => '([price]*(1-[disc]/100))/[instal]',
              '[int] > 0' => '(([price]*(1-[disc]/100))*(1+([int]/100))^[instal])/[instal]'
          ),
          'variable' => array(
              '[int] <= 0' => '([price]*(1-[disc]/100))',
              '[int] > 0' => '((([price]*(1-[disc]/100))*(1+([int]/100))^[instal])/[instal])*[instal]'
          )
      ),
      'pagseguro' => array(
          'equal' => array(
              '[int] <= 0' => '([price]*(1-[disc]/100))/[instal]',
              '[int] > 0' => '(([price]*(1-[disc]/100))*([int]/100))/(1-(1/((1+([int]/100))^[instal])))'
          ),
          'variable' => array(
              '[int] <= 0' => '([price]*(1-[disc]/100))',
              '[int] > 0' => '(([price]*(1-[disc]/100))*([int]/100))/(1-(1/((1+([int]/100))^[instal])))*[instal]'
          )
      ),
      'paypal' => array(
          'equal' => array(
              '[int] <= 0' => '([price]*(1-[disc]/100))/[instal]',
              '[int] > 0' => '(([price]*(1-[disc]/100))*(1+([int]/100)))/[instal]'
          ),
          'variable' => array(
              '[int] <= 0' => '([price]*(1-[disc]/100))',
              '[int] > 0' => '((([price]*(1-[disc]/100))*(1+([int]/100))^[instal])/[instal])*[instal]'
          )
      ),
      'redecard' => array(
          'equal' => array(
              '[int] <= 0' => '([price]*(1-[disc]/100))/[instal]',
              '[int] > 0' => '(([price]*(1-[disc]/100))*(1+([int]/100))^[instal])/[instal]'
          ),
          'variable' => array(
              '[int] <= 0' => '([price]*(1-[disc]/100))',
              '[int] > 0' => '((([price]*(1-[disc]/100))*(1+([int]/100))^[instal])/[instal])*[instal]'
          )
      ),      
      'cielo' => array(
          'equal' => array(
              '[int] <= 0' => '([price]*(1-[disc]/100))/[instal]',
              '[int] > 0' => '(([price]*(1-[disc]/100))*(1+([int]/100))^[instal])/[instal]'
          ),
          'variable' => array(
              '[int] <= 0' => '([price]*(1-[disc]/100))',
              '[int] > 0' => '((([price]*(1-[disc]/100))*(1+([int]/100))^[instal])/[instal])*[instal]'
          )
      ),      
  );
 
/**
 * Name of module key
 *
 * @var string
 */  
  static public $module_key = 'installment_plan_info';
  

  private $config;
  private $db;
  private $session;
  private $currency;
  private $language;

  private $config_data = null;
  private $math = null;
  private $_formulas = null;
  
/**
 * Constructor.
 *
 * @param object $registry
 * @return void
 */  
	public function __construct($registry) {
		$this->config = $registry->get('config');
		$this->db = $registry->get('db');
//		$this->request = $registry->get('request');
		$this->session = $registry->get('session');
		$this->language = $registry->get('language');
		$this->currency = $registry->get('currency');
    
    // Instantiate a math class
    $this->math = new EvalMath;
    $this->_formulas = self::getFormulas();
    
    // Get the configuration
    $obj_config = clone $this->config;
    $array_config = (array)$obj_config;
    $this->config_data = array_pop($array_config);
  }
  
/**
 * getPromoSnippet method
 *
 * @param string $key
 * @return array $options 
 */    
  public function getPromoSnippet($key, $options = null) {

    if (!(isset($this->config_data['installment_plan_info_module']) && $this->config_data['installment_plan_info_module'])  ) {
      return false;
    }

    $options = array_merge(array(
        'price' => null, 
        'special' => null,
        'shipping' => null
    ), (array)$options);
    
    
    if ($options['special']) {
      $price = $options['special'];
    } else {
      $price = $options['price'];
    }

    $price = $this->convertFormattedPriceToNumbers($price);

    // Get the settings.
    $content = null;
    
    foreach($this->config_data['installment_plan_info_module'] as  $module) {
      $result = $this->config->get('installment_plan_info_' . $module['content_id']);
      if (isset($result['key']) && $result['key'] == $key) {
        $content = $result;
        break;
      }
    }
    unset($result);
    

    if (!(isset($content['installment_plans']) && $content['installment_plans'])) {
      return false;
    }

    // Promo messages
    $promo_messages = array();
    
    // Minimum Installment Amount
    if ($this->session->data['currency']) {
      $currency_code = $this->session->data['currency'];
    } else {
      $currency_code = $this->config->get('config_currency');
    }

    if (isset($content['min_instal_amt'][$currency_code])) {
      $min_instal_amt = (float)$content['min_instal_amt'][$currency_code];
    } else {
      $min_instal_amt = 0;
    }

    foreach ($content['installment_plans'] as $key => $plan) {

      if (!isset($plan['no_of_instal'])) {
        continue;
      }

      foreach ($plan['payments'] as $paymentKey => $payment) {
        
        // Skip and continue if a promo message is no found. 
        if (!(isset($plan['promo_msg'][(int) $this->config->get('config_language_id')]) && $plan['promo_msg'][(int) $this->config->get('config_language_id')])) {
          continue;
        }

        if ($plan['plan_type'] == 1) {  //  Equal payment method
          $plan_type_key = 'equal';
          $_price = $price;

        } else if ($plan['plan_type'] == 2) {  //  Variable payment method
          $plan_type_key = 'variable';
          $_price = $price * ($payment['principal'] / 100);
        }

        // Set priciple to 100% if not specified yet.
        if (!isset($payment['principal'])) {
          $payment['principal'] = 100;
        }

        $payment['principal'] = (float)$payment['principal'];

        // Get type of formula.
        $formula_type = null;

        if (isset($plan['formula_type']) && $plan['formula_type']) {
          $formula_type = $plan['formula_type'];
        }

        // Apply formula.
        $formula = null;

        foreach ($this->_formulas[$formula_type][$plan_type_key] as $operation  => $value) {
          if ($value) {
            $op = str_replace(
              array('[disc]', '[int]', '[instal]', '[price]', '[prin]'),
              array((float)$payment['discount'], (float)$payment['interest'], (int)$plan['no_of_instal'], (float)$_price, (float)$payment['principal']),
            $operation);

            if (eval("return ($op);")) { 
              $formula = $value;
              break;
            }
          }
        }
        unset($operation, $op, $value);

        // Calculate the amount for an installment paymment by using a formula above.
        $amount = null;

        if ($formula) {
          $_formula = str_replace(
            array('[disc]', '[int]', '[instal]', '[price]', '[prin]'),
            array((float)$payment['discount'], (float)$payment['interest'], (int)$plan['no_of_instal'], (float)$_price, (float)$payment['principal']),
          $formula);

          $amount = $this->math->evaluate($_formula);
          unset($_formula);
        }

        // Remove a row that contains an amount smaller than the minimum installment amount.
        if($amount <= $min_instal_amt) {
          continue;
        }
        
        if ($options['shipping']) {
          $shipping = $this->convertFormattedPriceToNumbers($options['shipping']);
          $amount = $amount + $shipping;
        }

        // Promo messages
        $promo_msg = $plan['promo_msg'][(int) $this->config->get('config_language_id')];

        // Replace tokens
        $promo_messages[] = str_replace(
          array('[d'. $paymentKey . ']', '[i'. $paymentKey . ']', '[p'. $paymentKey . ']', '[x]'),
          array($payment['discount'], round($payment['interest'], 2), $this->currency->format($amount), $plan['no_of_instal']),
        $promo_msg);

      } // END of foreach

    } // END of foreach

    return $promo_messages;    
  }
  
  
/**
 * Convert formatted price back to numbers
 *
 * @param string $formatted_price
 * @return decimal $price
 */    
  public function convertFormattedPriceToNumbers($formatted_price) {
    // Convert formatted price back to numbers.
    $dec_point = $this->language->get('decimal_point');
    $price = floatval(str_replace($dec_point, '.', preg_replace('/[^\d'.preg_quote($dec_point).']/', '', $formatted_price)));
    return $price;
  }
  
/**
 * Returns formulas for payment calculation.
 *
 * @param void
 * @return array Predefined formulas 
 */  
  static public function getFormulas() {
    // Get predefined formulas.
    $data = self::$formulas;
    
    // Include custom formulas if it exists.
    if (file_exists(DIR_SYSTEM . 'library/installment_plan_info/custom_formulas.php')) {

      require DIR_SYSTEM . 'library/installment_plan_info/custom_formulas.php';

      if (isset($formulas) && is_array($formulas)) {
        // Merge user-defined formulas.
        $data = self::__array_replace_recursive($data, $formulas);
        unset($formulas);
      }
    }
    
    return $data;
  }

/**
 * Returns image base path.
 *
 * @param void
 * @return string Image base path
 */  
  static public function getImageBasePath() {
    $image_base_path = self::$module_key;

    // Add trailing slash to the image path.
    if (substr($image_base_path, -1) != DIRECTORY_SEPARATOR  ) {
      $image_base_path .= DIRECTORY_SEPARATOR ;
    }
    
    return $image_base_path;
  }  
  
/**
 * Replaces elements from passed arrays into the first array recursively
 *
 * @param array $base The array in which elements are replaced.
 * @param array $replacements The array from which elements will be extracted. 
 * @return array 
 */ 
  static private function __array_replace_recursive($base, $replacements) {
    foreach (array_slice(func_get_args(), 1) as $replacements) {
      $bref_stack = array(&$base);
      $head_stack = array($replacements);

      do {
        end($bref_stack);

        $bref = &$bref_stack[key($bref_stack)];
        $head = array_pop($head_stack);

        unset($bref_stack[key($bref_stack)]);

        foreach (array_keys($head) as $key) {
          if (isset($key, $bref) && isset($bref[$key]) && is_array($bref[$key]) && is_array($head[$key])) {
            $bref_stack[] = &$bref[$key];
            $head_stack[] = $head[$key];
          } else {
            $bref[$key] = $head[$key];
          }
        }
      } while (count($head_stack));
    }

    return $base;
  }

}

?>