package net.bounceme.chronos.utils.assemblers;

import java.lang.reflect.Array;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import net.bounceme.chronos.utils.common.Constantes;
import net.bounceme.chronos.utils.exceptions.AssembleException;

/**
 * Clase genérica que implementa el comportamiento de Assembler. Sigue el patrón
 * de diseño template-method.
 * 
 * @author frederik
 *
 * @param <SOURCE>
 * @param <TARGET>
 */
public abstract class GenericAssembler<SOURCE, TARGET> implements Assembler<SOURCE, TARGET> {
    
	protected Logger logger = LoggerFactory.getLogger(GenericAssembler.class);
    
    private Class<SOURCE> classSource;
    private Class<TARGET> classTarget;
    
    public GenericAssembler(Class<SOURCE> classSource, Class<TARGET> classTarget) {
        this.classSource = classSource;
        this.classTarget = classTarget;
    }
    
    /**
     * @return
     * @throws AssembleException
     */
    protected SOURCE newSourceInstance() {
        try {
			return (classSource!=null) ? (SOURCE) classSource.newInstance() : null;
		} catch (InstantiationException | IllegalAccessException e) {
			return null;
		}
    }
    
    /**
     * @return
     * @throws AssembleException
     */
    protected TARGET newTargetInstance() {
        try {
            return (classTarget!=null) ? (TARGET) classTarget.newInstance() : null;
        } catch (InstantiationException | IllegalAccessException e) {
        	logger.error(e.getMessage());
            return null;
        }
    }
    
    /**
     * @return
     */
    protected Class<SOURCE> getClassSource() {
        return classSource;
    }
    
    /**
     * @return
     */
    protected Class<TARGET> getClassTarget() {
        return classTarget;
    }
    
	/* (non-Javadoc)
	 * @see net.bounceme.chronos.utils.assemblers.Assembler#assemble(java.util.Collection)
	 */
	public final Collection<TARGET> assemble(Collection<SOURCE> source) {
		if (source == null || source.isEmpty()) {
			return new ArrayList<TARGET>(0);
		}
		List<TARGET> items = new ArrayList<TARGET>(source.size());
		for (SOURCE s : source) {
			items.add(assemble(s));
		}
		return items;
	}
	
	/* (non-Javadoc)
	 * @see net.bounceme.chronos.utils.assemblers.Assembler#assemble(java.util.Collection)
	 */
	@SuppressWarnings(Constantes.UNCHECKED)
	public final TARGET[] assemble(SOURCE[] source) {
		if (source == null || source.length == 0) {
			return null;
		}
		
		TARGET[] items = (TARGET[]) Array.newInstance(classTarget, source.length);
		int index = 0;
		for (SOURCE s : source) {
			items[index] = assemble(s);
			index++;
		}
		return items;
	}
}
