package it.micegroup.voila2.cartridge.library.javaservice;

import java.util.ArrayList;
import java.util.Collection;
import java.util.HashSet;
import java.util.Iterator;
import java.util.Set;
import java.util.stream.Collectors;

import org.eclipse.emf.common.util.EList;
import org.eclipse.emf.ecore.util.EcoreUtil;
import org.eclipse.uml2.uml.Association;
import org.eclipse.uml2.uml.Class;
import org.eclipse.uml2.uml.Classifier;
import org.eclipse.uml2.uml.EnumerationLiteral;
import org.eclipse.uml2.uml.Model;
import org.eclipse.uml2.uml.Package;
import org.eclipse.uml2.uml.Property;
import org.eclipse.uml2.uml.Stereotype;
import org.eclipse.uml2.uml.UMLPackage;

import it.micegroup.voila2.cartridge.library.javaservice.AssociationUtils.RelationType;

public class EntityUtils {

	public static boolean hasAutogeneratedIdBySeq(Class classe) {
		Stereotype stereotype = classe.getAppliedStereotype(StereotypeName.ENTITY);
		if (stereotype != null) {
			String sequence = (String) classe.getValue(stereotype, StereotypePropertyName.SEQUENCE_NAME);

			if (classe.hasValue(stereotype, StereotypePropertyName.SEQUENCE_NAME) && sequence != null
					&& !("").equals(sequence)) {
				return true;
			}
		}
		return false;
	}

	public static String getSequenceName(Class classe) {
		String sequence = "";
		Stereotype stereotype = classe.getAppliedStereotype(StereotypeName.ENTITY);
		if (stereotype != null) {
			sequence = (String) classe.getValue(stereotype, StereotypePropertyName.SEQUENCE_NAME);
		}
		return sequence;
	}

	/**
	 * Return true if the class has applied the Entity Stereotype
	 * 
	 * @author Jessica Pascarella <jessica.pascarella@micegroup.it>
	 * @param classe
	 * @return true if the class has applied the Entity Stereotype
	 */
	public static boolean isEntityStereotype(Class classe) {
		Stereotype stereotype = classe.getAppliedStereotype(StereotypeName.ENTITY);
		if (stereotype != null)
			return true;
		return false;
	}

	/**
	 * Check if a class has at least 1 property annotated as ID
	 * 
	 * @author Vittorio Niespolo <vittorio.niespolo@micegroup.it>
	 * @param classe
	 * @return
	 */
	public static boolean hasInnerCompositeKey(Class classe) {
		int numOfId = getClassIDsIncludingFK(classe).size();
		if (numOfId == 1) {
			return false;
		}
		return true;
	}

	/**
	 * Return a set containing all properties annotated as ID
	 * 
	 * @author Vittorio Niespolo <vittorio.niespolo@micegroup.it>
	 * @param classe
	 * @return a set of ID belonging to the class
	 */
	public static Collection<Property> getClassIDs(Class classe) {
		Collection<Property> ids = new ArrayList<>();
		if (classe != null && classe.getOwnedAttributes() != null) {
			Iterator<Property> attributes = getAttributeList(classe).iterator();
			while (attributes.hasNext()) {
				Property p = attributes.next();
				if (p.isID()) {
					ids.add(p);
				}
			}
		}
		return ids;
	}

	/**
	 * Return a set of id property obtained from class and its parent relations
	 * 
	 * @author Vittorio Niespolo <vittorio.niespolo@micegroup.it>
	 * @param classe
	 * @return a set of id property obtained from class and its parent relations
	 */
	public static Collection<Property> getClassIDsIncludingFK(Class classe) {
		// ID Interni
		Collection<Property> ids = getClassIDs(classe);
		// ID Esterni
		ids.addAll(getIdAttributesFromForeignKeys(classe));
		return ids;
	}

	/**
	 * Check if a property is in the compositeKey of the class
	 * 
	 * @author Vittorio Niespolo <vittorio.niespolo@micegroup.it>
	 * @param classe
	 * @param property
	 * @return true if contained in the class compositeKey
	 */
	public static boolean isInCompositeKey(Class classe, Property property) {
		if (hasCompositeKey(classe)) {
			if (getClassIDsIncludingFK(classe).contains(property)) {
				return true;
			}
		}
		return false;
	}

