import java.lang.reflect.Type;
import java.lang.reflect.Field;
import java.lang.reflect.Modifier;
import java.lang.reflect.TypeVariable;
import java.util.Map;
import java.util.Hashtable;
import java.util.List;
import java.util.LinkedList;

import codeassess.javassessment.Assessor;
import codeassess.misc.Misc;

public class Template extends Assessor
{
	public Map<String, String> getConfig()
	{
		final Map<String, String> config = new Hashtable<String, String>();

		config.put("Language", "Java");
		config.put("Extensions", ".java");
		config.put("AssessmentMode", "SemiAutomatic");

		return config;
	}
	public List<Object[]> getProperties()
	{
		final List<Object[]> properties = new LinkedList<Object[]>();

        // Posicion
		properties.add(new Object[]{ "ExisteInterfazPosicion", "Functionality", "Posicion.java", "Comprueba que se llama \"Posicion\"", "Posicion: Debe existir la interfaz Posicion", 0.0 });				
		properties.add(new Object[]{ "PosicionObtenerCoordenadaXMethod", "Functionality", "Posicion.java", "Comprueba que existe el método", "Posicion: Crear el método obtenerCoordenadaX de la interfaz Posicion", 0.5 });
		properties.add(new Object[]{ "PosicionObtenerCoordenadaYMethod", "Functionality", "Posicion.java", "Comprueba que existe el método", "Posicion: Crear el método obtenerCoordenadaY de la interfaz Posicion", 0.5 });				

        // Figura 
		properties.add(new Object[]{ "FiguraImplementsPosicion", "Functionality", "Figura.java", "Debes usar \"implements\"", "Figura: Debe implementar la interfaz Posicion", 0.0 });        
		properties.add(new Object[]{ "ExisteClaseFigura", "Functionality", "Figura.java", "Debes usar \"abstract\"", "Figura: Debe existir la clase abstracta Figura", 0.0 });         

        // Rectangulo
		properties.add(new Object[]{ "ExisteClaseRectangulo", "Functionality", "Rectangulo.java", "Comprueba que se llama \"Rectangulo\"", "Rectangulo: Debe existir la clase Rectangulo", 0.0 });
		properties.add(new Object[]{ "RectanguloExtendsFigura", "Functionality", "Rectangulo.java", "Debes usar \"extends\"", "Rectangulo: Debe heredar de la clase Figura", 0.0 });
		properties.add(new Object[]{ "RectanguloNoImplementaInterfaces", "Functionality", "Rectangulo.java", "No debes usar \"implements\"", "Rectangulo: NO debe implementar ninguna interfaz", 0.0 });
		        
        // Circulo
		properties.add(new Object[]{ "ExisteClaseCirculo", "Functionality", "Circulo.java", "Comprueba que se llama \"Circulo\"", "Circulo: Debe existir la clase Circulo", 0.0 });
		properties.add(new Object[]{ "CirculoExtendsFigura", "Functionality", "Circulo.java", "Debes usar \"extends\"", "Circulo: Debe heredar de la clase Figura", 0.0 });
		properties.add(new Object[]{ "CirculoNoImplementaInterfaces", "Functionality", "Circulo.java", "No debes usar \"implements\"", "Circulo: NO debe implementar ninguna interfaz", 0.0 });
        
        // Vehiculo
		properties.add(new Object[]{ "ExisteClaseVehiculo", "Functionality", "Vehiculo.java", "Debes usar \"abstract\"", "Vehiculo: Debe existir la clase abstracta Vehiculo", 0.5 });
		properties.add(new Object[]{ "VehiculoImplementsPosicion", "Functionality", "Vehiculo.java", "Debes usar \"implements\"", "Vehiculo: Debe implementar la interfaz Posicion", 0.5 });	
		properties.add(new Object[]{ "VehiculoXYFields", "Functionality", "Vehiculo.java", "Comprueba que solo hay dos atributos de tipo double", "Vehiculo: Tiene (solo) dos atributos de tipo double", 1.0 });
		properties.add(new Object[]{ "VehiculoConstructor", "Functionality", "Vehiculo.java", "Comprueba los argumentos del constructor", "Vehiculo: Debe tener un constructor con dos argumentos de tipo double", 1.0 });		
		properties.add(new Object[]{ "VehiculoObtenerCoordenadaXMethod", "Functionality", "Vehiculo.java", "Comprueba que existe el método", "Vehiculo: Crear el método ObtenerCoordenadaXMethod de la clase Vehiculo", 0.5 });
		properties.add(new Object[]{ "VehiculoObtenerCoordenadaYMethod", "Functionality", "Vehiculo.java", "Comprueba que existe el método", "Vehiculo: Crear el método ObtenerCoordenadaYMethod de la clase Vehiculo", 0.5 });		
		        
		// Coche
		properties.add(new Object[]{ "ExisteClaseCoche", "Functionality", "Coche.java", "Comprueba que se llama \"Coche\"", "Coche: Debe existir la clase Coche", 0.0 });
		properties.add(new Object[]{ "CocheExtendsVehiculo", "Functionality", "Coche.java", "Debes usar \"extends\"", "Coche: Debe heredar de la clase Vehiculo", 0.0 });
		properties.add(new Object[]{ "CocheNoImplementaInterfaces", "Functionality", "Coche.java", "No debes usar \"implements\"", "Coche: NO debe implementar ninguna interfaz", 0.0 });		
		properties.add(new Object[]{ "CocheNoImplementaObtenerCoordenadaXMethod", "Functionality", "Coche.java", "Los métodos se heredan", "Coche: NO crear los métodos ObtenerCoordenadaX / ObtenerCoordenadaY en la clase Coche", 0.0 });		
		properties.add(new Object[]{ "CocheConstructor", "Functionality", "Coche.java", "Comprueba los argumentos del constructor", "Coche: Crear el constructor de la clase Coche", 0.0 });
		
        // Comparador
		properties.add(new Object[]{ "ExisteClaseComparador", "Functionality", "Comparador.java", "Comprueba que se llama \"Comparador\"", "Comparador: Debe existir la clase Comparador", 0.0 });
		properties.add(new Object[]{ "ComparadorGenericidad", "Functionality", "Comparador.java", "Debes usar \"<…>\"", "Comparador: Es una clase genérica (con una única variable de tipo)", 1.0 });
		properties.add(new Object[]{ "ComparadorGenericidadLimitada", "Functionality", "Comparador.java", "Debes usar \"extends\"", "Comparador: La variable de tipo de Comparador hereda de Posicion", 1.0 });
		properties.add(new Object[]{ "ComparadorTFields", "Functionality", "Comparador.java", "Comprueba que defines exactamente dos atributos de tipo T", "Comparador: Tiene (sólo) dos atributos de tipo T", 1.0 });
		properties.add(new Object[]{ "ComparadorConstructor", "Functionality", "Comparador.java", "Comprueba los argumentos del constructor", "Comparador: Implementar el constructor de Comparador", 1.0 });
		properties.add(new Object[]{ "ComparadorCompararPosicionesMethod", "Functionality", "Comparador.java", "Comprueba el método \"compararPosiciones\"", "Comparador: Debe tener un método CompararPosiciones", 1.0 });		
                
		return properties;
	}

