/*
 * 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.hql.internal.ast.tree;

import org.hibernate.engine.internal.JoinSequence;
import org.hibernate.engine.spi.SessionFactoryImplementor;
import org.hibernate.hql.internal.antlr.HqlSqlTokenTypes;
import org.hibernate.hql.internal.ast.HqlSqlWalker;
import org.hibernate.hql.internal.ast.util.SessionFactoryHelper;
import org.hibernate.persister.collection.QueryableCollection;
import org.hibernate.persister.entity.EntityPersister;
import org.hibernate.persister.entity.Joinable;
import org.hibernate.sql.JoinType;
import org.hibernate.type.EntityType;
import org.hibernate.type.Type;

/**
 * @author Steve Ebersole
 */
public class MapKeyEntityFromElement extends FromElement {
	private final boolean useThetaJoin;

	public MapKeyEntityFromElement(boolean useThetaJoin) {
		super();
		this.useThetaJoin = useThetaJoin;
	}

	@Override
	public boolean isImplied() {
		return useThetaJoin;
	}

	@Override
	public int getType() {
		return useThetaJoin ? HqlSqlTokenTypes.FROM_FRAGMENT : HqlSqlTokenTypes.JOIN_FRAGMENT;
	}

	public static MapKeyEntityFromElement buildKeyJoin(FromElement collectionFromElement) {
		final HqlSqlWalker walker = collectionFromElement.getWalker();
		final SessionFactoryHelper sfh = walker.getSessionFactoryHelper();
		final SessionFactoryImplementor sf = sfh.getFactory();

		final QueryableCollection collectionPersister = collectionFromElement.getQueryableCollection();
		final Type indexType = collectionPersister.getIndexType();
		if ( indexType == null ) {
			throw new IllegalArgumentException( "Given collection is not indexed" );
		}
		if ( !indexType.isEntityType() ) {
			throw new IllegalArgumentException( "Given collection does not have an entity index" );
		}

		final EntityType indexEntityType = (EntityType) indexType;
		final EntityPersister indexEntityPersister = (EntityPersister) indexEntityType.getAssociatedJoinable( sf );

		final String rhsAlias = walker.getAliasGenerator().createName( indexEntityPersister.getEntityName() );
		final boolean useThetaJoin = collectionFromElement.getJoinSequence().isThetaStyle();

		MapKeyEntityFromElement join = new MapKeyEntityFromElement( useThetaJoin );
		join.initialize( HqlSqlTokenTypes.JOIN_FRAGMENT, ( (Joinable) indexEntityPersister ).getTableName() );
		join.initialize( collectionFromElement.getWalker() );

		join.initializeEntity(
				collectionFromElement.getFromClause(),
				indexEntityPersister.getEntityName(),
				indexEntityPersister,
				indexEntityType,
				"<map-key-join-" + collectionFromElement.getClassAlias() + ">",
				rhsAlias
		);

//		String[] joinColumns = determineJoinColuns( collectionPersister, joinTableAlias );
		// todo : assumes columns, no formulas
		String[] joinColumns = collectionPersister.getIndexColumnNames( collectionFromElement.getCollectionTableAlias() );

		JoinSequence joinSequence = sfh.createJoinSequence(
				useThetaJoin,
				indexEntityType,
				rhsAlias,
				// todo : ever a time when INNER is appropriate?
				//JoinType.LEFT_OUTER_JOIN,
				// needs to be an inner join because of how JoinSequence/JoinFragment work - ugh
//				JoinType.INNER_JOIN,
				collectionFromElement.getJoinSequence().getFirstJoin().getJoinType(),
				joinColumns
		);
		join.setJoinSequence( joinSequence );

		join.setOrigin( collectionFromElement, true );
		join.setColumns( joinColumns );

		join.setUseFromFragment( collectionFromElement.useFromFragment() );
		join.setUseWhereFragment( collectionFromElement.useWhereFragment() );

		walker.addQuerySpaces( indexEntityPersister.getQuerySpaces() );

		return join;
	}
}
