package eu.dnetlib.dlms.epub;

import java.util.HashMap;
import java.util.Map;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.beans.factory.annotation.Required;

import eu.dnetlib.dlms.lowlevel.objects.structures.IntBasicValue;
import eu.dnetlib.dlms.lowlevel.types.RelationMultiplicity;
import eu.dnetlib.dlms.lowlevel.types.RelationPartiality;
import eu.dnetlib.dlms.lowlevel.types.structures.BasicDescriptionType;
import eu.dnetlib.dlms.lowlevel.types.structures.BasicType;
import eu.dnetlib.dlms.lowlevel.types.structures.LabelType;

/**
 * Class for the generation of the second proposal for Enhanced Publication Data Model.
 * 
 * @author lexis
 */
public class EPubModelGenerator {
	/** logger. */
	private static final Log log = LogFactory.getLog(EPubModelGenerator.class); // NOPMD by marko on 11/24/08 5:02 PM

	/** Instance to use to generate types. */
	private TypeGenerator typeGenerator;
	/** Instance to use to generate sets. */
	private SetGenerator setGenerator;

	/** Names of the structure types to generate. */
	private String ePubsTypeName, ePrintsTypeName, nonEPrintsTypeName, researchDataTypeName, aggregationsTypeName, sequenceTypeName, componentsTypeName;
	/** Instances that represent the structure sets to create. */
	private SetToCreate ePubsSet, ePrintsSet, nonEPrintsSet, researchDataSet, aggregationObjectSet, sequenceSet, componentsUnionSet;

	/** Names of the relation types to generate. */
	private String relatedWithTypeName, citesTypeName, hasEPrintTypeName, composedByTypeName, orderTypeName, generatedByTypename, aggregatesTypeName;
	/** Instances that represent the relation sets to create. */
	private RelationSetToCreate relatedWithSet, citesSet, hasEPrintSet, composedBySet, orderSet, generatedBySet, aggregatesSet;
	/**
	 * True if you want the model to be generated by the generateModel() method. False otherwise.
	 */
	private boolean modelGenerationOn;

	/**
	 * Generate the EnhancedPublication data model.
	 */
	public void generateModel() {
		log.info("modelGenerationOn is " + this.modelGenerationOn);
		if (this.modelGenerationOn) {
			generateStructureTypes();
			generateComponentsUnionType();
			generateRelationTypes();
			generateSets();
		} else
			log.debug("No generation of ePubs data model occured");
	}

	/**
	 * Generates the types as following: <code>
	 * Type ePrintsType = struct([dmf1:type1; ...dmf32:type32]);
	 * Type nonEPrintsType = struct([dmf1:type1; ...dmf32:type32]);
	 * Type ePubsType = struct([dmf1:type1; ...dmf32:type32]);
	 * Type ResearchDataType = struct([dmf1:type1; ...dmf32:type32]);
	 * Type AggregationObjectType = struct([dmf1:type1; ...dmf32:type32]);
	 * Type SequenceType = struct([sequenceNo:int]);
	 * </code>.
	 */
	private void generateStructureTypes() {
		this.typeGenerator.generateDMFType(this.ePubsTypeName);
		this.typeGenerator.generateDMFType(this.ePrintsTypeName);
		this.typeGenerator.generateDMFType(this.nonEPrintsTypeName);
		this.typeGenerator.generateDMFType(this.researchDataTypeName);
		this.typeGenerator.generateDMFType(this.aggregationsTypeName);
		//then the type for structure used for sequential ordering:
		BasicDescriptionType def = new BasicDescriptionType();
		Map<String, LabelType> defMap = new HashMap<String, LabelType>();
		defMap.put("sequenceNo", new BasicType(IntBasicValue.class));
		def.setDescriptionDefinitionMap(defMap);
		this.typeGenerator.generateStructureType(this.sequenceTypeName, def);
	}