    // Existen las clases y las interfaces

	// Existe la clase abstracta Figura
	public boolean checkExisteClaseFigura()
	{	
		if (!(super.introspector.checkClassExists("Figura"))) return false;

		final Class<?> figuraClass = super.introspector.getClass("Figura");
		return Modifier.isAbstract( figuraClass.getModifiers() );			
	}
	public boolean solveExisteClaseFigura()
	{
		return false;
	}
	public boolean testExisteClaseFigura()
	{
		return true;
	}

	// Existe la clase Rectangulo
	public boolean checkExisteClaseRectangulo()
	{
		return super.introspector.checkClassExists("Rectangulo");
	}
	public boolean solveExisteClaseRectangulo()
	{
		return false;
	}
	public boolean testExisteClaseRectangulo()
	{
		return true;
	}

	// Existe la clase Circulo
	public boolean checkExisteClaseCirculo()
	{
		return super.introspector.checkClassExists("Circulo");
	}
	public boolean solveExisteClaseCirculo()
	{
		return false;
	}
	public boolean testExisteClaseCirculo()
	{
		return true;
	}

	// Existe la interfaz Posicion
	public boolean checkExisteInterfazPosicion()
	{
		return super.introspector.checkClassExists("Posicion");
	}
	public boolean solveExisteInterfazPosicion()
	{
		return false;
	}
	public boolean testExisteInterfazPosicion()
	{
		return true;
	}

