/**
*<pre>
* ----------------------------------------------------------------------------
* Copyright Notice
* This file contains proprietary information of SOFTGAL S.A.
* 
* Copyright (c) 2004
* ----------------------------------------------------------------------------
*</pre>
*
* <b>SOFTGAL S.A. code</p>
*
* www.softgal.com 
*
* @author G0375 
* @version 1.0
* @date 2004
*/

//---------------------------- CONSTANTES ------------------------------


//Constante de finalidad Adquisicion
var _ADQUISICION = 0;
// Constante de finalidad Transferencia
var _TRANSFERENCIA = 1;
// Constante de Autoconstruccion
var _AUTOCONSTRUCCION = 2;

// Constante de destino Vivienda principal
var _PRINCIPAL = 0;
// Constante de destino Vivienda secundaria
var _SECUNDARIA = 1;


//----------------------------- FUNCIONES ------------------------------

/**
* Calcula el coste denominado "imposto selo abertura"
*
* @param m_fin			montante de financiacion
* @param impuestos	objeto con impuestos aplicables
*/
function calcISeloAbertura(m_fin, duracion, impuestos) {
	return m_fin * impuestos.getISeloApertura(duracion) / 100;
} // calcISeloAbertura()



/**
* Calcula los costes de apertura para la hipoteca
* 
* @param credito		crédito sobre el que se realiza el calculo
* @param m_fin			montante de financiación
*
* @return	los costes de apertura
*/
function calcAbertura(credito, m_fin) {
	//alert('abertura = ' + m_fin + " * " + credito.abertura);
	var abertura = m_fin * credito.abertura / 100;
	return abertura < credito.minAbertura ? credito.minAbertura : abertura;
} // calcAbertura

/**
* Aplica el iva sobre una cantidad
*
* @param cantidad 	valor al que se aplicara el IVA
* @param iva				valor del iva
*
* @return el valor de la cantidad con el iva aplicado
*/
function aplicarIVA(cantidad, iva) {
	return cantidad + cantidad * iva / 100;
} // aplicarIVA()


/**
* Calcula los costes de escrituras para la hipoteca 
*
* @param finalidad		Finalidad del credito 
*										(0=Aquisição | 1=Transferência | 2=Autoconstrução)
* @param m_adq				Montante Aquisição
* @param m_finan			Montante Finaciamento
* @param duracion			Duración del crédito en meses
*
* @return el valor de los costes de escritura
*/
function calcEscritura(costes, finalidad, m_adq, m_finan, duracion) {
	//var escr = costes.emolumentosHipoteca;
	var escr = aplicarIVA(costes.getEscriturasHipoteca(m_finan), impuestos.iva);

	// Aplicamos el IVA solo a los costes
	switch(finalidad) {
		case _ADQUISICION:
			//escr += costes.emolumentosAdq + (impuestos.iSeloAdq * m_adq / 100) + (impuestos.getISeloApertura(duracion) * m_finan / 100) + costes.outrosEncargos;
			//escr += aplicarIVA(costes.getEscriturasCompraVenda(m_adq) + costes.outrosEncargos, impuestos.iva);
			escr += aplicarIVA(costes.getEscriturasCompraVenda(m_adq), impuestos.iva);
			escr += (impuestos.iSeloAdq * m_adq / 100) + (impuestos.getISeloApertura(duracion) * m_finan / 100) ;			
			break;
			
		case _TRANSFERENCIA:
			//escr += aplicarIVA(costes.outrosEncargos, impuestos.iva);
			break;
			
		default:
			escr += m_finan * impuestos.getISeloApertura(duracion) / 100;
			break;
			
	} // switch
	
	escr += aplicarIVA(getEscriturasConfDivida(m_finan) + costes.outrosEncargos, impuestos.iva);
	
	return escr;
	
} // calcEscritura



/**
* Calcula el valor de los Registros Prediais
*
* @param finalidad		Finalidad del credito 
*										(0=Aquisição | 1=Transferência | 2=Autoconstrução)
*
* @return el valor de los registros en funcion de la finalidad
*/
function calcRegistros(costes, finalidad) {
	resultado = costes.regProvisorioHipoteca;
	
	if(finalidad == _ADQUISICION) {
		resultado += costes.regProvisorioAdq;
		
	}
	
	return resultado;

} // calcRegistros