	/**
	 * Return a collection of Property containing ID derived from parents foreign
	 * key
	 * 
	 * @author Vittorio Niespolo <vittorio.niespolo@micegroup.it>
	 *
	 * @param classe
	 * @return
	 */
	public static Collection<Property> getIdAttributesFromForeignKeys(Class classe) {
		Collection<Property> idsFromForeignKeys = new ArrayList<>();
		Iterator<Association> associations = AssociationUtils.getParentAssociations(classe).iterator();

		while (associations.hasNext()) {
			Association a = associations.next();
			if (AssociationUtils.isIdentifying(a)) {
				idsFromForeignKeys.addAll(getClassIDsIncludingFK(AssociationUtils.getOtherEnd(classe, a)));
			}
		}
		return idsFromForeignKeys;
	}

	/**
	 * Return a Set of id properties from class and its associations
	 * 
	 * @author Vittorio Niespolo <vittorio.niespolo@micegroup.it>, Jessica
	 *         Pascarella <jessica.pascarella@micegroup.it>
	 * @param classe
	 * @return Set of id properties
	 */
	public static Collection<Property> getCompositeKeyAttributes(Class classe) {
		Set<Property> ckAttributes = new HashSet<>();

		// Attributi id interni
		ckAttributes.addAll(getClassIDs(classe));

		// Attributi id da FK
		Iterator<Association> associations = classe.getAssociations().iterator();
		while (associations.hasNext()) {
			Association a = associations.next();
			if (AssociationUtils.isIdentifying(a)) {
				for (Property p : a.getMemberEnds()) {
					if (GeneralUtils.isParentZeroOneMultiplicity(a, p))
						ckAttributes.addAll(getClassIDs((Class) p.getType()));
				}
			}
		}
		return ckAttributes;
	}

	/**
	 * Return a Set of id properties from class and its associations
	 * 
	 * @author Vittorio Niespolo <vittorio.niespolo@micegroup.it>, Jessica
	 *         Pascarella <jessica.pascarella@micegroup.it>
	 * @param classe
	 * @return Set of id properties
	 */

	public Collection<Property> getCompositeKeyAttributesWithMappedNames(Class classe, Boolean includeInternalCK) {
		Collection<Property> ckAttributes = new ArrayList<>();
		Collection<Property> ownerClassIDs = new ArrayList<>();
		AssociationUtils assUtils = new AssociationUtils();

		// Attributi id interni
		ckAttributes.addAll(getClassIDs(classe));

		// Attributi id da FK
		for (Association a : AssociationUtils.getParentAssociations(classe)) {
			if (!hasCompositeKey(AssociationUtils.getParentClass(classe, a)) && !includeInternalCK.booleanValue()
					|| includeInternalCK.booleanValue()) {
				if (AssociationUtils.isIdentifying(a)) {
					for (Property p : a.getMemberEnds()) {
						if (GeneralUtils.isClassParent(classe, a, p)) {
							ownerClassIDs = getClassIDsIncludingFK((Class) p.getType());
							for (Property id : ownerClassIDs) {
								id.eSetDeliver(false);
								id.setName(assUtils.getMappedName(a, id));
							}
							ckAttributes.addAll(ownerClassIDs);
						}
					}
				}
			}
		}
		return ckAttributes;
	}

	/**
	 * Return true if a Class has a CompositeKey
	 * 
	 * @author Vittorio Niespolo <vittorio.niespolo@micegroup.it>
	 * @param classe
	 * @return
	 */
	public static boolean hasCompositeKey(Class classe) {
		// VN L'id di una classe con generalizzazione è nella general
		if (hasGeneralClass(classe)) {
			return false;
		}
		if (classe != null && (hasExternalPK(classe) || EntityUtils.hasInnerCompositeKey(classe)))
			return true;
		return false;
	}