	// Existe la clase abstract Vehiculo
	public boolean checkExisteClaseVehiculo()
	{
		if (!(super.introspector.checkClassExists("Vehiculo"))) return false;

		final Class<?> vehiculoClass = super.introspector.getClass("Vehiculo");
		return Modifier.isAbstract( vehiculoClass.getModifiers() );	
	}
	public boolean solveExisteClaseVehiculo()
	{
		return false;
	}
	public boolean testExisteClaseVehiculo()
	{
		return true;
	}

	// Existe la clase Coche
	public boolean checkExisteClaseCoche()
	{
		return super.introspector.checkClassExists("Coche");
	}
	public boolean solveExisteClaseCoche()
	{
		return false;
	}
	public boolean testExisteClaseCoche()
	{
		return true;
	}

	// Existe la clase Comparador
	public boolean checkExisteClaseComparador()
	{
		return super.introspector.checkClassExists("Comparador");
	}
	public boolean solveExisteClaseComparador()
	{
		return false;
	}
	public boolean testExisteClaseComparador()
	{
		return true;
	}

    // La herencia es correcta

	// Circulo extends Figura
	public boolean checkCirculoExtendsFigura()
	{
		final Class<?> circuloClass = super.introspector.getClass("Circulo");
		final Class<?> figuraClass = super.introspector.getClass("Figura");
		if (circuloClass == null || figuraClass == null)
			return false;

		return super.introspector.checkSuperclass(circuloClass, figuraClass);
	}
	public boolean solveCirculoExtendsFigura()
	{
		return false;
	}
	public boolean testCirculoExtendsFigura()
	{
		return true;
	}

	// Rectangulo extends Figura
	public boolean checkRectanguloExtendsFigura()
	{
		final Class<?> rectanguloClass = super.introspector.getClass("Rectangulo");
		final Class<?> figuraClass = super.introspector.getClass("Figura");
		if (rectanguloClass == null || figuraClass == null)
			return false;

		return super.introspector.checkSuperclass(rectanguloClass, figuraClass);
	}
	public boolean solveRectanguloExtendsFigura()
	{
		return false;
	}
	public boolean testRectanguloExtendsFigura()
	{
		return true;
	}

	// Coche extends Vehiculo
	public boolean checkCocheExtendsVehiculo()
	{
		final Class<?> cocheClass = super.introspector.getClass("Coche");
		final Class<?> vehiculoClass = super.introspector.getClass("Vehiculo");
		if (cocheClass == null || vehiculoClass == null)
			return false;

		return super.introspector.checkSuperclass(cocheClass, vehiculoClass);
	}
	public boolean solveCocheExtendsVehiculo()
	{
		return false;
	}
	public boolean testCocheExtendsVehiculo()
	{
		return true;
	}

    // Se implementan las interfaces que deben implementarse
	
	// Figura implements Posicion
	public boolean checkFiguraImplementsPosicion()
	{
		final Class<?> figuraClass = super.introspector.getClass("Figura");
		final Class<?> posicionClass = super.introspector.getClass("Posicion");
		if (figuraClass == null || posicionClass == null)
			return false;

		return super.introspector.getInterfaceCount(figuraClass) == 1 && super.introspector.checkImplements(figuraClass, posicionClass);
	}
	public boolean solveFiguraImplementsPosicion()
	{
		return false;
	}
	public boolean testFiguraImplementsPosicion()
	{
		return true;
	}

	// Vehiculo implements Posicion
	public boolean checkVehiculoImplementsPosicion()
	{
		final Class<?> vehiculoClass = super.introspector.getClass("Vehiculo");
		final Class<?> posicionClass = super.introspector.getClass("Posicion");
		if (vehiculoClass == null || posicionClass == null)
			return false;

		return super.introspector.getInterfaceCount(vehiculoClass) == 1 && super.introspector.checkImplements(vehiculoClass, posicionClass);
	}	
	public boolean solveVehiculoImplementsPosicion()
	{
		return false;
	}
	public boolean testVehiculoImplementsPosicion()
	{
		return true;
	}

