package com.mx.dla.dda.excelMapper.bos;

import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.util.Date;

import javax.annotation.PostConstruct;

import org.apache.commons.beanutils.BeanUtilsBean;
import org.apache.commons.lang3.ClassUtils;
import org.apache.commons.lang3.reflect.FieldUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.context.annotation.Scope;
import org.springframework.stereotype.Component;

import com.mx.dla.dda.excelMapper.exceptions.ExcelMapperException;

@Component
@Scope("prototype")
public class BeanPopulator {
	private Logger logger = LoggerFactory.getLogger("excelMapper");

	private BeanUtilsBean beanUtils;

	/**
	 * The section is declared wheather is necesary modify the behavior of the
	 * beanUtils
	 */
	@PostConstruct
	public void init() {
		// secction declared if is necesari modify the behavior of the beanUtils
		beanUtils = BeanUtilsBean.getInstance();
	}

	public <T extends Object> T initObject(Class<T> cl)
			throws ExcelMapperException {
		T object;
		try {
			object = cl.newInstance();
			if (!ClassUtils.isPrimitiveOrWrapper(cl)) {
				Field[] fields = FieldUtils.getAllFields(cl);
				for (Field field : fields) {
					populate(object, field);
				}
			}
		} catch (InstantiationException e) {
			logger.error("Managed Error ", e);
			throw new ExcelMapperException(
					ExcelMapperException.ERROR_INIT_POPULATE, e);
		} catch (IllegalAccessException e) {
			logger.error("Managed Error ", e);
			throw new ExcelMapperException(
					ExcelMapperException.ERROR_INIT_POPULATE, e);
		}
		return object;
	}

	private void populate(Object target, Field field)
			throws ExcelMapperException {

		if (ClassUtils.isPrimitiveOrWrapper(field.getType())
				|| field.getType().isArray() || field.getType().isInterface()
				|| String.class == field.getType()
				|| Date.class == field.getType() || field.getType().isEnum()) {
			return;
		} else {
			Object aux = null;
			try {
				aux = field.getType().newInstance();
			} catch (InstantiationException e) {
				throw new ExcelMapperException(
						ExcelMapperException.ERROR_SETTING_VAL, e);
			} catch (IllegalAccessException e) {
				throw new ExcelMapperException(
						ExcelMapperException.ERROR_SETTING_VAL, e);
			}
			setProperty(target, field.getName(), aux);
			Field[] fs = FieldUtils.getAllFields(field.getType());
			for (Field f : fs) {
				populate(aux, f);
			}
		}
	}

	public void setProperty(Object target, String path, Object val)
			throws ExcelMapperException {
		try {
			logger.trace("setting [{}] [{}]",path,val);
			beanUtils.setProperty(target, path, val);
		} catch (IllegalAccessException e) {
			throw new ExcelMapperException(e,
					ExcelMapperException.ERROR_SETTING_VAL,val, path);
		} catch (InvocationTargetException e) {
			throw new ExcelMapperException(e,
					ExcelMapperException.ERROR_SETTING_VAL,val, path);
		}
	}

	public Object getProperty(Object target, String path)
			throws ExcelMapperException {
		try {
			return beanUtils.getPropertyUtils().getProperty(target, path);
		} catch (IllegalAccessException e) {
			throw new ExcelMapperException(e,
					ExcelMapperException.ERROR_GETTING_VAL, path);
		} catch (InvocationTargetException e) {
			throw new ExcelMapperException(e,
					ExcelMapperException.ERROR_GETTING_VAL, path);
		} catch (NoSuchMethodException e) {
			throw new ExcelMapperException(e,
					ExcelMapperException.ERROR_GETTING_VAL, path);
		}
	}
	
	public void overrideBeanUtilsConfig(BeanUtilsBean bu)
	{
		if(bu == null)
		{
			throw new IllegalArgumentException();
		}
		beanUtils = bu;
	}
}