	/**
	 * Return true if a Class has at least one father with a CompositeKey
	 * 
	 * @author Luca Barone <luca.barone@micegroup.it>
	 * @param classe
	 * @return
	 */
	public boolean hasFatherWithCompositeKey(Class classe) {
		if (classe != null) {
			for (Class fClass : AssociationUtils.getParentClasses(classe)) {
				if (fClass != null && hasExternalPK(fClass) || hasInnerCompositeKey(fClass))
					return true;
			}
		}
		return false;
	}

	/**
	 * Return true if an entity has id attribute flagged as autogenerated.
	 * 
	 * @author Vittorio Niespolo <vittorio.niespolo@micegroup.it>
	 * @param classe
	 * @return
	 */
	public static boolean hasAutogeneratedId(Class classe) {
		Stereotype stereotype = classe.getAppliedStereotype(StereotypeName.ENTITY);
		if (stereotype != null) {
			boolean autoId = (boolean) classe.getValue(stereotype, StereotypePropertyName.AUTOID);

			if (autoId) {
				return true;
			}
		}
		return false;
	}

	/**
	 * Return the definition's stereotype value for the input entity
	 * 
	 * @author Vittorio Niespolo <vittorio.niespolo@micegroup.it>
	 * @param classe
	 * @return
	 */
	public String getEntityTooltip(Class classe) {
		if (hasHelp(classe)) {
			return getEntityHelp(classe);
		}
		if (hasDefinition(classe)) {
			return getEntityDefinition(classe);
		}
		if (org.apache.commons.lang3.StringUtils.isNotEmpty(getEntityLabel(classe))) {
			return getEntityLabel(classe);
		}
		if (!classe.getLabel().equals(classe.getName())) {
			return classe.getLabel();
		}
		return StringUtils.splitCamel(classe.getName());
	}

	/**
	 * Return the definition's stereotype value for the input entity
	 * 
	 * @author Vittorio Niespolo <vittorio.niespolo@micegroup.it>
	 * @param classe
	 * @return
	 */
	public static String getEntityDefinition(Class classe) {
		return StereotypeUtils.<String, Class>getStereotypeValue(classe, StereotypeName.ENTITY,
				StereotypePropertyName.DEFINITION);
	}

	/**
	 * Return the definition's stereotype value for the input entity
	 * 
	 * @author Vittorio Niespolo <vittorio.niespolo@micegroup.it>
	 * @param classe
	 * @return
	 */
	public String getEntityLabel(Class classe) {
		if (org.apache.commons.lang3.StringUtils.isNotEmpty(StereotypeUtils.<String, Class>getStereotypeValue(classe,
				StereotypeName.ENTITY, StereotypePropertyName.LABEL))) {
			return StereotypeUtils.<String, Class>getStereotypeValue(classe, StereotypeName.ENTITY,
					StereotypePropertyName.LABEL);
		}
		return StringUtils.splitCamel(classe.getLabel());
	}

	/**
	 * Return true if definition's stereotype is not empty
	 * 
	 * @author Vittorio Niespolo <vittorio.niespolo@micegroup.it>
	 * @param classe
	 * @return
	 */
	public boolean hasDefinition(Class classe) {
		if (getEntityDefinition(classe) == null || getEntityDefinition(classe) == "") {
			return false;
		}
		return true;
	}

	/**
	 * Return the definition's stereotype value for the input entity
	 * 
	 * @author Vittorio Niespolo <vittorio.niespolo@micegroup.it>
	 * @param classe
	 * @return
	 */
	public static String getEntityHelp(Class classe) {
		return StereotypeUtils.<String, Class>getStereotypeValue(classe, StereotypeName.ENTITY,
				StereotypePropertyName.HELP);
	}

	/**
	 * Return true if definition's stereotype is not empty
	 * 
	 * @author Vittorio Niespolo <vittorio.niespolo@micegroup.it>
	 * @param classe
	 * @return
	 */
	public boolean hasHelp(Class classe) {
		if (getEntityHelp(classe) == null || getEntityHelp(classe) == "") {
			return false;
		}
		return true;
	}

