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

import java.util.ArrayList;
import java.util.Collection;
import java.util.List;

import org.eclipse.emf.ecore.impl.DynamicEObjectImpl;
import org.eclipse.uml2.uml.Actor;
import org.eclipse.uml2.uml.Association;
import org.eclipse.uml2.uml.Class;
import org.eclipse.uml2.uml.Model;
import org.eclipse.uml2.uml.Property;
import org.eclipse.uml2.uml.Stereotype;
import org.eclipse.uml2.uml.Type;
import org.eclipse.uml2.uml.UseCase;

public class FeatureUtils {

	/**
	 * Return a set of Generic Features belonging to model
	 * 
	 * @author Vittorio Niespolo <vittorio.niespolo@micegroup.it>
	 * @param model
	 * @return set of generic Feature
	 */
	public Collection<UseCase> getGenericFeatures(Model model) {
		Collection<UseCase> features = ModelUtils.getFeatures(model);
		Collection<UseCase> genericFeatures = new ArrayList<>();

		for (UseCase feature : features) {
			if (isGenericFeatureStereotype(feature)) {
				genericFeatures.add(feature);
			}
		}
		return genericFeatures;
	}

	/**
	 * Return a set of Entity Specific Features belonging to model
	 * 
	 * @author Vittorio Niespolo <vittorio.niespolo@micegroup.it>
	 * @param model
	 * @return set of Entity Specific Features
	 */
	public static Collection<UseCase> getEntitySpecificFeatures(Model model) {
		Collection<UseCase> features = ModelUtils.getFeatures(model);
		Collection<UseCase> entitySpecificfeatures = new ArrayList<>();

		for (UseCase feature : features) {
			if (isEntitySpecificFeatureStereotype(feature)) {
				entitySpecificfeatures.add(feature);
			}
		}
		return entitySpecificfeatures;
	}

	/**
	 * Return the Generator Actor from the model
	 * 
	 * @param model
	 * @return actor Generator
	 */
	public static Actor getGenerator(Model model) {
		for (org.eclipse.uml2.uml.Package pack : model.getImportedPackages()) {
			if (pack != null) {
				for (Type type : pack.getOwnedTypes()) {
					if (type instanceof Actor && "Generator".equals(type.getName())) {
						return (Actor) type;
					}
				}
			}
		}

		for (Type type : model.getOwnedTypes()) {
			if (type instanceof Actor && "Generator".equals(type.getName())) {
				return (Actor) type;
			}
		}
		return null;
	}

	/**
	 * Return true if a Feature is Linked to Generator
	 * 
	 * @author Vittorio Niespolo <vittorio.niespolo@micegroup.it>
	 * @param model
	 * @param feature
	 * @return
	 */
	public static boolean isFeatureLinkedToGenerator(Model model, UseCase feature) {
		for (Association association : feature.getAssociations()) {
			for (Property property : association.getMemberEnds()) {
				if (property.getType() == getGenerator(model)) {
					return true;
				}
			}
		}
		return false;
	}

	/**
	 * Return the Set of EntitySpecificFeature's Entities
	 * 
	 * @author Vittorio Niespolo <vittorio.niespolo@micegroup.it>
	 * @param feature
	 * @return Set of entities
	 */
	@SuppressWarnings("unchecked")
	public static Collection<Class> getEntitySpecificFeatureEntities(UseCase feature) {
		Stereotype stereotype;
		Collection<Class> entities = new ArrayList<>();

		stereotype = feature.getAppliedStereotype(StereotypeName.ENTITY_SPECIFIC_FEATURE);

		if (stereotype != null) {

			List<DynamicEObjectImpl> entitiesDEOI = (List<DynamicEObjectImpl>) feature.getValue(stereotype,
					StereotypePropertyName.ENTITIES);

			for (DynamicEObjectImpl deoi : entitiesDEOI) {
				for (Object crossReference : deoi.eCrossReferences()) {
					if (crossReference instanceof Class) {
						entities.add((Class) crossReference);
					}
				}
			}
		}
		return entities;
	}

	/**
	 * Return true if a GenericFeature is enabled. So it exists in the model and is
	 * linked to the generator
	 * 
	 * @author Vittorio Niespolo <vittorio.niespolo@micegroup.it>
	 * @param model
	 * @param genericFeature
	 * @return
	 */
	protected boolean isGenericFeatureEnabled(Model model, GenericFeature genericFeature) {
		Collection<UseCase> genericFeatures = getGenericFeatures(model);

		for (UseCase feature : genericFeatures) {
			if (feature.getName().equals(genericFeature.name()) && isFeatureLinkedToGenerator(model, feature)) {
				return true;
			}
		}
		return false;
	}

