<?php
class FreteUtils extends Model {

	private $altura_max;
	private $largura_max;
  private $comprimento_max;

  private $altura_min;
	private $largura_min;
  private $comprimento_min;

  //----Para calculo por volumes Ps. Não confundr o tipo de calculo por volume da intelipost com os demais
  private $cubagem_max;
	private $cubagem_min;
  private $lado_max = 105;
  //-------------------------------------------------------------------------------------------------------

	private $soma_dim_max = 200; //medida máxima das somas da altura, largura, comprimento
  private $soma_dim_min = 0;

	private $peso_max = 30; //Pesos em KG
  private $peso_min = 0.300;

  public function setUnidadesMedida($dados = array()){
    ////As medidas padrões seguem a referencia de medida dos correios, caso a trasnportadora utilize medidas diferentes, setalas....
    $this->altura_max       = isset($dados['altura_max']) ? $dados['altura_max'] : 105 ;
    $this->largura_max      = isset($dados['largura_max']) ? $dados['largura_max'] : 105 ;
    $this->comprimento_max  = isset($dados['comprimento_max']) ? $dados['comprimento_max'] : 105 ;

    $this->altura_min       = isset($dados['altura_min']) ? $dados['altura_min'] : 2 ;
    $this->largura_min      = isset($dados['largura_min']) ? $dados['largura_min'] : 11 ;
    $this->comprimento_min  = isset($dados['comprimento_min']) ? $dados['comprimento_min'] : 16 ;

    $this->cubagem_max      = isset($dados['cubagem_max']) ? $dados['cubagem_max'] : 0;
    $this->cubagem_min      = isset($dados['cubagem_min']) ? $dados['cubagem_min'] : 0;
    $this->cubagem_min      = isset($dados['cubagem_min']) ? $dados['cubagem_min'] : 0;

    $this->soma_dim_max      = isset($dados['soma_dim_max']) ? $dados['soma_dim_max'] : 200;
    $this->soma_dim_min      = isset($dados['soma_dim_min']) ? $dados['soma_dim_min'] : 0;

    $this->peso_max      = isset($dados['peso_max']) ? $dados['peso_max'] : 30;
    $this->peso_min      = isset($dados['peso_min']) ? $dados['peso_min'] : 0.300;

  }

	// retorna a dimensão em centímetros
	public function getDimensaoEmCm($unidade_id, $dimensao){

		if(is_numeric($dimensao)){
			$length_class_product_query = $this->db->query("SELECT * FROM " . DB_PREFIX . "length_class mc LEFT JOIN " . DB_PREFIX . "length_class_description mcd ON (mc.length_class_id = mcd.length_class_id) WHERE mcd.language_id = '" . (int)$this->config->get('config_language_id') . "' AND mc.length_class_id =  '" . (int)$unidade_id . "'");

			if(isset($length_class_product_query->row['unit'])){
				if($length_class_product_query->row['unit'] == 'mm'){
					return $dimensao / 10;
				}
			}
		}
		return $dimensao;
	}

	// retorna o peso em quilogramas
	public function getPesoEmKg($unidade_id, $peso){

		if(is_numeric($peso)) {
			$weight_class_product_query = $this->db->query("SELECT * FROM " . DB_PREFIX . "weight_class wc LEFT JOIN " . DB_PREFIX . "weight_class_description wcd ON (wc.weight_class_id = wcd.weight_class_id) WHERE wcd.language_id = '" . (int)$this->config->get('config_language_id') . "' AND wc.weight_class_id =  '" . (int)$unidade_id . "'");

			if(isset($weight_class_product_query->row['unit'])){
				if($weight_class_product_query->row['unit'] == 'g'){
					return ($peso / 1000);
				}
			}
		}
		return $peso;
	}

