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

import java.io.IOException;
import java.io.InputStream;
import java.util.ArrayList;
import java.util.Date;
import java.util.HashMap;
import java.util.List;

import javax.annotation.PostConstruct;

import org.apache.commons.beanutils.BeanUtilsBean;
import org.apache.commons.beanutils.converters.DateConverter;
import org.codehaus.jackson.JsonParseException;
import org.codehaus.jackson.map.JsonMappingException;
import org.codehaus.jackson.map.ObjectMapper;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationContext;
import org.springframework.core.io.Resource;

import com.mx.dla.dda.excelMapper.bos.BeanPopulator;
import com.mx.dla.dda.excelMapper.bos.ExcelMapperValidator;
import com.mx.dla.dda.excelMapper.dtos.ExcelMapper;
import com.mx.dla.dda.excelMapper.dtos.ExcelRule;
import com.mx.dla.dda.excelMapper.exceptions.ExcelMapperException;

public class ExcelMapperContext {

	private Logger logger = LoggerFactory.getLogger("excelMapper");

	private HashMap<String, ExcelMapper> mappers;
	private List<String> locations;
	private String dateFormat;

	@Autowired
	private ApplicationContext applicationContext;

	@Autowired
	private ExcelMapperValidator validator;

	@Autowired
	private BeanPopulator BeanPopulator;

	@PostConstruct
	public void initIt() throws ExcelMapperException {
		logger.debug("loading excel context");
		mappers = new HashMap<String, ExcelMapper>();
		ObjectMapper jsonMapper = new ObjectMapper();

		for (String path : locations) {
			try {
				ExcelMapper p = jsonMapper.readValue(getInputStreamFile(path),
						ExcelMapper.class);
				validator.validateMapper(p);
				if (p.getId() == null || "".equals(p.getId())) {
					p.setId(p.getClassName());
				}
				p = enrichMapperParent(p);
				logger.debug("Adding [{}] file[{}] to excelContext",p.getId(),path);
				mappers.put(p.getId(), p);
			} catch (ExcelMapperException e) {
				logger.error("Errores [{}]",e.getSErrors());
				throw new ExcelMapperException(
						ExcelMapperException.ERROR_READING_FILE + path,
						e.getErrors());
			} catch (JsonParseException e) {
				logger.error("Managed Error ", e);
				throw new ExcelMapperException(
						ExcelMapperException.ERROR_READING_FILE + path, e);
			} catch (JsonMappingException e) {
				logger.error("Managed Error ", e);
				throw new ExcelMapperException(
						ExcelMapperException.ERROR_READING_FILE + path, e);
			} catch (IOException e) {
				logger.error("Managed Error ", e);
				throw new ExcelMapperException(
						ExcelMapperException.ERROR_READING_FILE + path, e);
			}
		}
	}

	private InputStream getInputStreamFile(String path) throws IOException {
		Resource resource = applicationContext.getResource("classpath:" + path);
		return resource.getInputStream();

	}

	private ExcelMapper enrichMapperParent(ExcelMapper excelMapper)
			throws ExcelMapperException {
		List<ExcelRule> rules = new ArrayList<ExcelRule>();
		rules.addAll(0, excelMapper.getRules());
		if (excelMapper.getParent() != null) {
			ExcelMapper p = mappers.get(excelMapper.getParent());
			if (p == null) {
				List<String> errors = new ArrayList<String>();
				errors.add(ExcelMapperException.ERROR_PARENT + excelMapper.getParent());
				throw new ExcelMapperException(
						ExcelMapperException.ERROR_PARENT + excelMapper.getParent(),
						errors);
			} else {
				rules.addAll(0, p.getRules());
			}
		}
		excelMapper.setRules(rules);
		return excelMapper;
	}

	public<T> ExcelMapper getExcelMapper(Class<T> c) {
		logger.debug("getmappers {}", c);

		List<ExcelMapper> ms = new ArrayList<ExcelMapper>(mappers.values());
		List<ExcelMapper> cfg = new ArrayList<ExcelMapper>();
		for (ExcelMapper m : ms) {
			if (m.getClassName().equals(c.getName())) {
				cfg.add(m);
			}
		}
		if (cfg.isEmpty()) {
			throw new IllegalArgumentException("No configuration for "
					+ c.getName());
		}
		if (cfg.size() != 1) {
			throw new IllegalArgumentException(
					ExcelMapperException.ERROR_MULTIPLE_MAPPER);
		}
		return cfg.get(0);
	}

	public ExcelMapper getExcelMapper(String id) {
		logger.debug("getmappers {}", id);
		ExcelMapper cfg = mappers.get(id);
		if (cfg == null) {
			logger.error("No se encontro el mapeo con id [{}]",id);
			throw new IllegalArgumentException("No configuration for " + id);
		}
		return cfg;
	}

	public void setLocations(List<String> locations) {
		this.locations = locations;
	}
	
	public void setDateFormat(String dateFormat) {
		this.dateFormat = dateFormat;
	}
	
	public String getDateFormat() {
		return dateFormat;
	}
 
	/**
	 *Se configura parse especial para asignar fechas 
	 */
	public void prepareBeanUtils() {
		dateFormat = (dateFormat== null) ? "MM/dd/yyyy":dateFormat;
		BeanUtilsBean beanUtils = BeanUtilsBean.getInstance();
		Date defaultValue = null;
		DateConverter converter = new DateConverter(defaultValue);
		converter.setPattern(dateFormat);
		beanUtils.getConvertUtils().register(converter, Date.class);
		BeanPopulator.overrideBeanUtilsConfig(beanUtils);
	}

	public BeanPopulator getBeanPopulator() {
		return BeanPopulator;
	};
}