	/**
	 * Check if a property is contained in compositeKey or is an ID
	 * 
	 * @author Vittorio Niespolo <vittorio.niespolo@micegroup.it>
	 * @param property
	 * @param classe
	 * @return
	 */
	public static boolean isInID(Property property, Class classe) {
		if (getClassIDsIncludingFK(classe).contains(property)) {
			return true;
		}
		return false;
	}

	/**
	 * Check if a property is contained in compositeKey or is an ID
	 * 
	 * @author Vittorio Niespolo <vittorio.niespolo@micegroup.it>
	 * @param property
	 * @param classe
	 * @return
	 */
	public static boolean isInInnerID(Class classe, Property property) {
		if (getClassIDs(classe).contains(property)) {
			return true;
		}
		return false;
	}

	public static boolean isAuditable(Class classe) {
		Stereotype stereotype = classe.getAppliedStereotype(StereotypeName.ENTITY);
		if (stereotype != null) {
			return (boolean) classe.getValue(stereotype, StereotypePropertyName.AUDITABLE) == true;
		}
		return false;
	}

	/**
	 * Return a list containing internal attributes and externals attributes from
	 * parent entity
	 * 
	 * @author Vittorio Niespolo <vittorio.niespolo@micegroup.it>
	 * @param aClass
	 * @return
	 */
	public static Collection<Property> getCompleteAttributeList(Class aClass) {
		Collection<Property> attributeList = new ArrayList<>();
		// ID ESTERNI
		attributeList.addAll(EntityUtils.getIdAttributesFromForeignKeys(aClass));
		// ATTR. INTERNI
		attributeList.addAll(getAttributeList(aClass));
		return attributeList;
	}

	/**
	 * Return a list containing internal attributes and externals attributes from
	 * parent entity
	 * 
	 * @author Vittorio Niespolo <vittorio.niespolo@micegroup.it>
	 * @param aClass
	 * @return
	 */
	public static Collection<Property> getAttributeList(Class aClass) {
		Collection<Property> attributeList = new ArrayList<>();
		// VN Attenzione ogni classe può estendere solo una classe.
		if (hasGeneralClass(aClass)) {
			// ATTR. DA GENERAL
			Collection<Property> attributesFromGeneral = ((Class) aClass.getGenerals().toArray()[0])
					.getOwnedAttributes();
			attributeList.addAll(attributesFromGeneral);
		}

		// ATTR. INTERNI
		attributeList.addAll(aClass.getOwnedAttributes());

		return attributeList;
	}

	/**
	 * Ritorna la categoria di appartenenza di una entità
	 * 
	 * @author Vittorio Niespolo <vittorio.niespolo@micegroup.it>
	 * @param aClass
	 * @return
	 */
	public static String getCategory(Class aClass) {
		return StereotypeUtils.<EnumerationLiteral, Class>getStereotypeValue(aClass, StereotypeName.ENTITY,
				StereotypePropertyName.CATEGORY).getName();
	}

	/**
	 * @author Vittorio Niespolo <vittorio.niespolo@micegroup.it>
	 * @param aClass
	 * @param category
	 * @return
	 */
	public static boolean isInCategory(Class aClass, EntityCategory category) {
		if (category.toString().equals(getCategory(aClass)))
			return true;
		return false;
	}

	public static boolean isBusinessCategory(Class aClass) {
		return isInCategory(aClass, EntityCategory.business);
	}

	public static boolean isAttachmentCategory(Class aClass) {
		return isInCategory(aClass, EntityCategory.attachment);
	}

	public static boolean isMailCategory(Class aClass) {
		return isInCategory(aClass, EntityCategory.mail);
	}

	public static boolean isSecurityCategory(Class aClass) {
		return isInCategory(aClass, EntityCategory.security);
	}

	public static boolean hasRelationTypeRelation(Class aClass, RelationType relationType) {

		if (aClass != null && aClass.getAssociations() != null) {
			Iterator<Association> associations = aClass.getAssociations().iterator();
			while (associations.hasNext()) {
				Association a = associations.next();
				if (AssociationUtils.isAssociationInRelationType(a, relationType)) {
					return true;
				}
			}
		}
		return false;
	}

	public static boolean hasOneToOneRelation(Class aClass) {
		return hasRelationTypeRelation(aClass, RelationType.OneToOne);
	}