	// pré-validação das dimensões e peso do produto
	public function validar($produto){

		if(!is_numeric($produto['height']) || !is_numeric($produto['width']) || !is_numeric($produto['length']) || !is_numeric($produto['weight'])){
			$this->log->write(sprintf($this->language->get('error_dim'), $produto['name']));
			return false;
		}

		$altura = $produto['height'];
		$largura = $produto['width'];
		$comprimento = $produto['length'];
		$peso = $produto['weight'];

		if( $altura > $this->altura_max || $largura > $this->largura_max || $comprimento > $this->comprimento_max ){
			$this->log->write(sprintf($this->language->get('error_dim_limite'), $this->comprimento_max, $this->largura_max, $this->altura_max, $produto['name'], $comprimento, $largura, $altura));
			return false;
		}

		$soma_dim = $altura + $largura + $comprimento;
		if( $soma_dim > $this->soma_dim_max) {
			$this->log->write(sprintf($this->language->get('error_dim_soma'), $this->soma_dim_max, $produto['name'], $soma_dim));
			return false;
		}

		if( $peso > $this->peso_max) {
			$this->log->write(sprintf($this->language->get('error_peso'), $this->peso_max, $produto['name'], $peso));
			return false;
		}

  		return true;
  }

	public function validarPorVolume($produto){

		if (empty($produto['height'])) { $produto['height'] = $this->altura_min; }
		if (empty($produto['width'])) { $produto['width'] = $this->largura_min; }
		if (empty($produto['length'])) { $produto['length'] = $this->comprimento_min; }
		if (empty($produto['weight'])) { $produto['weight'] = $this->peso_min; }

		$cubagem = (float)$produto['height'] * (float)$produto['width'] * (float)$produto['length'];

		if($produto['weight'] > $this->peso_max || $cubagem > $this->cubagem_max || $produto['height'] > $this->lado_max || $produto['width'] > $this->lado_max || $produto['length'] > $this->lado_max){
			$this->log->write(sprintf($this->language->get('error_limites'), $produto['name']));

			return false;
		}

		return true;
	}