	// Circulo does NOT implement any interface
	public boolean checkCirculoNoImplementaInterfaces()
	{
		final Class<?> circuloClass = super.introspector.getClass("Circulo");
		if (circuloClass == null)
			return false;

		return super.introspector.getInterfaceCount(circuloClass) == 0;
	}
	public boolean solveCirculoNoImplementaInterfaces()
	{
		return false;
	}
	public boolean testCirculoNoImplementaInterfaces()
	{
		return true;
	}

	// Rectangulo does NOT implement any interface
	public boolean checkRectanguloNoImplementaInterfaces()
	{
		final Class<?> trianguloClass = super.introspector.getClass("Rectangulo");
		if (trianguloClass == null)
			return false;

		return super.introspector.getInterfaceCount(trianguloClass) == 0;
	}
	public boolean solveRectanguloNoImplementaInterfaces()
	{
		return false;
	}
	public boolean testRectanguloNoImplementaInterfaces()
	{
		return true;
	}

	// Coche does NOT implement any interface
	public boolean checkCocheNoImplementaInterfaces()
	{
		final Class<?> cocheClass = super.introspector.getClass("Coche");
		if (cocheClass == null)
			return false;

		return super.introspector.getInterfaceCount(cocheClass) == 0;
	}
	public boolean solveCocheNoImplementaInterfaces()
	{
		return false;
	}
	public boolean testCocheNoImplementaInterfaces()
	{
		return true;
	}

    // Los atributos y métodos de las clases son los correctos
	
	// Posicion implementa método obtenerCoordenadaX 		
	public boolean checkPosicionObtenerCoordenadaXMethod()
	{
		final Class<?> posicionClass = super.introspector.getClass("Posicion");
		if (posicionClass == null)
			return false;

		final Class<?>[] paramTypesX = { };
		
		return super.introspector.checkDeclaredMethod(posicionClass, "obtenerCoordenadaX", paramTypesX, double.class, true);
	}
	public boolean solvePosicionObtenerCoordenadaXMethod()
	{
		return false;
	}
	public boolean testPosicionObtenerCoordenadaXMethod()
	{
		return true;
	}

	// Posicion implementa método obtenerCoordenadaY 		
	public boolean checkPosicionObtenerCoordenadaYMethod()
	{
		final Class<?> posicionClass = super.introspector.getClass("Posicion");
		if (posicionClass == null)
			return false;

		final Class<?>[] paramTypesX = { };
		
		return super.introspector.checkDeclaredMethod(posicionClass, "obtenerCoordenadaY", paramTypesX, double.class, true);
	}
	public boolean solvePosicionObtenerCoordenadaYMethod()
	{
		return false;
	}
	public boolean testPosicionObtenerCoordenadaYMethod()
	{
		return true;
	}

	// COCHE NO implementa los métodos ObtenerCoordenadaX / ObtenerCoordenadaY 			
	public boolean checkCocheNoImplementaObtenerCoordenadaXMethod()
	{
		final Class<?> cocheClass = super.introspector.getClass("Coche");
		if (cocheClass == null)
			return false;

		final Class<?>[] paramTypes = { };

		return (!(super.introspector.checkDeclaredMethod(cocheClass, "obtenerCoordenadaX", paramTypes, double.class, true))) &&
			   (!(super.introspector.checkDeclaredMethod(cocheClass, "obtenerCoordenadaY", paramTypes, double.class, true)));
	}
	public boolean solveCocheNoImplementaObtenerCoordenadaXMethod()
	{
		return false;
	}
	public boolean testCocheNoImplementaObtenerCoordenadaXMethod()
	{
		return true;
	}

