package com.fp.general.record;

import java.math.BigDecimal;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import com.fp.common.helper.Constant;
import com.fp.common.logger.APPLogger;
import com.fp.persistence.commondb.PersistenceHelper;
import com.fp.persistence.pgeneral.msg.TgeneMessageStructure;
import com.fp.persistence.pgeneral.msg.TgeneMessageStructureData;
import com.fp.persistence.pgeneral.msg.TgeneMessageStructureHeader;
import com.fp.telered.exception.TeleredException;

/**
 * Clase que se encarga de parsear un registro. 
 * @author Jorge Vaca.
 * @version 2.1
 */
public class ParserRecord {

	private ParserRecord () {}
	
    /**
     * Metodo que llena los datos del record
     * @param inString Cadena de busqueda
     * @param structurecode Codigo de estructura del archivo. 
     * @return  Record
     * @throws Exception
     */
    public static Record read(String inString, TgeneMessageStructure ms,boolean isheader,Integer recordnumber) throws Exception {
        Record record = new Record();
        if (isheader) {
        	readheader(inString, record, ms,recordnumber);
        }             
        readdata(inString, record, ms ,recordnumber);
        return record;
    }

    /**
     * Metodo que completa un Record, con la definicion de TACHSTRUCTURE, considera tipos de registro 6 y 7.
     * @param inString  Datos en formato nacha.
     * @param record	Registro a completar data.
     * @param ms	Definicion de tipo de estructura.
     * @throws Exception
     */
    private static void readheader(String inString, Record record, TgeneMessageStructure ms,Integer recordnumber) throws Exception {        
        Map<Integer,FieldData> map = new HashMap<Integer, FieldData>();
    	//Crea un bean persistente en la base.
        record.setHibernateBean(ms.getHeaderclass());
        //Obtiene la estructura de campos para el tipo de ach.
        List<TgeneMessageStructureHeader> ldata = getTgeneMessageStructureHeader(ms.getPk());
        for (TgeneMessageStructureHeader obj : ldata) {
            FieldData field = new FieldData();
            Integer fieldnumber = obj.getPk().getCode();
            field.setAttribute(obj.getAttribute());
            field.setFieldNumber(fieldnumber);
            Object valueAux = null;
            try {
                valueAux = getValue(inString, obj,obj.getAttribute(),recordnumber);
            } catch (Exception ex) {
            	APPLogger.getLogger().error(ex.getLocalizedMessage());
                field.setErrorMessage(ex.getMessage());
                field.setValid(false);
            }
            if (Constant.ifYes(obj.getRequired()) && valueAux == null ) {
                field.setValid(false);
            }       
            field.setValue(valueAux);
            map.put(fieldnumber, field);
        }
        record.setFieldMap(map);
    }

    /**
     * Metodo que arma los datos de la tabla TACHFILESSTRUCTURE, considera tipos de registo 1,5,8,9
     * @param inString  Datos en formato nacha.
     * @param record   Record a completar datos.
     * @param ms	Definicion de tipo de estructura.
     * @throws Exception
     */
    private static void readdata(String inString, Record record,TgeneMessageStructure ms,Integer recordnumber) throws Exception {
    	Map<Integer,FieldData> map = new HashMap<Integer, FieldData>();    
    	//Crea un bean persistente en la base.
    	record.setHibernateBean(ms.getDetailclass());
    	List<TgeneMessageStructureData> ldata = getTgeneMessageStructureData(ms.getPk());			
    	for (TgeneMessageStructureData obj : ldata) {
    		FieldData field = new FieldData();
            Integer fieldnumber = obj.getPk().getCode();
            field.setAttribute(obj.getAttribute());
            field.setFieldNumber(fieldnumber);
            Object valueAux = null;
            if(obj.getAttribute().compareTo("cardacctnbr") ==0){
            	valueAux = null;
            }
            try {
                valueAux = getValue(inString,obj,obj.getAttribute(),recordnumber);
            } catch (TeleredException ex) {
                throw ex;
            }catch (Exception ex) {
                field.setErrorMessage(ex.getMessage());
                field.setValid(false);
            }
            field.setValue(valueAux);
            if (Constant.ifYes(obj.getRequired()) && valueAux == null ) {
                field.setValid(false);
            }
            map.put(fieldnumber, field);
            if(Constant.ifYes(obj.getRequired()) && valueAux == null){
            	throw new TeleredException("GENE-0031", "VALOR REQUERIDO PARA EL CAMPO: {0} NUMERO DE REGISTRO: {1}", 
            			obj.getAttribute(),recordnumber + 1);
            }
        }
        record.setFieldMap(map);
    }

