/*
 * Hibernate, Relational Persistence for Idiomatic Java
 *
 * License: GNU Lesser General Public License (LGPL), version 2.1 or later.
 * See the lgpl.txt file in the root directory or <http://www.gnu.org/licenses/lgpl-2.1.html>.
 */
package org.hibernate.boot.model.source.internal.hbm;

import java.util.ArrayList;
import java.util.List;
import java.util.Map;

import org.hibernate.EntityMode;
import org.hibernate.boot.jaxb.hbm.spi.JaxbHbmCompositeIdType;
import org.hibernate.boot.model.IdentifierGeneratorDefinition;
import org.hibernate.boot.model.JavaTypeDescriptor;
import org.hibernate.boot.model.source.spi.AttributePath;
import org.hibernate.boot.model.source.spi.AttributeRole;
import org.hibernate.boot.model.source.spi.AttributeSource;
import org.hibernate.boot.model.source.spi.AttributeSourceContainer;
import org.hibernate.boot.model.source.spi.EmbeddableSource;
import org.hibernate.boot.model.source.spi.IdentifierSourceNonAggregatedComposite;
import org.hibernate.boot.model.source.spi.LocalMetadataBuildingContext;
import org.hibernate.boot.model.source.spi.SingularAttributeSource;
import org.hibernate.boot.model.source.spi.ToolingHintContext;
import org.hibernate.id.EntityIdentifierNature;
import org.hibernate.internal.util.StringHelper;

/**
 * Models a composite identifier with is not not encapsulated in a dedicated "id class".
 *
 * @author Steve Ebersole
 */
class IdentifierSourceNonAggregatedCompositeImpl implements IdentifierSourceNonAggregatedComposite, EmbeddableSource {
	private final RootEntitySourceImpl rootEntitySource;

	private final AttributePath attributePathBase;
	private final AttributeRole attributeRoleBase;
	private final IdentifierGeneratorDefinition generatorDefinition;

	// NOTE: not typed because we need to expose as both:
	// 		List<AttributeSource>
	//		List<SingularAttributeSource>
	// :(
	private final List attributeSources;

	private final EmbeddableSource idClassSource;
	private final ToolingHintContext toolingHintContext;

	IdentifierSourceNonAggregatedCompositeImpl(RootEntitySourceImpl rootEntitySource) {
		this.rootEntitySource = rootEntitySource;

		this.attributePathBase = rootEntitySource.getAttributePathBase().append( "<id>" );
		this.attributeRoleBase = rootEntitySource.getAttributeRoleBase().append( "<id>" );
		this.generatorDefinition = EntityHierarchySourceImpl.interpretGeneratorDefinition(
				rootEntitySource.sourceMappingDocument(),
				rootEntitySource.getEntityNamingSource(),
				rootEntitySource.jaxbEntityMapping().getCompositeId().getGenerator()
		);

		this.attributeSources = new ArrayList();
		AttributesHelper.processCompositeKeySubAttributes(
				rootEntitySource.sourceMappingDocument(),
				new AttributesHelper.Callback() {
					@Override
					public AttributeSourceContainer getAttributeSourceContainer() {
						return IdentifierSourceNonAggregatedCompositeImpl.this;
					}

					@Override
					@SuppressWarnings("unchecked")
					public void addAttributeSource(AttributeSource attributeSource) {
						attributeSources.add( attributeSource );
					}
				},
				rootEntitySource.jaxbEntityMapping().getCompositeId().getKeyPropertyOrKeyManyToOne()
		);

		// NOTE : the HBM support for IdClass is very limited.  Essentially
		// we assume that all identifier attributes occur in the IdClass
		// using the same name and type.
		this.idClassSource = interpretIdClass(
				rootEntitySource.sourceMappingDocument(),
				rootEntitySource.jaxbEntityMapping().getCompositeId()
		);

		this.toolingHintContext = Helper.collectToolingHints(
				rootEntitySource.getToolingHintContext(),
				rootEntitySource.jaxbEntityMapping().getCompositeId()
		);
	}

	private EmbeddableSource interpretIdClass(
			MappingDocument mappingDocument,
			JaxbHbmCompositeIdType jaxbHbmCompositeIdMapping) {
		// if <composite-id/> is null here we have much bigger problems :)

		if ( !jaxbHbmCompositeIdMapping.isMapped() ) {
			return null;
		}

		final String className = jaxbHbmCompositeIdMapping.getClazz();
		if ( StringHelper.isEmpty( className ) ) {
			return null;
		}

		final String idClassQualifiedName = mappingDocument.qualifyClassName( className );
		final JavaTypeDescriptor idClassTypeDescriptor = new JavaTypeDescriptor() {
			@Override
			public String getName() {
				return idClassQualifiedName;
			}
		};
		return new IdClassSource( idClassTypeDescriptor, rootEntitySource, mappingDocument );
	}

	@Override
	@SuppressWarnings("unchecked")
	public List<SingularAttributeSource> getAttributeSourcesMakingUpIdentifier() {
		return attributeSources;
	}


	@Override
	public EmbeddableSource getIdClassSource() {
		return idClassSource;
	}

	@Override
	public IdentifierGeneratorDefinition getIndividualAttributeIdGenerator(String identifierAttributeName) {
		// for now, return null.  this is that stupid specj bs
		return null;
	}

	@Override
	public IdentifierGeneratorDefinition getIdentifierGeneratorDescriptor() {
		return generatorDefinition;
	}

	@Override
	public EntityIdentifierNature getNature() {
		return EntityIdentifierNature.NON_AGGREGATED_COMPOSITE;
	}

	@Override
	public JavaTypeDescriptor getTypeDescriptor() {
		return null;
	}

	@Override
	public String getParentReferenceAttributeName() {
		return null;
	}

	@Override
	public Map<EntityMode, String> getTuplizerClassMap() {
		return null;
	}

	@Override
	public boolean isDynamic() {
		return false;
	}

	@Override
	public boolean isUnique() {
		return false;
	}

	@Override
	public AttributePath getAttributePathBase() {
		return attributePathBase;
	}

	@Override
	public AttributeRole getAttributeRoleBase() {
		return attributeRoleBase;
	}

	@Override
	@SuppressWarnings("unchecked")
	public List<AttributeSource> attributeSources() {
		return attributeSources;
	}

	@Override
	public LocalMetadataBuildingContext getLocalMetadataBuildingContext() {
		return rootEntitySource.metadataBuildingContext();
	}

	@Override
	public EmbeddableSource getEmbeddableSource() {
		return this;
	}

	@Override
	public ToolingHintContext getToolingHintContext() {
		return toolingHintContext;
	}
}