	// Coche constructor
	public boolean checkCocheConstructor()
	{
		final Class<?> vehiculoClass = super.introspector.getClass("Vehiculo");
		if (vehiculoClass == null)
			return false;

		final Class<?>[] paramTypesConstructor1 = { double.class, double.class };

		return super.introspector.checkDeclaredConstructor(vehiculoClass, paramTypesConstructor1, true);
	}
	public boolean solveCocheConstructor()
	{
		return false;
	}
	public boolean testCocheConstructor()
	{
		final String testCaseName = "TestCocheConstructor";
		final String testMethodName = "test";
		final Boolean expectedResult = true;
		final double posX = 12.3;
		final double posY = 56.6;
		final String marca = "Nissan";
		
		final Object[] args = { posX, posY, marca };
		final Object testCaseResult = super.tester.executeTestCase(testCaseName, testMethodName, args);

		return expectedResult.equals(testCaseResult);
	}	
	
	// Vehiculo tiene (sólo) dos atributos de tipo Double
	public boolean checkVehiculoXYFields()
	{
		final Class<?> vehiculoClass = super.introspector.getClass("Vehiculo");
		if (vehiculoClass == null)
			return false;

		final Field[] vehiculoFields = super.introspector.getDeclaredFields(vehiculoClass);
		if (vehiculoFields.length != 2)
			return false;

		final Field vehiculoField1 = vehiculoFields[0];
		final Field vehiculoField2 = vehiculoFields[1];
		
		return super.introspector.checkClass(vehiculoField1, double.class, true) &&
			   super.introspector.checkClass(vehiculoField2, double.class, true);
	}	
	public boolean solveVehiculoXYFields()
	{
		return false;
	}
	public boolean testVehiculoXYFields()
	{
		return true;
	}

	// Implementar el constructor de Vehiculo
	public boolean checkVehiculoConstructor()
	{
		final Class<?> vehiculoClass = super.introspector.getClass("Vehiculo");
		if (vehiculoClass == null) return false;

		final Class<?>[] paramTypesConstructor = { double.class, double.class };

		return super.introspector.checkDeclaredConstructor(vehiculoClass, paramTypesConstructor, true);
	}
	public boolean solveVehiculoConstructor()
	{
		return false;
	}
	public boolean testVehiculoConstructor()
	{
		return true;
	}

	// Crear el método ObtenerCoordenadaXMethodX de la clase Vehiculo		
	public boolean checkVehiculoObtenerCoordenadaXMethod()
	{
		final Class<?> vehiculoClass = super.introspector.getClass("Vehiculo");
		if (vehiculoClass == null)
			return false;

		final Class<?>[] paramTypesX = { };
		
		return super.introspector.checkDeclaredMethod(vehiculoClass, "obtenerCoordenadaX", paramTypesX, double.class, true);
	}
	public boolean solveVehiculoObtenerCoordenadaXMethod()
	{
		return false;
	}
	public boolean testVehiculoObtenerCoordenadaXMethod()
	{
		final String testCaseName = "TestVehiculoObtenerCoordenadaXMethod";
		final String testMethodName = "test";
		final double posX = 12.3;
		final double posY = 56.6;
		final Double expectedResult = posX;
		
		final Object[] args = { posX, posY };
		final Object testCaseResult = super.tester.executeTestCase(testCaseName, testMethodName, args);

		return expectedResult.equals(testCaseResult);
	}	
	
	// Crear el método ObtenerCoordenadaXMethodY de la clase Vehiculo		
	public boolean checkVehiculoObtenerCoordenadaYMethod()
	{
		final Class<?> vehiculoClass = super.introspector.getClass("Vehiculo");
		if (vehiculoClass == null)
			return false;

		final Class<?>[] paramTypesX = { };
		
		return super.introspector.checkDeclaredMethod(vehiculoClass, "obtenerCoordenadaY", paramTypesX, double.class, true);
	}
	public boolean solveVehiculoObtenerCoordenadaYMethod()
	{
		return false;
	}
	public boolean testVehiculoObtenerCoordenadaYMethod()
	{
		final String testCaseName = "TestVehiculoObtenerCoordenadaYMethod";
		final String testMethodName = "test";
		final double posX = 12.3;
		final double posY = 56.6;
		final Double expectedResult = posY;
		
		final Object[] args = { posX, posY };
		final Object testCaseResult = super.tester.executeTestCase(testCaseName, testMethodName, args);

		return expectedResult.equals(testCaseResult);
	}		