	/**
	 * Return true if a GenericFeature is enabled. So it exists in the model and is
	 * linked to the generator
	 * 
	 * @author Vittorio Niespolo <vittorio.niespolo@micegroup.it>
	 * @param model
	 * @param specificFeature
	 * @return
	 */
	protected boolean isEntitySpecificFeatureEnabled(Model model, GenericFeature specificFeature) {
		Collection<UseCase> specificFeatures = getEntitySpecificFeatures(model);

		for (UseCase feature : specificFeatures) {
			if (feature.getName().equals(specificFeature.name()) && isFeatureLinkedToGenerator(model, feature)) {
				return true;
			}
		}
		return false;
	}

	/**
	 * Return true if a EntitySpecificFeature is enabled. So it exists in the model
	 * and is linked to the generator
	 * 
	 * @author Vittorio Niespolo <vittorio.niespolo@micegroup.it>
	 * @param model
	 * @param entitySpecificFeature
	 * @param entity
	 * @return
	 */
	protected static boolean isEntitySpecificFeatureEnabled(Model model, EntitySpecificFeature entitySpecificFeature,
			Class entity) {
		Collection<UseCase> entitySpecificFeatures = getEntitySpecificFeatures(model);

		for (UseCase feature : entitySpecificFeatures) {
			if (feature.getName().equals(entitySpecificFeature.name()) && isFeatureLinkedToGenerator(model, feature)) {
				// VN Check if Feature use-case contains specified entity
				Collection<Class> entities = getEntitySpecificFeatureEntities(feature);
				if (entities.contains(entity)) {
					return true;
				}
			}
		}
		return false;
	}

	/**
	 * Return true if Attachment feature is enabled
	 * 
	 * @author Vittorio Niespolo <vittorio.niespolo@micegroup.it>
	 * @param model
	 * @param entity
	 * @return
	 */
	public boolean isAttachmentEnabled(Class entity, Model model) {
		if (isEntitySpecificFeatureEnabled(model, EntitySpecificFeature.Attachment, entity)) {
			return true;
		}
		return false;
	}

	/**
	 * Return true if Email feature is enabled
	 * 
	 * @author Vittorio Niespolo <vittorio.niespolo@micegroup.it>
	 * @param model
	 * @return
	 */
	public boolean isEmailEnabled(Model model) {
		if (isGenericFeatureEnabled(model, GenericFeature.Email)) {
			return true;
		}
		return false;
	}

	/**
	 * Return true if Email feature is enabled
	 * 
	 * @author Vittorio Niespolo <vittorio.niespolo@micegroup.it>
	 * @param model
	 * @return
	 */
	public boolean isAttachmentEnabled(Model model) {
		if (isEntitySpecificFeatureEnabled(model, GenericFeature.Attachment)) {
			return true;
		}
		return false;
	}

	/**
	 * Return true if Security feature is enabled
	 * 
	 * @author Vittorio Niespolo <vittorio.niespolo@micegroup.it>
	 * @param model
	 * @return
	 */
	public boolean isSecurityEnabled(Model model) {
		if (isGenericFeatureEnabled(model, GenericFeature.Security)) {
			return true;
		}
		return false;
	}

	/**
	 * Return true if Caching feature is enabled
	 * 
	 * @author Vittorio Niespolo <vittorio.niespolo@micegroup.it>
	 * @param model
	 * @return
	 */
	public boolean isCachingEnabled(Model model) {
		if (isGenericFeatureEnabled(model, GenericFeature.Caching)) {
			return true;
		}
		return false;
	}

	/**
	 * Return true if the useCase has applied the Feature Stereotype
	 * 
	 * @author Vittorio Niespolo <vittorio.niespolo@micegroup.it>
	 * @param usecase
	 * @return true if the usecase has applied the Feature Stereotype
	 */
	public static boolean isFeatureStereotype(UseCase useCase) {
		Stereotype stereotype = (Stereotype) useCase.getGeneral(StereotypeName.FEATURE);
		if (stereotype != null)
			return true;
		return false;
	}

	/**
	 * Return true if the useCase has applied the Feature Stereotype
	 * 
	 * @author Vittorio Niespolo <vittorio.niespolo@micegroup.it>
	 * @param usecase
	 * @return true if the usecase has applied the Feature Stereotype
	 */
	public static boolean isEntitySpecificFeatureStereotype(UseCase useCase) {
		Stereotype stereotype = useCase.getAppliedStereotype(StereotypeName.ENTITY_SPECIFIC_FEATURE);
		if (stereotype != null)
			return true;
		return false;
	}

	/**
	 * Return true if the useCase has applied the Feature Stereotype
	 * 
	 * @author Vittorio Niespolo <vittorio.niespolo@micegroup.it>
	 * @param usecase
	 * @return true if the usecase has applied the Feature Stereotype
	 */
	public static boolean isGenericFeatureStereotype(UseCase useCase) {
		Stereotype stereotype = useCase.getAppliedStereotype(StereotypeName.GENERIC_FEATURE);
		if (stereotype != null)
			return true;
		return false;
	}

	public static boolean isReportEnabled(Class entity, Model model) {
		return isEntitySpecificFeatureEnabled(model, EntitySpecificFeature.BasicPdfCsvExport, entity);
	}
}