	/**
	 * Generates the UnionType for Components:
	 * <code>Type ComponentsUnionType = union(ePrintsType, non-ePrintsType, ResearchDataType, AggregationObjectType);</code>
	 * .
	 */
	private void generateComponentsUnionType() {
		this.typeGenerator.generateUnionType(this.componentsTypeName, this.ePrintsTypeName, this.nonEPrintsTypeName, this.researchDataTypeName,
				this.aggregationsTypeName);
	}

	/**
	 * Generates the types for relationships among sets. The types are those related to the following sets: <code>
	 * Set relatedWith = create rel(ePubs, ePubs, n:m, p:p);
	 * Set cites = create rel(ePrints, ePrints, n:m, p:p);
	 * Set hasEPrint = create rel(ePubs, ePrints, n:1, t:p);
	 * Set composedBy = create rel(ePubs, Components, n:m, p:p);
	 * Set order = create rel(aggregates, Sequence, 1:1, p:t);
	 * Set generatedBy = create rel(ResearchData, ResearchData, n:m, p:p);
	 * Set aggregates = create rel(AggregationObjects, Components, n:m, p:p);
	 * </code>. Note that the DOL code reports also the creation of sets, which is done in this.generateSets() method
	 * instead.
	 */
	private void generateRelationTypes() {
		this.typeGenerator.generateRelationType(this.relatedWithTypeName, this.ePubsTypeName, this.ePubsTypeName, RelationMultiplicity.many_to_many,
				RelationPartiality.part_part);
		this.typeGenerator.generateRelationType(this.citesTypeName, this.ePrintsTypeName, this.ePrintsTypeName, RelationMultiplicity.many_to_many,
				RelationPartiality.part_part);
		this.typeGenerator.generateRelationType(this.hasEPrintTypeName, this.ePubsTypeName, this.ePrintsTypeName, RelationMultiplicity.many_to_one,
				RelationPartiality.tot_part);
		this.typeGenerator.generateRelationType(this.composedByTypeName, this.ePubsTypeName, this.componentsTypeName, RelationMultiplicity.many_to_many,
				RelationPartiality.part_part);
		this.typeGenerator.generateRelationType(this.generatedByTypename, this.researchDataTypeName, this.researchDataTypeName,
				RelationMultiplicity.many_to_many, RelationPartiality.part_part);
		this.typeGenerator.generateRelationType(this.aggregatesTypeName, this.aggregationsTypeName, this.componentsTypeName,
				RelationMultiplicity.many_to_many, RelationPartiality.part_part);
		this.typeGenerator.generateRelationType(this.orderTypeName, this.aggregatesTypeName, this.sequenceTypeName, RelationMultiplicity.one_to_one,
				RelationPartiality.part_tot);
	}

	/**
	 * Generates the Sets as following: <code>
	 * Set ePubs = create ePubsType;
	 * Set ePrints = create ePrintsType;
	 * Set non-ePrints = create nonEPrintsType;
	 * Set AggregationObjects = create AggregationType;
	 * Set ResearchData = create ResearchDataType; 
	 * Set Sequence = create SequenceType;
	 * Set Components = create ComponentUnionType(ePrints, non-ePrints, AggregationObjects, ResearchData);
	 * </code> The following are sets used to contain relations: <code>
	 * Set relatedWith = create rel(ePubs, ePubs, n:m, p:p);
	 * Set cites = create rel(ePrints, ePrints, n:m, p:p);
	 * Set hasEPrint = create rel(ePubs, ePrints, n:1, t:p);
	 * Set composedBy = create rel(ePubs, Components, n:m, p:p);
	 * Set order = create rel(aggregates, Sequence, 1:1, p:t);
	 * Set generatedBy = create rel(ResearchData, ResearchData, n:m, p:p);
	 * Set aggregates = create rel(AggregationObjects, Components, n:m, p:p);
	 * </code> Note that this method does not generate the types for relation sets, just the sets.
	 */
	private void generateSets() {
		this.setGenerator.generateSets(this.ePubsSet, this.ePrintsSet, this.nonEPrintsSet, this.researchDataSet, this.aggregationObjectSet,
				this.sequenceSet);
		this.setGenerator.generateUnionSet(this.componentsUnionSet, this.ePrintsSet, this.nonEPrintsSet, this.researchDataSet, this.aggregationObjectSet);
		this.setGenerator.generateRelationSet(this.relatedWithSet, this.citesSet, this.hasEPrintSet, this.composedBySet, this.generatedBySet,
				this.aggregatesSet, this.orderSet);
	}