    /**
     * Entrega el valor de un campo dada la estructura de un tipo de registro definida en TgeneMessageStructureHeader.
     * @param inString  Datos un registro..
     * @param structure  Datos de definicion de estructura de un campo.
     * @return  Object
     * @throws Exception
     */
    private static Object getValue(String inString,TgeneMessageStructureHeader structure,String atributo,Integer numeroregistro) throws Exception {
    	return getValue(inString,
    			structure.getInitposition(), structure.getEndposition(),
    			structure.getDecimalnumber(), structure.getDatatype(),atributo,numeroregistro);
    }
    
    /**
     * Entrega el valor de un campo dada la estructura de un tipo de registro definida en TgeneMessageStructureData.
     * @param inString  Datos un registro..
     * @param fileStructure  Datos de definicion de estructura de un campo.
     * @return  Object
     * @throws Exception
     */
    private static Object getValue(String inString,TgeneMessageStructureData structure,String atributo,Integer numeroregistro) throws Exception {
    	return getValue(inString,
    			structure.getInitposition(), structure.getEndposition(),
    			structure.getDecimalnumber(), structure.getDatatype(),atributo,numeroregistro);
    }
    
    /**
     * Metodo que devuelve el valor del campo de un regsitro
     * @param inString Cadena donde se va a buscar el campo
     * @param initPosition Posicion inicial del campo
     * @param endPosition Posicion final del campo
     * @param decimalnumber Numero de decimales
     * @param datatype Tipo de dato del campo
     * @param formatdate Formato para fecha
     * @param formattime Formato para tiempo
     * @return  Object
     * @throws Exception
     */
    private static Object getValue(String inString, int initPosition,
            int endPosition, Integer decimalnumber, String datatype,String atributo,Integer numeroregistro)
            throws Exception {
        String value = inString.substring(--initPosition, endPosition);
        if (value.compareTo("") == 0 || value == null) {
        	//la validacion del campo requerido se realiza en la parte que llama al metodo que obtiene el valor.
        	return null;
        }
        Object auxValue = value;
        try {
            if (datatype.toUpperCase().compareTo("STRING") == 0) {
            	auxValue = value;
            } else if (datatype.toUpperCase().compareTo("INTEGER") == 0) {
            	value = value.trim();
                if (decimalnumber != null && decimalnumber > 0) {
                    String number = value.substring(0, value.length() - decimalnumber)
                            + "."
                            + value.substring(value.length() - decimalnumber, value.length());
                    auxValue = new BigDecimal(number);
                } else {
                	auxValue = Integer.valueOf(value);
                }
            }else if(datatype.toUpperCase().compareTo("LONG") == 0){
            	auxValue = Long.valueOf(value.trim());
            }else if(datatype.toUpperCase().compareTo("BIGDECIMAL") == 0){
            	auxValue = new BigDecimal(value.trim());
            }
		} catch (NumberFormatException ex) {
        	throw new TeleredException("GENE-0032", "TIPO DE DATO: {0} PARA EL CAMPO: {1} NO SE PUEDE TRANSFORMAR VALOR: {2} REGISTRO {3}", 
        			datatype,atributo,value,numeroregistro + 1);
		}
        return auxValue;
    }
    

    
    /**
     * Enterga una lista de definicion de campos de cabecera de un archivo. 
     * @param pStructureCode  Codigo de estructura.  
     * @return  List<TgeneMessageStructureHeader>
     * @throws Exception
     */
    private static List<TgeneMessageStructureHeader> getTgeneMessageStructureHeader(String pStructureCode) throws Exception {
    	return TgeneMessageStructureHeader.find(PersistenceHelper.getEntityManager(), pStructureCode);
    }

    /**
     * Enterga una lista de definicion de campos de datos de un archivo. 
     * @param pStructureCode  Codigo de estructura.  
     * @return  List<TgeneMessageStructureData>
     * @throws Exception
     */
    private static List<TgeneMessageStructureData> getTgeneMessageStructureData(String pStructureCode) throws Exception {
    	return TgeneMessageStructureData.find(PersistenceHelper.getEntityManager(), pStructureCode);
    }
}
