import japa.parser.ast.body.ModifierSet;

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

import java.lang.reflect.*;
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[]>();
         
         // Figura
         
		properties.add(new Object[]{ "ExisteClaseFigura", "Functionality", "Figura.java", "Debes usar \"abstract\"", "Figura: Debe existir la clase abstracta Figura", 0.0 });         
         
         // Triangulo

		properties.add(new Object[]{ "ExisteClaseTriangulo", "Functionality", "Triangulo.java", "Comprueba que la clase se llama \"Triangulo\"", "Triangulo: Debe existir la clase Triangulo", 0.0 });
		properties.add(new Object[]{ "TrianguloExtendsFigura", "Functionality", "Triangulo.java", "Debes usar \"extends\"", "Triangulo: Triangulo debe heredar de la clase Figura", 0.0 });         
         // Circulo

		properties.add(new Object[]{ "ExisteClaseCirculo", "Functionality", "Circulo.java", "Comprueba que la clase se llama \"Circulo\"", "Circulo: Debe existir la clase Circulo", 0.0 });
		properties.add(new Object[]{ "CirculoExtendsFigura", "Functionality", "Circulo.java", "Debes usar \"extends\"", "Circulo: Circulo debe heredar de la clase Figura", 0.0 });
         
         // Volumen

		properties.add(new Object[]{ "ExisteInterfazVolumen", "Functionality", "Volumen.java", "Comprueba que la interfaz se llama \"Volumen\"", "Volumen: Debe existir la interfaz Volumen", 0.0 });				
         
         // Cilindro

		properties.add(new Object[]{ "ExisteClaseCilindro", "Functionality", "Cilindro.java", "Comprueba que la clase se llama \"Cilindro\"", "Cilindro: Debe existir la clase Cilindro", 0.0 });
		properties.add(new Object[]{ "CilindroExtendsCirculo", "Functionality", "Cilindro.java", "Debes usar \"extends\"", "Cilindro: Cilindro debe heredar de la clase Circulo", 0.0 });
		properties.add(new Object[]{ "CilindroImplementsVolumen", "Functionality", "Cilindro.java", "Debes usar \"implements\"", "Cilindro: Cilindro debe implementar la interfaz Volumen", 0.0 });
         
         // GrupoFiguras

		properties.add(new Object[]{ "ExisteClaseGrupoFiguras", "Functionality", "GrupoFiguras.java", "Comprueba que la clase se llama \"GrupoFiguras\"", "GrupoFiguras: Debe existir la clase GrupoFiguras", 0.0 });
         
         // Poliedro

		properties.add(new Object[]{ "ExisteClasePoliedro", "Functionality", "Poliedro.java", "Debes usar \"abstract\"", "Poliedro: Debe existir la clase abstracta Poliedro", 1.0 });
		properties.add(new Object[]{ "PoliedroExtendsFigura", "Functionality", "Poliedro.java", "Debes usar \"extends\"", "Poliedro: Poliedro debe heredar de la clase Figura", 1.0 });
		properties.add(new Object[]{ "PoliedroImplementsVolumen", "Functionality", "Poliedro.java", "Debes usar \"implements\"", "Poliedro: Poliedro debe implementar la interfaz Volumen", 1.0 });
		properties.add(new Object[]{ "PoliedroAristaField", "Functionality", "Poliedro.java", "Comprueba que el atributo es de tipo double", "Poliedro: Debe utilizar un único atributo (double) para la longitud de la arista", 1.0 });
		properties.add(new Object[]{ "PoliedroConstructor", "Functionality", "Poliedro.java", "Comprueba los argumentos del constructor", "Poliedro: Crear el constructor de la clase Poliedro (double, double, double)", 1.0 });
		properties.add(new Object[]{ "PoliedroSoyPoliedroMethod", "Functionality", "Poliedro.java", "Comprueba los argumentos del método", "Poliedro: Crear el método soyPoliedro de la clase Poliedro", 1.0 });
		
         
         // Tetraedro

		properties.add(new Object[]{ "ExisteClaseTetraedro", "Functionality", "Tetraedro.java", "Comprueba que la clase se llama \"Tetraedro\"", "Tetraedro: Debe existir la clase Tetraedro", 0.0 });
		properties.add(new Object[]{ "TetraedroExtendsPoliedro", "Functionality", "Tetraedro.java", "Debes usar \"extends\"", "Tetraedro: Tetraedro debe heredar de la clase Poliedro", 0.5 });
		properties.add(new Object[]{ "TetraedroSinAtributos", "Functionality", "Tetraedro.java", "Los atributos los hereda", "Tetraedro: no debe tener atributos", 0.5 });
		properties.add(new Object[]{ "TetraedroConstructor", "Functionality", "Tetraedro.java", "Comprueba los argumentos del constructor", "Tetraedro: Crear el constructor de la clase Tetraedro (double, double, double)", 1.0 });
		properties.add(new Object[]{ "TetraedroAreaMethod", "Functionality", "Tetraedro.java", "Comprueba los argumentos del método", "Tetraedro: Crear el método area de la clase Tetraedro", 0.0 });        
		properties.add(new Object[]{ "TetraedroVolumenMethod", "Functionality", "Tetraedro.java", "Comprueba los argumentos del método", "Tetraedro: Crear el método volumen de la clase Tetraedro", 0.0 });      
         
         // Hexaedro

		properties.add(new Object[]{ "ExisteClaseHexaedro", "Functionality", "Hexaedro.java", "Comprueba que la clase se llama \"Hexaedro\"", "Hexaedro: Debe existir la clase Hexaedro", 0.0 });
		properties.add(new Object[]{ "HexaedroExtendsPoliedro", "Functionality", "Hexaedro.java", "Debes usar \"extends\"", "Hexaedro: Hexaedro debe heredar de la clase Poliedro", 0.5 });
		properties.add(new Object[]{ "HexaedroSinAtributos", "Functionality", "Hexaedro.java", "Los atributos los hereda", "Hexaedro: no debe tener atributos", 0.5 });
		properties.add(new Object[]{ "HexaedroConstructor", "Functionality", "Hexaedro.java", "Comprueba los argumentos del constructor", "Hexaedro: Crear el constructor de la clase Hexaedro (double, double, double)", 1.0 });
		properties.add(new Object[]{ "HexaedroAreaMethod", "Functionality", "Hexaedro.java", "Comprueba los argumentos del método", "Hexaedro: Crear el método area de la clase Hexaedro", 0.0 });        
		properties.add(new Object[]{ "HexaedroVolumenMethod", "Functionality", "Hexaedro.java", "Comprueba los argumentos del método", "Hexaedro: Crear el método volumen de la clase Hexaedro", 0.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 Triangulo
	public boolean checkExisteClaseTriangulo()
	{
		return super.introspector.checkClassExists("Triangulo");
	}
	public boolean solveExisteClaseTriangulo()
	{
		return false;
	}
	public boolean testExisteClaseTriangulo()
	{
		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 clase Cilindro
	public boolean checkExisteClaseCilindro()
	{
		return super.introspector.checkClassExists("Cilindro");
	}
	public boolean solveExisteClaseCilindro()
	{
		return false;
	}
	public boolean testExisteClaseCilindro()
	{
		return true;
	}

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

		final Class<?> poliedroClass = super.introspector.getClass("Poliedro");
		return Modifier.isAbstract( poliedroClass.getModifiers() );			
	}
	public boolean solveExisteClasePoliedro()
	{
		return false;
	}
	public boolean testExisteClasePoliedro()
	{
		return true;
	}

	// Existe la clase Tetraedro
	public boolean checkExisteClaseTetraedro()
	{
		return super.introspector.checkClassExists("Tetraedro");
	}
	public boolean solveExisteClaseTetraedro()
	{
		return false;
	}
	public boolean testExisteClaseTetraedro()
	{
		return true;
	}

	// Existe la clase Hexaedro
	public boolean checkExisteClaseHexaedro()
	{
		return super.introspector.checkClassExists("Hexaedro");
	}
	public boolean solveExisteClaseHexaedro()
	{
		return false;
	}
	public boolean testExisteClaseHexaedro()
	{
		return true;
	}

	// Existe la clase GrupoFiguras
	public boolean checkExisteClaseGrupoFiguras()
	{
		return super.introspector.checkClassExists("GrupoFiguras");
	}
	public boolean solveExisteClaseGrupoFiguras()
	{
		return false;
	}
	public boolean testExisteClaseGrupoFiguras()
	{
		return true;
	}

	// Existe la interfaz Volumen
	public boolean checkExisteInterfazVolumen()
	{
		return super.introspector.checkClassExists("Volumen");
	}
	public boolean solveExisteInterfazVolumen()
	{
		return false;
	}
	public boolean testExisteInterfazVolumen()
	{
		return true;
	}

	// Existe la interfaz Volumen
	public boolean checkExisteClaseVolumen()
	{
		return super.introspector.checkClassExists("Volumen");
	}
	public boolean solveExisteClaseVolumen()
	{
		return false;
	}
	public boolean testExisteClaseVolumen()
	{
		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;
	}

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

		return super.introspector.checkSuperclass(trianguloClass, figuraClass);
	}
	public boolean solveTrianguloExtendsFigura()
	{
		return false;
	}
	public boolean testTrianguloExtendsFigura()
	{
		return true;
	}

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

		return super.introspector.checkSuperclass(cilindroClass, circuloClass);
	}
	public boolean solveCilindroExtendsCirculo()
	{
		return false;
	}
	public boolean testCilindroExtendsCirculo()
	{
		return true;
	}

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

		return super.introspector.checkSuperclass(poliedroClass, figuraClass);
	}
	public boolean solvePoliedroExtendsFigura()
	{
		return false;
	}
	public boolean testPoliedroExtendsFigura()
	{
		return true;
	}

	// Tetraedro extends Figura
	public boolean checkTetraedroExtendsPoliedro()
	{
		final Class<?> tetraedroClass = super.introspector.getClass("Tetraedro");
		final Class<?> poliedroClass = super.introspector.getClass("Poliedro");
		if (poliedroClass == null || tetraedroClass == null)
			return false;

		return super.introspector.checkSuperclass(tetraedroClass, poliedroClass);
	}
	public boolean solveTetraedroExtendsPoliedro()
	{
		return false;
	}
	public boolean testTetraedroExtendsPoliedro()
	{
		return true;
	}

	// Hexaedro extends Figura
	public boolean checkHexaedroExtendsPoliedro()
	{
		final Class<?> hexaedroClass = super.introspector.getClass("Hexaedro");
		final Class<?> poliedroClass = super.introspector.getClass("Poliedro");
		if (poliedroClass == null || hexaedroClass == null)
			return false;

		return super.introspector.checkSuperclass(hexaedroClass, poliedroClass);
	}
	public boolean solveHexaedroExtendsPoliedro()
	{
		return false;
	}
	public boolean testHexaedroExtendsPoliedro()
	{
		return true;
	}

    // Se implementan las interfaces que deben implementarse
		
	// Cilindro implements Volumen
	public boolean checkCilindroImplementsVolumen()
	{
		final Class<?> cilindroClass = super.introspector.getClass("Cilindro");
		final Class<?> volumenClass = super.introspector.getClass("Volumen");
		if (cilindroClass == null || volumenClass == null)
			return false;

		return super.introspector.getInterfaceCount(cilindroClass) == 1 && super.introspector.checkImplements(cilindroClass, volumenClass);
	}
	public boolean solveCilindroImplementsVolumen()
	{
		return false;
	}
	public boolean testCilindroImplementsVolumen()
	{
		return true;
	}

	// Poliedro implements Volumen
	public boolean checkPoliedroImplementsVolumen()
	{
		final Class<?> poliedroClass = super.introspector.getClass("Poliedro");
		final Class<?> volumenClass = super.introspector.getClass("Volumen");
		if (poliedroClass == null || volumenClass == null)
			return false;

		return super.introspector.getInterfaceCount(poliedroClass) == 1 && super.introspector.checkImplements(poliedroClass, volumenClass);
	}
	public boolean solvePoliedroImplementsVolumen()
	{
		return false;
	}
	public boolean testPoliedroImplementsVolumen()
	{
		return true;
	}

    // Los atributos y métodos de las clases son los correctos

	// La clase Poliedro tiene un unico atributo (double) para la longitud de la arista
	public boolean checkPoliedroAristaField() 
	{
		final Class<?> poliedroClass = super.introspector.getClass("Poliedro");
		if (poliedroClass == null)
			return false;

		final Field[] poliedroFields = super.introspector.getDeclaredFields(poliedroClass);
		if (poliedroFields.length != 1)
			return false;

		final Field poliedroField = poliedroFields[0];

		return super.introspector.checkClass(poliedroField, double.class, true);
	}
	public boolean solvePoliedroAristaField()
	{
		return false;
	}
	public boolean testPoliedroAristaField()
	{
		return true;
	}

	// Poliedro constructor
	public boolean checkPoliedroConstructor()
	{
		final Class<?> poliedroClass = super.introspector.getClass("Poliedro");
		if (poliedroClass == null)
			return false;

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

		return super.introspector.checkDeclaredConstructor(poliedroClass, paramTypesConstructor1, true);
	}
	public boolean solvePoliedroConstructor()
	{
		return false;
	}
	public boolean testPoliedroConstructor()
	{
		return true;
	}

	// Poliedro soyPoliedro method
	public boolean checkPoliedroSoyPoliedroMethod()
	{
		final Class<?> poliedroClass = super.introspector.getClass("Poliedro");
		if (poliedroClass == null)
			return false;

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

		return super.introspector.checkDeclaredMethod(poliedroClass, "soyPoliedro", paramTypes, boolean.class, true);
	}
	public boolean solvePoliedroSoyPoliedroMethod()
	{
		return false;
	}
	public boolean testPoliedroSoyPoliedroMethod()
	{
		final String testCaseName = "TestPoliedroSoyPoliedroMethod";
		final String testMethodName = "test";
		final double posX = 3.0;
		final double posY = 5.0;
		final double arista = 5.0;		
		final Boolean expectedResult = true;


		final Object[] args = { posX, posY, arista };
		final Object testCaseResult = super.tester.executeTestCase(testCaseName, testMethodName, args);

		return expectedResult.equals(testCaseResult);
	}
	
	// La clase Tetraedro no tiene ningún atributo
	public boolean checkTetraedroSinAtributos() 
	{
		final Class<?> tetraedroClass = super.introspector.getClass("Tetraedro");
		if (tetraedroClass == null)
			return false;

		final Field[] tetraedroFields = super.introspector.getDeclaredFields(tetraedroClass);
		if (tetraedroFields.length != 0)
			return false;
        
        return true;
	}
	public boolean solveTetraedroSinAtributos()
	{
		return false;
	}
	public boolean testTetraedroSinAtributos()
	{
		return true;
	}

	// Tetraedro constructor
	public boolean checkTetraedroConstructor()
	{
		final Class<?> tetraedroClass = super.introspector.getClass("Tetraedro");
		if (tetraedroClass == null)
			return false;

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

		return super.introspector.checkDeclaredConstructor(tetraedroClass, paramTypesConstructor1, true);
	}
	public boolean solveTetraedroConstructor()
	{
		return false;
	}
	public boolean testTetraedroConstructor()
	{
		final String testCaseName = "TestTetraedroConstructor";
		final String testMethodName = "test";
		final Boolean expectedResult = true;
		final double posX = 12.3;
		final double posY = 56.6;
		final double arista = 922.5;

		final Object[] args = { posX, posY, arista };
		final Object testCaseResult = super.tester.executeTestCase(testCaseName, testMethodName, args);

		return expectedResult.equals(testCaseResult);
	}

	// Tetraedro volumen method
	public boolean checkTetraedroVolumenMethod()
	{
		final Class<?> tetraedroClass = super.introspector.getClass("Tetraedro");
		if (tetraedroClass == null)
			return false;

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

		return super.introspector.checkDeclaredMethod(tetraedroClass, "volumen", paramTypes, double.class, true);
	}
	public boolean solveTetraedroVolumenMethod()
	{
		return false;
	}
	public boolean testTetraedroVolumenMethod()
	{
		final String testCaseName = "TestTetraedroVolumenMethod";
		final String testMethodName = "test";
		final double posX = 2.2;
		final double posY = 6.1;
		final double arista = 9.5;
		final Double expectedResult = Math.pow(arista,3)*Math.sqrt(2)/12;


		final Object[] args = { posX, posY, arista };
		final Object testCaseResult = super.tester.executeTestCase(testCaseName, testMethodName, args);

		return expectedResult.equals(testCaseResult);
	}

	// Tetraedro area method
	public boolean checkTetraedroAreaMethod()
	{
		final Class<?> tetraedroClass = super.introspector.getClass("Tetraedro");
		if (tetraedroClass == null)
			return false;

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

		return super.introspector.checkDeclaredMethod(tetraedroClass, "area", paramTypes, double.class, true);
	}
	public boolean solveTetraedroAreaMethod()
	{
		return false;
	}
	public boolean testTetraedroAreaMethod()
	{
		final String testCaseName = "TestTetraedroAreaMethod";
		final String testMethodName = "test";
		final double posX = 2.2;
		final double posY = 6.1;
		final double arista = 9.5;
		final Double expectedResult = Math.pow(arista,2)*Math.sqrt(3);


		final Object[] args = { posX, posY, arista };
		final Object testCaseResult = super.tester.executeTestCase(testCaseName, testMethodName, args);

		return expectedResult.equals(testCaseResult);
	}
		
	// La clase Hexaedro no tiene ningún atributo
	public boolean checkHexaedroSinAtributos() 
	{
		final Class<?> hexaedroClass = super.introspector.getClass("Hexaedro");
		if (hexaedroClass == null)
			return false;

		final Field[] hexaedroFields = super.introspector.getDeclaredFields(hexaedroClass);
		if (hexaedroFields.length != 0)
			return false;
        
        return true;
	}
	public boolean solveHexaedroSinAtributos()
	{
		return false;
	}
	public boolean testHexaedroSinAtributos()
	{
		return true;
	}

	// Tetraedro constructor
	public boolean checkHexaedroConstructor()
	{
		final Class<?> hexaedroClass = super.introspector.getClass("Hexaedro");
		if (hexaedroClass == null)
			return false;

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

		return super.introspector.checkDeclaredConstructor(hexaedroClass, paramTypesConstructor1, true);
	}
	public boolean solveHexaedroConstructor()
	{
		return false;
	}
	public boolean testHexaedroConstructor()
	{
		final String testCaseName = "TestHexaedroConstructor";
		final String testMethodName = "test";
		final Boolean expectedResult = true;
		final double posX = 12.3;
		final double posY = 56.6;
		final double arista = 922.5;

		final Object[] args = { posX, posY, arista };
		final Object testCaseResult = super.tester.executeTestCase(testCaseName, testMethodName, args);

		return expectedResult.equals(testCaseResult);
	}

	// Hexaedro volumen method
	public boolean checkHexaedroVolumenMethod()
	{
		final Class<?> hexaedroClass = super.introspector.getClass("Hexaedro");
		if (hexaedroClass == null)
			return false;

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

		return super.introspector.checkDeclaredMethod(hexaedroClass, "volumen", paramTypes, double.class, true);
	}
	public boolean solveHexaedroVolumenMethod()
	{
		return false;
	}
	public boolean testHexaedroVolumenMethod()
	{
		final String testCaseName = "TestHexaedroVolumenMethod";
		final String testMethodName = "test";
		final double posX = 2.2;
		final double posY = 6.1;
		final double arista = 9.5;
		final Double expectedResult = Math.pow(arista,3);


		final Object[] args = { posX, posY, arista };
		final Object testCaseResult = super.tester.executeTestCase(testCaseName, testMethodName, args);

		return expectedResult.equals(testCaseResult);
	}

	// Hexaedro area method
	public boolean checkHexaedroAreaMethod()
	{
		final Class<?> hexaedroClass = super.introspector.getClass("Hexaedro");
		if (hexaedroClass == null)
			return false;

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

		return super.introspector.checkDeclaredMethod(hexaedroClass, "area", paramTypes, double.class, true);
	}
	public boolean solveHexaedroAreaMethod()
	{
		return false;
	}
	public boolean testHexaedroAreaMethod()
	{
		final String testCaseName = "TestHexaedroAreaMethod";
		final String testMethodName = "test";
		final double posX = 2.2;
		final double posY = 6.1;
		final double arista = 9.5;
		final Double expectedResult = 6 * Math.pow(arista,2);


		final Object[] args = { posX, posY, arista };
		final Object testCaseResult = super.tester.executeTestCase(testCaseName, testMethodName, args);

		return expectedResult.equals(testCaseResult);
	}

}