	public TypeGenerator getTypeGenerator() {
		return this.typeGenerator;
	}

	@Required
	public void setTypeGenerator(final TypeGenerator typeGenerator) {
		this.typeGenerator = typeGenerator;
	}

	public String getEPubsTypeName() {
		return this.ePubsTypeName;
	}

	@Required
	public void setEPubsTypeName(final String pubsTypeName) {
		this.ePubsTypeName = pubsTypeName;
	}

	public String getEPrintsTypeName() {
		return this.ePrintsTypeName;
	}

	@Required
	public void setEPrintsTypeName(final String printsTypeName) {
		this.ePrintsTypeName = printsTypeName;
	}

	public String getResearchDataTypeName() {
		return this.researchDataTypeName;
	}

	@Required
	public void setResearchDataTypeName(final String researchDataTypeName) {
		this.researchDataTypeName = researchDataTypeName;
	}

	public String getAggregationsTypeName() {
		return this.aggregationsTypeName;
	}

	@Required
	public void setAggregationsTypeName(final String compoundObjectTypeName) {
		this.aggregationsTypeName = compoundObjectTypeName;
	}

	public String getNonEPrintsTypeName() {
		return this.nonEPrintsTypeName;
	}

	@Required
	public void setNonEPrintsTypeName(final String nonEPrintsTypeName) {
		this.nonEPrintsTypeName = nonEPrintsTypeName;
	}

	public String getComponentsTypeName() {
		return this.componentsTypeName;
	}

	@Required
	public void setComponentsTypeName(final String componentsTypeName) {
		this.componentsTypeName = componentsTypeName;
	}

	public SetGenerator getSetGenerator() {
		return this.setGenerator;
	}

	@Required
	public void setSetGenerator(final SetGenerator setGenerator) {
		this.setGenerator = setGenerator;
	}

	public String getSequenceTypeName() {
		return this.sequenceTypeName;
	}

	@Required
	public void setSequenceTypeName(final String sequenceTypeName) {
		this.sequenceTypeName = sequenceTypeName;
	}

	public SetToCreate getEPubsSet() {
		return this.ePubsSet;
	}

	@Required
	public void setEPubsSet(final SetToCreate pubsSet) {
		this.ePubsSet = pubsSet;
	}

	public SetToCreate getEPrintsSet() {
		return this.ePrintsSet;
	}

	@Required
	public void setEPrintsSet(final SetToCreate printsSet) {
		this.ePrintsSet = printsSet;
	}

	public SetToCreate getResearchDataSet() {
		return this.researchDataSet;
	}

	@Required
	public void setResearchDataSet(final SetToCreate reserachDataSet) {
		this.researchDataSet = reserachDataSet;
	}

	public SetToCreate getAggregationObjectSet() {
		return this.aggregationObjectSet;
	}

	@Required
	public void setAggregationObjectSet(final SetToCreate aggrObjectSet) {
		this.aggregationObjectSet = aggrObjectSet;
	}

	public SetToCreate getSequenceSet() {
		return this.sequenceSet;
	}

	@Required
	public void setSequenceSet(final SetToCreate sequenceSet) {
		this.sequenceSet = sequenceSet;
	}

	public SetToCreate getNonEPrintsSet() {
		return this.nonEPrintsSet;
	}