	public static boolean hasManyToOneRelation(Class aClass) {
		return hasRelationTypeRelation(aClass, RelationType.ManyToOne);
	}

	/**
	 * Return the first string attribute from class attributes. If noone of
	 * attribute is a string then first one is taken.
	 * 
	 * @author Vittorio Niespolo <vittorio.niespolo@micegroup.it>
	 * @param aClass
	 * @return
	 */
	public static String getFirstStringAttribute(Class aClass) {
		for (Property attribute : getFiltrableAttributes(aClass)) {
			if (PropertyUtils.isString(attribute)) {
				return StringUtils.toUpperFirst(attribute.getName());
			}
		}
		return "ObjectKey";
	}

	public static boolean isInRole(Class classe, String entityRole) {
		Stereotype stereotype = null;
		String role;

		stereotype = classe.getAppliedStereotype(StereotypeName.ENTITY);
		if (stereotype != null) {
			role = (String) classe.getValue(stereotype, StereotypePropertyName.ROLE);
			if (role != null) {
				return (role.toLowerCase().equals(entityRole.toLowerCase()));
			}
		}
		return false;
	}

	/**
	 * @author Vittorio Niespolo <vittorio.niespolo@micegroup.it>, Jessica
	 *         Pascarella <jessica.pascarella@micegroup.it>
	 * @param classe
	 * @return
	 */
	public static boolean hasExternalPK(Class classe) {
		if (classe != null && classe.getAssociations() != null) {
			Iterator<Association> associations = classe.getAssociations().iterator();
			while (associations.hasNext()) {
				Association a = associations.next();
				if (AssociationUtils.isIdentifying(a)) {
					for (Property p : a.getMemberEnds()) {
						if (((Class) p.getType() == classe)
								&& (PropertyUtils.isNMultiplicity(p) || GeneralUtils.isChildZeroOneMultiplicity(a, p)))
							return true;
					}
				}
			}
		}
		return false;
	}

	/**
	 * Return true if entity has at least one date attribute
	 * 
	 * @author Federico Gambardella <federico.gambardella@micegroup.it>
	 * @param classe
	 * @return
	 */
	public static boolean hasDateProperty(Class classe) {
		for (Property property : classe.allAttributes()) {
			if (PropertyUtils.isDate(property))
				return true;
		}
		return false;
	}

	/**
	 * Return true if entity has at least one attribute
	 * 
	 * @author Vittorio Niespolo <vittorio.niespolo@micegroup.it>
	 * @param classe
	 * @return
	 */
	public boolean hasAttributes(Class classe) {
		if (classe.getAllAttributes().size() > 0) {
			return true;
		}
		return false;
	}

	/**
	 * Return true if entity has at least one attribute
	 * 
	 * @author Vittorio Niespolo <vittorio.niespolo@micegroup.it>
	 * @param classe
	 * @return
	 */
	public boolean hasAttributesWithoutIDs(Class classe) {
		Collection<Property> ids = getClassIDs(classe);
		Collection<Property> attributes = getAttributeList(classe);
		attributes.removeAll(ids);

		if (attributes.size() > 0) {
			return true;
		}
		return false;
	}

	/**
	 * Ritorna la lista degli attributi semplici (NON ID) della classe
	 * 
	 * @author Vittorio Niespolo <vittorio.niespolo@micegroup.it>
	 *
	 * @param classe
	 * @return
	 */
	public Collection<Property> getAttributesWithoutIDs(Class classe) {
		Collection<Property> ids = getClassIDs(classe);
		Collection<Property> attributes = getAttributeList(classe);
		attributes.removeAll(ids);

		return attributes;
	}

	/**
	 * Ritorna true se la classe in input ha almeno una classe Gen collegata
	 * 
	 * @author Vittorio Niespolo <vittorio.niespolo@micegroup.it>
	 *
	 * @param aClass
	 * @return
	 */
	public static boolean hasGeneralClass(Class aClass) {
		return !aClass.getGenerals().isEmpty();
	}