/**
* Redondea una cantidad a un numero determinado de decimales
*
* @param valor			cantidad a redondear
*	@param decimales	decimales a los que redondear el valor
*
* @return	el valor redondeado al número de decimales indicado
*/
function redondea(valor, decimales) {
	var n = Math.pow(10, decimales);
	return Math.round(valor * n) /n ;
	
} // redondea


/**
* Aplica un redondeo a un interes. El redondeo debe ser un
* número entre 0 y 1.
*
* @param taxa			interes a redondear
* @param redondeo		valor al que redondear
*
* @return	el interes con el redondeo añadido, o -1 si el
*					redondeo no esta entre 0 y 1
*/
function calcRedondeo(taxa, redondeo) {
	var resultado = -1
	
	if(redondeo >= 0 &&  redondeo < 1 ) {
		// Calculamos la division entera 
		var div = Math.floor(taxa / redondeo);
		// Tomamos el resto de la division entera
		var mod = redondeo * div - taxa;
		// Si el resto es distinto de cero, aproximamos al siguiente multiplo del redondeo
		resultado = mod != 0 ?  redondeo * (div + 1) : taxa;
	} else if(redondeo = 0) {
		resultado = taxa;
	}

	return resultado;
	
} // calcRedondeo




/**
* Realiza la suma de los elementos de un array
*
* @param	lista		array con los elementos a sumar
*
* @return	la suma de los elementos de la lista
*/
function sumaLista(lista) {
	var i, total;
	total = 0;
	
	for (i = 0; i < lista.length; i++) {
		total += lista[i];
	} // for
	
	return total;
	
} // sumaLista


/**
* Calcula la cuota a pagar en un mes determinado
*
* @param taxa			 intereses en el mes
* @param iSelo			 imposto de selo
* @param duracion	 	 nº de meses que dura el periodo en el que
*									 coincide el mes de calculo
* @param saldo			 cantidad pendiente de amortizar
* @param enCarencia boolean que indica si es mes de carencia	
*/
function calcCuota(taxa, selo, duracion, saldo, enCarencia) {
	var interesMes, x;

	interesMes = ((taxa/100) * (1+(selo/100))) / 12;
	if (enCarencia) {
		cuota = (saldo * interesMes);
	} 	else {
		x = 1 + 1/(-1 + Math.pow(1 + interesMes, duracion));
		cuota = saldo * interesMes * x;
		
	} // else
	
	return cuota;
	
} // calcCuota()


/**
* Calcula la duracion total de una lista de tramos
* de intereses
*
* @param intereses	array de objetos TramoCredito
* 
* @return duracion total de los intereses
*/
function duracionTotal(intereses) {
	var total = 0;
	
	for(i = 0; i < intereses.length; i++) {
		if(intereses[i].DuracionMinima < intereses[i].DuracionMaxima) {
				total += intereses[i].DuracionUsuario;
		} else {
			total += intereses[i].DuracionMinima;
		} // else
	} // for
	
	return total;
	
} // getDuracionTotal()


/**
* Obtiene la carencia
*/
function getCarencia(credito, finalidad) {
	var carencia;
	
	switch(finalidad) {
		case _ADQUISICION:
			carencia = credito.carenciaAdq;
			break;
		
		case _TRANSFERENCIA:
			carencia = credito.carenciaTrans;
			break;
		
		case _AUTOCONSTRUCCION:
			carencia = credito.carenciaAuto;
			break;
			
		default:
			carencia = -1;
			break;
	
	} // switch
	
	return (carencia != 0) ? carencia : credito.carenciaUsuario;
	
} // getCarencia()