	@Required
	public void setNonEPrintsSet(final SetToCreate nonEPrintsSet) {
		this.nonEPrintsSet = nonEPrintsSet;
	}

	public SetToCreate getComponentsUnionSet() {
		return this.componentsUnionSet;
	}

	@Required
	public void setComponentsUnionSet(final SetToCreate componentsUnionSet) {
		this.componentsUnionSet = componentsUnionSet;
	}

	public String getRelatedWithTypeName() {
		return this.relatedWithTypeName;
	}

	@Required
	public void setRelatedWithTypeName(final String relatedWithTypeName) {
		this.relatedWithTypeName = relatedWithTypeName;
	}

	public String getCitesTypeName() {
		return this.citesTypeName;
	}

	@Required
	public void setCitesTypeName(final String citesTypeName) {
		this.citesTypeName = citesTypeName;
	}

	public String getHasEPrintTypeName() {
		return this.hasEPrintTypeName;
	}

	@Required
	public void setHasEPrintTypeName(final String hasEPrintTypeName) {
		this.hasEPrintTypeName = hasEPrintTypeName;
	}

	public String getComposedByTypeName() {
		return this.composedByTypeName;
	}

	@Required
	public void setComposedByTypeName(final String composedByTypeName) {
		this.composedByTypeName = composedByTypeName;
	}

	public String getOrderTypeName() {
		return this.orderTypeName;
	}

	@Required
	public void setOrderTypeName(final String orderTypeName) {
		this.orderTypeName = orderTypeName;
	}

	public String getGeneratedByTypename() {
		return this.generatedByTypename;
	}

	@Required
	public void setGeneratedByTypename(final String generatedByTypename) {
		this.generatedByTypename = generatedByTypename;
	}

	public String getAggregatesTypeName() {
		return this.aggregatesTypeName;
	}

	@Required
	public void setAggregatesTypeName(final String aggregatesTypename) {
		this.aggregatesTypeName = aggregatesTypename;
	}

	public RelationSetToCreate getRelatedWithSet() {
		return this.relatedWithSet;
	}

	@Required
	public void setRelatedWithSet(final RelationSetToCreate relatedWithSet) {
		this.relatedWithSet = relatedWithSet;
	}

	public RelationSetToCreate getCitesSet() {
		return this.citesSet;
	}

	@Required
	public void setCitesSet(final RelationSetToCreate citesset) {
		this.citesSet = citesset;
	}

	public RelationSetToCreate getHasEPrintSet() {
		return this.hasEPrintSet;
	}

	@Required
	public void setHasEPrintSet(final RelationSetToCreate hasEPrintSet) {
		this.hasEPrintSet = hasEPrintSet;
	}

	public RelationSetToCreate getComposedBySet() {
		return this.composedBySet;
	}

	@Required
	public void setComposedBySet(final RelationSetToCreate composedBySet) {
		this.composedBySet = composedBySet;
	}

	public RelationSetToCreate getOrderSet() {
		return this.orderSet;
	}

	@Required
	public void setOrderSet(final RelationSetToCreate orderSet) {
		this.orderSet = orderSet;
	}

	public RelationSetToCreate getGeneratedBySet() {
		return this.generatedBySet;
	}

	@Required
	public void setGeneratedBySet(final RelationSetToCreate generatedBySet) {
		this.generatedBySet = generatedBySet;
	}

	public RelationSetToCreate getAggregatesSet() {
		return this.aggregatesSet;
	}

	@Required
	public void setAggregatesSet(final RelationSetToCreate aggregatesSet) {
		this.aggregatesSet = aggregatesSet;
	}

	@Required
	public void setModelGenerationOn(final boolean modelGenerationOn) {
		this.modelGenerationOn = modelGenerationOn;
	}

	public boolean isModelGenerationOn() {
		return this.modelGenerationOn;
	}

}
