/*
 * 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.test.stats;

import java.util.HashSet;
import java.util.Iterator;

import org.hibernate.Hibernate;
import org.hibernate.ScrollableResults;
import org.hibernate.Session;
import org.hibernate.SessionFactory;
import org.hibernate.Transaction;
import org.hibernate.cfg.AvailableSettings;
import org.hibernate.cfg.Configuration;
import org.hibernate.stat.QueryStatistics;

import org.hibernate.testing.junit4.BaseUnitTestCase;
import org.junit.Test;

import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNotNull;

/**
 * Show the difference between fetch and load
 *
 * @author Emmanuel Bernard
 */
public class StatsTest extends BaseUnitTestCase {
	public String[] getMappings() {
		return new String[] { "stats/Continent.hbm.xml" };
	}

	private Configuration buildBaseConfiguration() {
		return new Configuration()
				.addResource( "org/hibernate/test/stats/Continent.hbm.xml" )
				.setProperty( AvailableSettings.GENERATE_STATISTICS, "true" );
	}
//
//	@Test
//	@SuppressWarnings( {"UnusedAssignment"})
//	public void testCollectionFetchVsLoad() throws Exception {
//		SessionFactory sf = buildBaseConfiguration()
//				.setProperty( AvailableSettings.HBM2DDL_AUTO, "create-drop" )
//				.buildSessionFactory();
//
//		Session s = sf.openSession();
//		Transaction tx = s.beginTransaction();
//		Continent europe = fillDb(s);
//		tx.commit();
//		s.close();
//
//		s = sf.openSession();
//		tx = s.beginTransaction();
//		assertEquals(0, sf.getStatistics().getCollectionLoadCount() );
//		assertEquals(0,  sf.getStatistics().getCollectionFetchCount() );
//		Continent europe2 = (Continent) s.get( Continent.class, europe.getId() );
//		assertEquals("Lazy true: no collection should be loaded", 0, sf.getStatistics().getCollectionLoadCount() );
//		assertEquals( 0, sf.getStatistics().getCollectionFetchCount() );
//		europe2.getCountries().size();
//		assertEquals( 1, sf.getStatistics().getCollectionLoadCount() );
//		assertEquals("Explicit fetch of the collection state", 1, sf.getStatistics().getCollectionFetchCount() );
//		tx.commit();
//		s.close();
//
//		sf.getStatistics().clear();
//
//		s = sf.openSession();
//		tx = s.beginTransaction();
//		europe = fillDb(s);
//		tx.commit();
//		s.clear();
//		tx = s.beginTransaction();
//		assertEquals( 0, sf.getStatistics().getCollectionLoadCount() );
//		assertEquals( 0, sf.getStatistics().getCollectionFetchCount() );
//		europe2 = (Continent) s.createQuery(
//				"from " + Continent.class.getName() + " a join fetch a.countries where a.id = " + europe.getId()
//			).uniqueResult();
//		assertEquals( 1, sf.getStatistics().getCollectionLoadCount() );
//		assertEquals( "collection should be loaded in the same query as its parent", 0, sf.getStatistics().getCollectionFetchCount() );
//		tx.commit();
//		s.close();
//
//		// open a new SF
//		sf.close();
//		Configuration cfg = buildBaseConfiguration().setProperty( AvailableSettings.HBM2DDL_AUTO, "create-drop" );
//		cfg.buildMappings();
//		Collection coll = cfg.getCollectionMapping(Continent.class.getName() + ".countries");
//		coll.setFetchMode(FetchMode.JOIN);
//		coll.setLazy(false);
//		sf = cfg.buildSessionFactory();
//
//		s = sf.openSession();
//		tx = s.beginTransaction();
//		europe = fillDb(s);
//		tx.commit();
//		s.close();
//
//		s = sf.openSession();
//		tx = s.beginTransaction();
//		assertEquals( 0, sf.getStatistics().getCollectionLoadCount() );
//		assertEquals( 0, sf.getStatistics().getCollectionFetchCount() );
//		europe2 = (Continent) s.get( Continent.class, europe.getId() );
//		assertEquals( 1, sf.getStatistics().getCollectionLoadCount() );
//		assertEquals( "Should do direct load, not indirect second load when lazy false and JOIN", 0, sf.getStatistics().getCollectionFetchCount() );
//		tx.commit();
//		s.close();
//		sf.close();
//
//		// open yet another SF
//		sf.close();
//		cfg = buildBaseConfiguration().setProperty( AvailableSettings.HBM2DDL_AUTO, "create-drop" );
//		cfg.buildMappings();
//		coll = cfg.getCollectionMapping( Continent.class.getName() + ".countries" );
//		coll.setFetchMode(FetchMode.SELECT);
//		coll.setLazy(false);
//		sf = cfg.buildSessionFactory();
//
//		s = sf.openSession();
//		tx = s.beginTransaction();
//		europe = fillDb(s);
//		tx.commit();
//		s.close();
//
//		s = sf.openSession();
//		tx = s.beginTransaction();
//		assertEquals( 0, sf.getStatistics().getCollectionLoadCount() );
//		assertEquals( 0, sf.getStatistics().getCollectionFetchCount() );
//		europe2 = (Continent) s.get( Continent.class, europe.getId() );
//		assertEquals( 1, sf.getStatistics().getCollectionLoadCount() );
//		assertEquals( "Should do explicit collection load, not part of the first one", 1, sf.getStatistics().getCollectionFetchCount() );
//		for ( Object o : europe2.getCountries() ) {
//			s.delete( o );
//		}
//		cleanDb( s );
//		tx.commit();
//		s.close();
//
//		sf.close();
//	}