    // 'empacota' os produtos do carrinho em caixas com dimensões e peso dentro dos limites
    // algoritmo desenvolvido por: Thalles Cardoso <thallescard@gmail.com>
    public function organizarEmCaixas($produtos) {

  		$caixas = array();

  		foreach ($produtos as $prod) {

  			$prod_copy = $prod;


  			// muda-se a quantidade do produto para incrementá-la em cada caixa
  			$prod_copy['quantity'] = 1;

  			// todas as dimensões da caixa serão em cm e kg
  			$prod_copy['width']	= $this->getDimensaoEmCm($prod_copy['length_class_id'], $prod_copy['width']);
  			$prod_copy['height']= $this->getDimensaoEmCm($prod_copy['length_class_id'], $prod_copy['height']);
  			$prod_copy['length']= $this->getDimensaoEmCm($prod_copy['length_class_id'], $prod_copy['length']);

  			// O peso do produto não é unitário como a dimensão. É multiplicado pela quantidade. Se quisermos o peso unitário, teremos que dividir pela quantidade.
  			$prod_copy['weight']= $this->getPesoEmKg($prod_copy['weight_class_id'], $prod_copy['weight'])/$prod['quantity'];

  			$prod_copy['length_class_id'] = $this->config->get('select_comprimento');
  			$prod_copy['weight_class_id'] = $this->config->get('select_peso');

  			$cx_num = 0;


				if (($prod_copy['comp_progressiva'] > 0) || ($prod_copy['larg_progressiva'] >0) || ($prod_copy['alt_progressiva'] > 0)&&($prod['quantity']>1)){
					$prodquantidade = $prod['quantity']-1; //-1 por não conta o item principal
					$prod_copy['height'] = $prod_copy['height'] +(($prodquantidade)*$prod_copy['alt_progressiva']);
					$prod_copy['length'] = $prod_copy['length'] +(($prodquantidade)*$prod_copy['comp_progressiva']);
					$prod_copy['width']  = $prod_copy['width'] +(($prodquantidade)*$prod_copy['larg_progressiva']);
					$prod['quantity'] = 1; // caso o produto seja empilhado, seta para um pois ira adicionalo como se fosse uma unidade na caixa
				}

				if ($this->tipo_calculo == Type_FreteTipoCalculo::Tp_FreteTpCalcDimensao){
					for ($i = 1; $i <= $prod['quantity']; $i++) {

						// valida as dimensões do produto
						if ($this->validar($prod_copy)){

							// cria-se a caixa caso ela não exista.
							isset($caixas[$cx_num]['weight']) ? true : $caixas[$cx_num]['weight'] = 0;
							isset($caixas[$cx_num]['height']) ? true : $caixas[$cx_num]['height'] = 0;
							isset($caixas[$cx_num]['width']) ? true : $caixas[$cx_num]['width'] = 0;
							isset($caixas[$cx_num]['length']) ? true : $caixas[$cx_num]['length'] = 0;

							$new_width 	= $caixas[$cx_num]['width'] + $prod_copy['width'];
							$new_height = $caixas[$cx_num]['height'] + $prod_copy['height'];
							$new_length = $caixas[$cx_num]['length'] + $prod_copy['length'];
							$new_weight = $caixas[$cx_num]['weight'] + $prod_copy['weight'];

							$cabe_do_lado = ($new_width <= $this->largura_max) && $this->somaDimDentroLimite($caixas, $prod_copy, $cx_num, 'lado');

							$cabe_no_fundo = ($new_length <= $this->comprimento_max) && $this->somaDimDentroLimite($caixas, $prod_copy, $cx_num, 'fundo');

							$cabe_em_cima = ($new_height <= $this->altura_max) && $this->somaDimDentroLimite($caixas, $prod_copy, $cx_num, 'cima');

								$peso_dentro_limite = ($new_weight <= $this->peso_max);

							// o produto cabe na caixa
							if (($cabe_do_lado || $cabe_no_fundo || $cabe_em_cima) && $peso_dentro_limite) {

								// já existe o mesmo produto na caixa, assim incrementa-se a sua quantidade

								if (isset($caixas[$cx_num]['produtos'][$prod_copy['key']])){
									$caixas[$cx_num]['produtos'][$prod_copy['key']]['quantity']++;
								}
								// adiciona o novo produto
								else {
									$caixas[$cx_num]['produtos'][$prod_copy['key']] = $prod_copy;
								}

								// aumenta-se o peso da caixa
								$caixas[$cx_num]['weight'] += $prod_copy['weight'];

								// ajusta-se as dimensões da nova caixa
								if ($cabe_do_lado) {
									$caixas[$cx_num]['width'] += $prod_copy['width'];

									// a caixa vai ficar com a altura do maior produto que estiver nela
									$caixas[$cx_num]['height'] = max($caixas[$cx_num]['height'], $prod_copy['height']);

									// a caixa vai ficar com o comprimento do maior produto que estiver nela
									$caixas[$cx_num]['length'] = max($caixas[$cx_num]['length'], $prod_copy['length']);
								}
								else if ($cabe_no_fundo) {
									$caixas[$cx_num]['length'] += $prod_copy['length'];

									// a caixa vai ficar com a altura do maior produto que estiver nela
									$caixas[$cx_num]['height'] = max($caixas[$cx_num]['height'], $prod_copy['height']);

									// a caixa vai ficar com a largura do maior produto que estiver nela
									$caixas[$cx_num]['width'] = max($caixas[$cx_num]['width'], $prod_copy['width']);

								}
								else if ($cabe_em_cima) {
									$caixas[$cx_num]['height'] += $prod_copy['height'];

									//a caixa vai ficar com a altura do maior produto que estiver nela
									$caixas[$cx_num]['width'] = max($caixas[$cx_num]['width'], $prod_copy['width']);

									//a caixa vai ficar com a largura do maior produto que estiver nela
									$caixas[$cx_num]['length'] = max($caixas[$cx_num]['length'], $prod_copy['length']);
								}
							}
							// tenta adicionar o produto que não coube em uma nova caixa
							else{
								$cx_num++;
								$i--;
							}
						}
						// produto não tem as dimensões/peso válidos então abandona sem calcular o frete.
						else {
							$caixas = array();
							break 2;  // sai dos dois foreach
						}
					}
				}else{

					for ($i = 1; $i <= $prod['quantity']; $i++) {


	  				if ($this->validarPorVolume($prod_copy)){

	   					// cria-se a caixa caso ela não exista.
						isset($caixas[$cx_num]['peso']) ? true : $caixas[$cx_num]['peso'] = 0;
						isset($caixas[$cx_num]['cubagem']) ? true : $caixas[$cx_num]['cubagem'] = 0;

	  					$peso = $caixas[$cx_num]['peso'] + $prod_copy['weight'];
							$cubagem = $caixas[$cx_num]['cubagem'] + ($prod_copy['width'] * $prod_copy['height'] * $prod_copy['length']);

	 						$peso_dentro_limite = ($peso <= $this->peso_max);
							$cubagem_dentro_limite = ($cubagem <= $this->cubagem_max);

	  					if ($peso_dentro_limite && $cubagem_dentro_limite) {

								// já existe o mesmo produto na caixa, assim incrementa-se a sua quantidade
								if (isset($caixas[$cx_num]['produtos'][$prod_copy['key']])) {
									$caixas[$cx_num]['produtos'][$prod_copy['key']]['quantity']++;
								}
								// adiciona o novo produto
								else {
									$caixas[$cx_num]['produtos'][$prod_copy['key']] = $prod_copy;
								}

								$caixas[$cx_num]['peso'] = $peso;
								$caixas[$cx_num]['cubagem'] = $cubagem;
	  					}
	  					// tenta adicionar o produto que não coube em uma nova caixa
	  					else{
	  						$cx_num++;
	  						$i--;
	  					}
	  				}
	  				// produto não tem as dimensões/peso válidos então abandona sem calcular o frete.
	  				else {
	  					$caixas = array();
	  					break 2;  // sai dos dois foreach
	  				}
	  			}
				}

  		}
  		return $caixas;
  	}
  	// retorna o valor total dos prodtos na caixa
  	public function getTotalCaixa($products) {
  		$total = 0;

  		foreach ($products as $product) {
  			$total += $this->currency->format(($product['price']*$product['quantity']), '', '', false);
  		}
  		return $total;
  	}

