package db.mapper;

import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.List;

import db.DatabaseTable;
import db.DatabaseUtils;
import db.annotation.DbField;
import db.annotation.DbLink;
import db.annotation.DbTable;

public class DatabaseMapper {
	
	public static <T> ResultSetMapper<T> objectMapper(DbTable dbTable) {
		return new ResultSetMapper<T>() {	
			@Override
			public T map(ResultSet rs) {
				try {
					T obj = (T) dbTable.entity().getConstructor().newInstance();
					rs.next();
					for(Field field : dbTable.entity().getFields()) {
						if(!field.isAnnotationPresent(DbField.class)) {
							continue;
						}
						// Recup valeur dans le resultat
						Object value = rs.getObject(field.getAnnotation(DbField.class).value());
						// Si lien avec une autre entité
						String link = DatabaseUtils.getDbLink(field);
						if(value != null && link != null) {
							DatabaseUtils.checkIfPersistable(field);
							Long otherId = (Long) value;
							if(otherId == 0) {
								value = null;
							} else {
								String[] split = field.getType().getName().split("\\.");
								String className = split[split.length - 1];
								// Chargement de la class Table qui gère l'entité
								Class<?> clazz = Class.forName(link + "." + className + "Table");
								DatabaseTable table = (DatabaseTable) clazz.getMethod("getInstance").invoke(null);
								value = table.getById(otherId);
							}
						}
						field.set(obj, value);
					}
					return obj;
				} catch (InstantiationException | IllegalAccessException | IllegalArgumentException | InvocationTargetException | NoSuchMethodException | SecurityException | SQLException | ClassNotFoundException e) {
					throw new IllegalStateException("Unable to map value to the entity");
				}
				
			}
		};
	}
	
	public static <T> ResultSetMapper<List<T>> listMapper(DbTable dbTable) {
		return new ResultSetMapper<List<T>>() {	
			@Override
			public List<T> map(ResultSet rs) {
				try {
					List<T> list = new ArrayList<>();
					while(rs.next()) {
						T obj = (T) dbTable.entity().getConstructor().newInstance();
						for(Field field : dbTable.entity().getFields()) {
							if(!field.isAnnotationPresent(DbField.class)) {
								continue;
							}
							// Recup valeur dans le resultat
							Object value = rs.getObject(field.getAnnotation(DbField.class).value());
							// Si lien avec une autre entité
							String link = DatabaseUtils.getDbLink(field);
							if(value != null && link != null) {
								DatabaseUtils.checkIfPersistable(field);
								Long otherId = (Long) value;
								if(otherId == 0) {
									value = null;
								} else {
									String[] split = field.getType().getName().split("\\.");
									String className = split[split.length - 1];
									// Chargement de la class Table qui gère l'entité
									Class<?> clazz = Class.forName(link + "." + className + "Table");
									DatabaseTable table = (DatabaseTable) clazz.getMethod("getInstance").invoke(null);
									value = table.getById(otherId);
								}
							}
							field.set(obj, value);
						}
						list.add(obj);
					}
					return list;
				} catch (InstantiationException | IllegalAccessException | IllegalArgumentException | InvocationTargetException | NoSuchMethodException | SecurityException | SQLException | ClassNotFoundException e) {
					throw new IllegalStateException("Unable to map value to the entity");
				}
			}
		};
	}

}