	/**
	 * Ritorna true se la classe in input estende SecurityUser
	 * 
	 * @author Vittorio Niespolo <vittorio.niespolo@micegroup.it>
	 *
	 * @param aClass
	 * @return
	 */
	public static boolean generalizeSecurityUserRole(Class aClass) {
		Class general = getGeneralClass(aClass);
		if (general != null && SecurityUtils.isInSecurityUserRole(general)) {
			return true;
		}
		return false;
	}

	/**
	 * Ritorna la classe Gen collegata a quella in input
	 * 
	 * @author Vittorio Niespolo <vittorio.niespolo@micegroup.it>
	 *
	 * @param aClass
	 * @return
	 */
	public static Class getGeneralClass(Class aClass) {
		EList<Classifier> generals = aClass.getGenerals();
		if (generals != null && generals.size() > 0)
			return ((Class) generals.toArray()[0]);
		return null;
	}

	/**
	 * Array delle classi estese
	 * 
	 * @author Vittorio Niespolo <vittorio.niespolo@micegroup.it>
	 */
	private Collection<Class> extendedClasses = new ArrayList<Class>();

	/**
	 * Popola l'elenco delle entità estese. Attualmente Non c'è un metodo per sapese
	 * se una data entità viene estesa da altre
	 * 
	 * @author Vittorio Niespolo <vittorio.niespolo@micegroup.it>
	 *
	 * @param aModel
	 */
	public void populateExtendedClasses(Model aModel) {
		extendedClasses = new ArrayList<Class>();
		for (Class classe : ModelUtils.getModelEntities()) {
			if (!classe.getGenerals().isEmpty()) {
				extendedClasses.addAll((Collection<? extends Class>) classe.getGenerals());
			}
		}
	}

	/**
	 * Controlla se la classe in input è estesa da altre entità
	 * 
	 * @author Vittorio Niespolo <vittorio.niespolo@micegroup.it>
	 *
	 * @param classe
	 * @return
	 */
	public boolean isClassExtended(Class classe) {
		for (Class aClass : extendedClasses) {
			if (classe.getName().equals(aClass.getName())) {
				return true;
			}
		}
		return false;
	}

	/**
	 * Ritorna una lista di proprietà di tipo date
	 * 
	 * @author Vittorio Niespolo <vittorio.niespolo@micegroup.it>
	 *
	 * @param classe
	 * @return
	 */
	public Collection<Property> getDateProperties(Class classe) {
		return getAttributeList(classe).stream().filter(attr -> PropertyUtils.isDate(attr))
				.collect(Collectors.toList());
	}

	/**
	 * Ritorna la lista dei soli attributi di tipo stringa della classe
	 * 
	 * @author Vittorio Niespolo <vittorio.niespolo@micegroup.it>
	 *
	 * @param classe
	 * @return
	 */
	public Collection<Property> getStringProperties(Class classe) {
		return getAttributeList(classe).stream().filter(attr -> PropertyUtils.isString(attr))
				.collect(Collectors.toList());
	}

	/**
	 * Ritorna la lista dei soli attributi di tipo stringa dei padri della classe in
	 * input
	 * 
	 * @author Vittorio Niespolo <vittorio.niespolo@micegroup.it>
	 *
	 * @param classe
	 * @return
	 */
	public static Collection<Property> getParentStringProperties(Class classe) {
		Collection<Property> result = new ArrayList<Property>();
		for (Class parentClass : AssociationUtils.getParentClasses(classe)) {
			result.addAll(getAttributeList(parentClass).stream().filter(attr -> PropertyUtils.isString(attr))
					.collect(Collectors.toList()));
		}
		return result;
	}

	public static Collection<Property> getParentStringId(Class classe) {
		Collection<Property> result = new ArrayList<Property>();
		for (Class parentClass : AssociationUtils.getParentClasses(classe)) {
			result.addAll(getClassIDs(parentClass).stream().filter(attr -> PropertyUtils.isString(attr))
					.collect(Collectors.toList()));
		}
		return result;
	}