/**
* Calcula la tabla de pagos.
*
* Las cuotas son constantes a lo largo de cada periodo. Para calcular
* una cuota, hay que especificar la duración del periodo completo. Las
* carencias se consideran periodos aparte
*
* @param credito		 objeto credito sobre el que se haran los calculos
* @param	destino		 destino (0=Principal, 1=Secundaria)
* @param finalidad	 finalidad (0=Adquisicion, 1= Transferencia, 2=Autoconstruccion)
* @param montante		 montante de financiación
* @param m_aval			montante de avaliaçao
* @param matSpread		matriz de spreads generica
* @param impuestos  objeto con los impuestos aplicables
* @param incremento incremento a aplicar sobre intereses
*/
function calcTablaPagos(credito, destino, finalidad, montante, m_aval, matSpread, impuestos, incremento, iSelo) {
	var intereses;
	var duracionTot;
	//var iSelo;
	
	// Cogemos los intereses en funcion del destino
	if(destino == _PRINCIPAL) {
		intereses = credito.interesesHabPropia;
//		iSelo = 0;
	} else {
		intereses = credito.interesesHabSec;
//		iSelo = impuestos.iSeloJuros;
	} // else
	
	
	duracionTot = duracionTotal(intereses);
	
	var cuota;
	var impuestos;
	var amortizado = 0;
	var interesMes;
	var carencia = getCarencia(credito, finalidad);
	var duracionPeriodo;
	var duracion;
	var saldo = montante;
	var numPeriodo = -1;
	
	//Matriz con la cuota mensual
	var matCuota = new Array();

	// Matriz con las cuotas mensuales con incremento en intereses
	var matCuotaInc = new Array();
	// Valor de los intereses incrementados para cada tramo
	matCuotaInc[0] = new Array();
	// Valor de la cuota con el incremento de intereses aplicado
	matCuotaInc[1] = new Array();

	// Matriz con la deuda mensual
	var matDeuda = new Array();
	// Matriz con los impuestos abonados mensualmente
	var matImpuestos = new Array();
	// Matriz con la cantidad amortizada mensualmente
	var matAmortizado = new Array();
	
	// Para cada mes de la duracion de la hipoteca	
	duracion = duracionTot;
	for(var count = 1; count <= duracionTot; count++) {	
		// Comprobamos si empieza un periodo nuevo o si termino el de carencia
		if(numPeriodo != getTramo(count, intereses) || carencia == 0) {
			numPeriodo = getTramo(count, intereses);
			//alert("En calcCuotas... " + matSpread);
			interesMes = getInteres(count, intereses, montante, m_aval, matSpread);
			
			// Aplicamos el incremento en caso de que haya tipo de referencia
			interesMes += intereses[numPeriodo].TipoReferencia > 0 ? incremento : 0;			
		
			cuota = calcCuota(interesMes, iSelo, duracion, saldo, carencia > 0);
	
			// Calculamos las cuotas 
			matCuotaInc[0][numPeriodo] = interesMes;
			matCuotaInc[1][numPeriodo] = cuota;
			
		} // if
		
		// Ponemos >= para que la carencia se quede en -1 y no salte continuamente
		// el indicador de nuevo periodo
		if(carencia >= 0) carencia--;
		
		impuestos = saldo * (interesMes /100) * (1 +(iSelo / 100)) /12;
		amortizado = cuota - impuestos;
		saldo -= amortizado;
		
		matCuota[count - 1] = cuota;
		matAmortizado[count - 1] = amortizado;
		matDeuda[count - 1] = saldo;
		matImpuestos[count - 1] = impuestos;	
		
		duracion--;
		
	} // for
	
	// En el caso del primer periodo, se debe mostrar la cuota del primer mes
	// por si hubiese periodo de carencia
	matCuotaInc[1][0] = matCuota[0];
	
	return new TablaPagos(matCuota, matAmortizado, matDeuda, matImpuestos, matCuotaInc);
		
} // calcTablaPagos

/**
* Objeto con los valores de la tabla de pagos
*
* @param matCuota				lista con las distintas cuotas mensuales
* @param matAmortizado	lista con la cantidad ya amortizada en cada mes
* @param matDeuda				lista con la cantidad que se adeuda en cada mes
* @param matImpuestos		lista con la cantidad pagada por intereses en cada mes
* @param matCuotaInc		matriz con los siguientes datos:
*				fila 0 : intereses en los distintos tramos
*				fila 1 : cuotas para cada tramo
*/
function TablaPagos(matCuota, matAmortizado, matDeuda, matImpuestos, matCuotaInc) {
	this.matCuota = matCuota;
	this.matAmortizado = matAmortizado;
	this.matDeuda = matDeuda;
	this.matImpuestos = matImpuestos;
	this.matCuotaInc = matCuotaInc
	
} // TablaPagos()