  	public function ajustarDimensoes($caixa){

  		// a altura não pode ser maior que o comprimento, assim inverte-se as dimensões
  		$height = $caixa['height'];
  		$width = $caixa['width'];
  		$length = $caixa['length'];
  		$weight = $caixa['weight'];

  		// se dimensões menores que a permitida, ajusta para o padrão
  		if( $height < $this->altura_min){
  			$height = $this->altura_min;
  		}
  		if($width < $this->largura_min){
  			$width = $this->largura_min;
  		}
  		if($length < $this->comprimento_min ){
  			$length = $this->comprimento_min;
  		}
  		if($weight < $this->peso_min ){
  			$weight = $this->peso_min;
  		}
  		if( $height > $length){
  			$temp = $height;
  			$height = $length;
  			$length = $temp;
  		}

  		return array($weight, $height, $width, $length);
  	}
  public function somaDimDentroLimite($caixas, $prod_copy, $cx_num, $orientacao){

		if($orientacao == 'lado') {
			$width = $caixas[$cx_num]['width'] + $prod_copy['width'];
			$height = max($caixas[$cx_num]['height'], $prod_copy['height']);
			$length = max($caixas[$cx_num]['length'], $prod_copy['length']);
		}
		elseif($orientacao == 'fundo') {
			$length = $caixas[$cx_num]['length'] + $prod_copy['length'];
			$height = max($caixas[$cx_num]['height'], $prod_copy['height']);
			$width = max($caixas[$cx_num]['width'], $prod_copy['width']);
		}
		elseif($orientacao == 'cima') {
			$height = $caixas[$cx_num]['height'] + $prod_copy['height'];
			$width = max($caixas[$cx_num]['width'], $prod_copy['width']);
			$length = max($caixas[$cx_num]['length'], $prod_copy['length']);
		}
		else{
			$width = $caixas[$cx_num]['width'];
			$height = $caixas[$cx_num]['height'];
			$length = $caixas[$cx_num]['length'];
		}
		$dentroLimite = ($width + $height + $length) <= $this->soma_dim_max;

		return $dentroLimite;
	}

	public function raizCubica($n) {
		return pow($n, 1/3);
	}

}