	/**
	 * Ritorna l'elenco di tutte le associazioni identifying della classe in input
	 * 
	 * @author Vittorio Niespolo <vittorio.niespolo@micegroup.it>
	 *
	 * @param classe
	 * @return
	 */
	public static Collection<Association> getIdentifyingParentAssociations(Class classe) {
		Collection<Association> result = new ArrayList<>();
		for (Association association : AssociationUtils.getParentAssociations(classe)) {
			if (AssociationUtils.isIdentifying(association)) {
				result.add(association);
			}
		}
		return result;
	}

	/**
	 * Ritorna l'elenco di tutte le associazioni identifying della classe in input
	 * 
	 * @author Vittorio Niespolo <vittorio.niespolo@micegroup.it>
	 *
	 * @param classe
	 * @return
	 */
	public Collection<Association> getNotIdentifyingParentAssociations(Class classe) {
		Collection<Association> result = new ArrayList<>();
		for (Association association : AssociationUtils.getParentAssociations(classe)) {
			if (!AssociationUtils.isIdentifying(association)) {
				result.add(association);
			}
		}
		return result;
	}

	/**
	 * Ritorna una lista di attributi sui quali è possibile eseguire ricerche
	 * 
	 * @author Vittorio Niespolo <vittorio.niespolo@micegroup.it>
	 *
	 * @param classe
	 * @return
	 */
	public static Collection<Property> getFiltrableAttributes(Class classe) {
		Collection<Property> result = getFiltrableInternalAttributes(classe);

		// Campi Audit
		if (isAuditable(classe)) {
			result.add(createPropertyForClass(classe, AuditableAttributes.createdBy.toString(), "String", "Creato da"));
			result.add(createPropertyForClass(classe, AuditableAttributes.lastModifiedBy.toString(), "String", "Ultima Modifica da"));
			result.add(createPropertyForClass(classe, AuditableAttributes.createdDate.toString(), "Datetime", "Data Creazione"));
			result.add(createPropertyForClass(classe, AuditableAttributes.lastModifiedDate.toString(), "Datetime", "Data Ultima Modifica"));
		}
		return result;
	}

	public static Collection<Property> getFiltrableInternalAttributes(Class classe) {
		Collection<Property> result = classe.getOwnedAttributes().stream()
				.filter(a -> !TypeUtils.getNotFiltrableTypes().contains(a.getType().getName()))
				.collect(Collectors.toList());
		return result;
	}

	private static Property createPropertyForClass(Class classe, String propertyName, String dataType, String labelValue) {
		Property prop = (Property) EcoreUtil.create(UMLPackage.Literals.PROPERTY);
		
		prop.setName(propertyName);
		prop.setType(GeneralUtils.getVoilaType(classe, dataType));

		//FIXME :(
//		Stereotype stereotype = (Stereotype)ModelUtils.getVoila2Profile().getMember("Attribute");
//		prop.applyStereotype(stereotype);
//		prop.setValue(stereotype, StereotypePropertyName.LABEL, labelValue);
		
		return prop;
	}

	public static Collection<Property> getAttributesForPresentation(Class classe) {
		return getFiltrableAttributes(classe);
	}

	/**
	 * Return a list containing internal attributes and externals attributes from
	 * parent entity
	 * 
	 * @author Vittorio Niespolo <vittorio.niespolo@micegroup.it>
	 * @param aClass
	 * @return
	 */
	public static Collection<Property> getCompleteAttributeListForPresentation(Class aClass) {
		Collection<Property> attributeList = new ArrayList<>();
		// ID ESTERNI
		attributeList.addAll(EntityUtils.getIdAttributesFromForeignKeys(aClass));
		// ATTR. INTERNI
		attributeList.addAll(getAttributesForPresentation(aClass));
		return attributeList;
	}

	/**
	 * Method to check if a Class has exactly one field of String type to condition
	 * annotation generation
	 * 
	 * @param aClass
	 * @return 1 if num field string == 1 , 0 otherwise
	 */
	public boolean entityHasOneStringField(Class aClass) {
		Collection<Property> stringProperties = getStringProperties(aClass);

		if (stringProperties.size() == 1)
			return true;

		return false;
	}