	// Comparador es una clase genérica (con una única variable de tipo)
	public boolean checkComparadorGenericidad()
	{
		final Class<?> comparadorClass = super.introspector.getClass("Comparador");
		if (comparadorClass == null)
			return false;

		return super.introspector.getGenericTypeParameterCount(comparadorClass) == 1;
	}
	public boolean solveComparadorGenericidad()
	{
		return false;
	}
	public boolean testComparadorGenericidad()
	{
		return true;
	}

	// La variable de tipo de Comparador hereda de Posicion   	// No esta en la libreria
	public boolean checkComparadorGenericidadLimitada()
	{
		final Class<?> comparadorClass = super.introspector.getClass("Comparador");
		if (comparadorClass == null) return false;

		final TypeVariable<?> genericParameter = super.introspector.getGenericTypeParameters(comparadorClass)[0];
		final Type type = genericParameter.getBounds()[0];
		final String typeName = type.getTypeName();

		return typeName.equals("Posicion");
	}	
	public boolean solveComparadorGenericidadLimitada()
	{
		return false;
	}
	public boolean testComparadorGenericidadLimitada()
	{
		return true;
	}

	// Comparador tiene (sólo) dos atributos de tipo T	
	public boolean checkComparadorTFields()
	{
		final Class<?> comparadorClass = super.introspector.getClass("Comparador");
		if (comparadorClass == null)
			return false;
		final TypeVariable<?> classGenericType = super.introspector.getGenericTypeParameters(comparadorClass)[0];

		final Field[] fields = super.introspector.getDeclaredFields(comparadorClass);
		boolean cumple = true;
		for (Field field : fields)
		{
			final Type fieldType = super.introspector.getType(field);
			final TypeVariable<?> fieldGenericType = super.introspector.getGenericType(fieldType, new int[] { });
			if (fieldGenericType == null)
			{
				cumple = false;
				break;
			}
			if (!super.introspector.checkSameGenericTypes(classGenericType, fieldGenericType))
			{
				cumple = false;
				break;
			}
		}

		return cumple;
	}	
	public boolean solveComparadorTFields()
	{
		return false;
	}
	public boolean testComparadorTFields()
	{
		return true;
	}

	// Implementar el constructor de Comparador con dos variables genéricas T como parámetros
	public boolean checkComparadorConstructor()
	{
		final Class<?> comparadorClass = super.introspector.getClass("Comparador");
		if (comparadorClass == null) return false;

		final Class<?> posicionClass = super.introspector.getClass("Posicion");
		final Class<?>[] paramTypesConstructor = { posicionClass, posicionClass };

		return super.introspector.checkDeclaredConstructor(comparadorClass, paramTypesConstructor, true);
	}
	public boolean solveComparadorConstructor()
	{
		return false;
	}
	public boolean testComparadorConstructor()
	{
		final String testCaseName = "TestComparadorConstructor";
		final String testMethodName = "test";		
		final Boolean expectedResult = true;

		final Object[] args = { };
		final Object testCaseResult = super.tester.executeTestCase(testCaseName, testMethodName, args);
 
		return expectedResult.equals(testCaseResult);
	}	
	
	// Comparador debe tener un método CompararPosiciones	
	public boolean checkComparadorCompararPosicionesMethod()
	{
		final Class<?> comparadorClass = super.introspector.getClass("Comparador");
		if (comparadorClass == null)
			return false;

		final Class<?>[] paramTypesX = { };
		
		return super.introspector.checkDeclaredMethod(comparadorClass, "compararPosiciones", paramTypesX, boolean.class, true);
	}
	public boolean solveComparadorCompararPosicionesMethod()
	{
		return false;
	}
	public boolean testComparadorCompararPosicionesMethod()
	{
		final String testCaseName = "TestComparadorCompararPosicionesMethod";
		final String testMethodName = "test";
		final Boolean expectedResult = true;
		
		final Object[] args = { };
		final Object testCaseResult = super.tester.executeTestCase(testCaseName, testMethodName, args);

		return expectedResult.equals(testCaseResult);
	}	
		
}