	@Test
	public void testQueryStatGathering() {
		SessionFactory sf = buildBaseConfiguration()
				.setProperty( AvailableSettings.HBM2DDL_AUTO, "create-drop" )
				.buildSessionFactory();

		Session s = sf.openSession();
		Transaction tx = s.beginTransaction();
		fillDb(s);
		tx.commit();
		s.close();

		s = sf.openSession();
		tx = s.beginTransaction();
		final String continents = "from Continent";
		int results = s.createQuery( continents ).list().size();
		QueryStatistics continentStats = sf.getStatistics().getQueryStatistics( continents );
		assertNotNull( "stats were null",  continentStats );
		assertEquals( "unexpected execution count", 1, continentStats.getExecutionCount() );
		assertEquals( "unexpected row count", results, continentStats.getExecutionRowCount() );
		long maxTime = continentStats.getExecutionMaxTime();
		assertEquals( maxTime, sf.getStatistics().getQueryExecutionMaxTime() );
//		assertEquals( continents, stats.getQueryExecutionMaxTimeQueryString() );

		Iterator itr = s.createQuery( continents ).iterate();
		// iterate() should increment the execution count
		assertEquals( "unexpected execution count", 2, continentStats.getExecutionCount() );
		// but should not effect the cumulative row count
		assertEquals( "unexpected row count", results, continentStats.getExecutionRowCount() );
		Hibernate.close( itr );

		ScrollableResults scrollableResults = s.createQuery( continents ).scroll();
		// same deal with scroll()...
		assertEquals( "unexpected execution count", 3, continentStats.getExecutionCount() );
		assertEquals( "unexpected row count", results, continentStats.getExecutionRowCount() );
		// scroll through data because SybaseASE15Dialect throws NullPointerException
		// if data is not read before closing the ResultSet
		while ( scrollableResults.next() ) {
			// do nothing
		}
		scrollableResults.close();
		tx.commit();
		s.close();

		// explicitly check that statistics for "split queries" get collected
		// under the original query
		sf.getStatistics().clear();

		s = sf.openSession();
		tx = s.beginTransaction();
		final String localities = "from Locality";
		results = s.createQuery( localities ).list().size();
		QueryStatistics localityStats = sf.getStatistics().getQueryStatistics( localities );
		assertNotNull( "stats were null",  localityStats );
		// ...one for each split query
		assertEquals( "unexpected execution count", 2, localityStats.getExecutionCount() );
		assertEquals( "unexpected row count", results, localityStats.getExecutionRowCount() );
		maxTime = localityStats.getExecutionMaxTime();
		assertEquals( maxTime, sf.getStatistics().getQueryExecutionMaxTime() );
//		assertEquals( localities, stats.getQueryExecutionMaxTimeQueryString() );
		tx.commit();
		s.close();
		assertFalse( s.isOpen() );

		// native sql queries
		sf.getStatistics().clear();

		s = sf.openSession();
		tx = s.beginTransaction();
		final String sql = "select id, name from Country";
		results = s.createSQLQuery( sql ).addEntity( Country.class ).list().size();
		QueryStatistics sqlStats = sf.getStatistics().getQueryStatistics( sql );
		assertNotNull( "sql stats were null", sqlStats );
		assertEquals( "unexpected execution count", 1, sqlStats.getExecutionCount() );
		assertEquals( "unexpected row count", results, sqlStats.getExecutionRowCount() );
		maxTime = sqlStats.getExecutionMaxTime();
		assertEquals( maxTime, sf.getStatistics().getQueryExecutionMaxTime() );
//		assertEquals( sql, stats.getQueryExecutionMaxTimeQueryString() );
		tx.commit();
		s.close();

		s = sf.openSession();
		tx = s.beginTransaction();
		cleanDb( s );
		tx.commit();
		s.close();
		sf.close();
	}

	private Continent fillDb(Session s) {
		Continent europe = new Continent();
		europe.setName("Europe");
		Country france = new Country();
		france.setName("France");
		europe.setCountries( new HashSet() );
		europe.getCountries().add(france);
		s.persist(france);
		s.persist(europe);
		return europe;
	}

	private void cleanDb(Session s) {
		s.createQuery( "delete Locality" ).executeUpdate();
		s.createQuery( "delete Country" ).executeUpdate();
		s.createQuery( "delete Continent" ).executeUpdate();
	}

}