/**
* Realiza el cálculo del TAE empleando el método de bisección
* de aproximación de raices reales de una ecuación. Tan solo
* funciona para pagos mensuales.
*
* @param m_financiado		montante del prestamo
* @param comisiones			valor de las comisiones a contemplar 
*											en el calculo del TAE
* @param matCuota				matriz con los pagos realizados por el
*											tomador del credito
*	@param guess					valor de partida para la aproximacion del TAE
*
* @return el valor del TAE, o -1 en caso de que no converja.
*/
function calcTAE(m_financiado, comisiones, matCuota, guess) {
	// Valor mínimo para que se considere que ha convergido
	var MINDIF = 0.0000001;
	var maxIter = 500;
	var left = 0;
	var right = guess > 0 ? guess : 0.5;
	var ik;
	var f_left;
	var f_right;
	var f_ik;
	var iter = 0;
	var total = m_financiado - comisiones;
	
	// Iteramos hasta que converja o hasta que se llegue al número
	do {
		f_left = f(left, matCuota) - total;
		f_right = f(right, matCuota) - total;
				
		// Si la raiz esta dentro del intervalo
		if(f_left * f_right < 0) {
			ik = (right - left) / 2 + left;
			f_ik = f(ik, matCuota) - total;
		} else {
			return -1;
		} // else
		
		// Movemos los intervalos
		if(f_left * f_ik < 0) {
			right = ik;
		} else {
			left = ik;
		} // else
		
	} while(++iter < maxIter && Math.abs(f_ik) > MINDIF);
	
	// Si no converge
	if(Math.abs(f_ik) > MINDIF) {
		return -1;
	} else {
		return (Math.pow(1 + ik, 12) - 1) * 100;
	} // else
	
} // calcTAE()


/**
* Funcion de la que vamos a buscar raices para el calculo del TAE
* 
* @param ik				valor de la incognita de la ecuación
* @param matCuota		matriz con los pagos realizados
*
* @return el valor de la función para el "ik" especificado
*/
function f(ik, matCuota) {
	var resultado = 0;
	for(var i = 1; i <= matCuota.length; i++) {
		resultado += matCuota[i - 1] * Math.pow(1 + ik, -i);
	} // for
	
	return resultado;
	
} // f()


/**
* Función de calculo del TIR
*/
function IRR(m_financiado, comisiones, guess, matCuota) {
  // Variables
  var valor = m_financiado - comisiones;
  var MINDIF = 0.0000001;
  var irr = 0;
  var was_hi = false;
  var a1 = -valor;
  var a3 = 0;
  var list = 0;
  var i = 0;

  // Calculo del GUESS
  if ( guess <= 0 ) guess = 0.5;
  if ( irr < 0 ) {
		irr = -guess;
  } else {
		irr = guess;
  }

  // Hacemos 50 iteraciones
  for ( var iter = 0; iter <= 50; iter++ ) {
    a3 = a1;
    var j = 1;

    // Para cada mes de vida del crédito
    for ( i = 1; i <= matCuota.length; i++ ) {
			// Calculamos la prestacion para el mes en curso y lo guardamos en list
			list = matCuota[i - 1];
      	a3 += list / Math.pow(1.0 + irr, j);
      	j++;

    } // for

    	if ( Math.abs(a3) < 0.01 ) {return irr;}
    	if ( a3 > 0 ) {
    	  if ( was_hi ) { guess /= 2;}
      	irr += guess;
		
      	if ( was_hi ) {
				guess -= MINDIF;
				was_hi = false;
      	}

		} else {
      guess /= 2;
      irr -= guess;
      was_hi = true;
    }

    if ( guess <= MINDIF ) {return irr;}
    
  }
  
  return 0; 
}