	public String getTableName(Class aClass) {
		return StereotypeUtils.<String, Class>getStereotypeValue(aClass, StereotypeName.ENTITY,
				StereotypePropertyName.TABLE_NAME);
	}

	/**
	 * Ritorna un helper che descriva la classe
	 * 
	 * @author Vittorio Niespolo <vittorio.niespolo@micegroup.it>
	 *
	 * @param aClass
	 * @return
	 */
	public static String getHelper(Class aClass) {
		String result = getEntityHelp(aClass);
		if (result == null) {
			result = getEntityDefinition(aClass);
		}
		if (result == null) {
			result = StringUtils.splitCamel(aClass.getName());
		}
		return result;
	}

	public static Collection<Property> getAttributesForPresentationWOAudit(Class classe) {
		return getFiltrableInternalAttributes(classe);
	}

	public static boolean hasParentsWithCK(Class classe) {
		for (Association parentAss : AssociationUtils.getParentAssociations(classe)) {
			if (AssociationUtils.isIdentifying(parentAss)
					&& hasCompositeKey(AssociationUtils.getParentClass(classe, parentAss))) {
				return true;
			}
		}
		return false;
	}

	/**
	 * Ritorna true se classe ha una self relation.
	 * 
	 * @author Federico Gambardella<federico.gambardella@micegroup.it>
	 *
	 * @param classe
	 * @return
	 */
	public static boolean hasSelfRelation(Class classe) {
		for (Association association : classe.getAssociations()) {
			if (Boolean.TRUE.equals(AssociationUtils.isSelfRelation(association)))
				return true;
		}
		return false;
	}

	/**
	 * Return true id the class has the attribute in input.
	 * 
	 * @author Federico Gambardella<federico.gambardella@micegroup.it>
	 * @param classe
	 * @param attribute
	 * @return
	 */
	public static boolean hasAttribute(Class classe, Property attribute) {
		Collection<Property> attributeList = classe.getAllAttributes();
		for (Property property : attributeList) {
			if (property.equals(attribute))
				return true;
		}
		return false;
	}

	/**
	 * Ritorna true se la classe ha almeno una TextArea nel FE
	 * 
	 * @author Vittorio Niespolo <vittorio.niespolo@micegroup.it>
	 *
	 * @param classe
	 * @return
	 */
	public static boolean hasTextAreas(Class classe) {
		for (Property attribute : getAttributesForPresentation(classe)) {
			if (PropertyUtils.hasPhysicalLengthGreaterThan(PropertyUtils.getPhysicalColumnLenght(attribute))) {
				return true;
			}
		}
		return false;
	}

	/**
	 * Ritorna true se c'è almeno un'associazione con popup per la parent selection
	 * 
	 * @author Vittorio Niespolo <vittorio.niespolo@micegroup.it>
	 *
	 * @param classe
	 * @return
	 */
	public static boolean hasPopUpParentSelection(Class classe) {
		for (Association parentAss : AssociationUtils.getParentAssociations(classe)) {
			if (AssociationUtils.hasPopupParentSelection(parentAss)) {
				return true;
			}
		}
		return false;
	}

	/**
	 * Check if the input class has at least one parent.
	 * 
	 * @author Federico Gambardella<federico.gambardella@micegroup.it>
	 * @param classe
	 * @return
	 */
	public static boolean hasAtLeastOneParent(Class classe) {
		return !getIdentifyingParentAssociations(classe).isEmpty();
	}

	/**
	 * Check if the input class has at least a properties annotated as ID.
	 * 
	 * @author Federico Gambardella<federico.gambardella@micegroup.it>
	 * @param classe
	 * @return
	 */
	public static boolean hasAtLeastAPorpertyAnnotatedAsID(Class classe) {
		return !getClassIDs(classe).isEmpty();
	}

	/**
	 * Ritorna true se la classe appartiene alla subject area principale
	 * 
	 * @author Vittorio Niespolo <vittorio.niespolo@micegroup.it>
	 *
	 * @param classe
	 * @return
	 */
	public static boolean belongsToMainSubjectArea(Class classe) {
		if (classe.getOwner() instanceof Package) {
			return false;
		}
		return true;
	}
}
