

package gov.va.med.cds.persistence.hibernate.common;


import static org.easymock.EasyMock.expect;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNull;

import gov.va.med.cds.junit.runners.Suite;
import gov.va.med.cds.junit.runners.SuiteAwareSpringRunner;
import gov.va.med.cds.util.DateTimeUtil;

import org.dom4j.Element;
import org.easymock.EasyMock;
import org.hibernate.EntityMode;
import org.hibernate.HibernateException;
import org.hibernate.engine.spi.SessionImplementor;
import org.hibernate.type.StandardBasicTypes;
import org.junit.BeforeClass;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.test.context.ContextConfiguration;

import java.io.Serializable;
import java.sql.Date;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.Properties;
import java.util.TimeZone;


@RunWith( SuiteAwareSpringRunner.class )
@ContextConfiguration( locations = { "classpath:gov/va/med/cds/config/timeZoneOffsetContext.xml" } )
public class VistaPointInTimeUserTypeQueryTest
{
    private static final String TEST_NO_CONVERTION = "20";
    private static final String TEST_DATE_PLUS_OFFSET = "20071231235959.999+0600";
    private static final String TEST_DATE_MINUS_OFFSET = "20071231235959.999-0600";
    private static final String TEST_DATE_MINUS_OFFSET_COMPARE = "20071231-0600";
    private static final String TEST_DATE_WITH_NO_OFFSET = "20071231";
    private static final String TEST_NON_CONFORMANT_DATE = "2007/23/1235959.999-0600";
    private static final double CONVERTED_VALUE = 3071231.235959;
    private static final double NO_CONVERTION_VALUE = 0.0;
    private static VistaPointInTimeUserTypeQuery vistaPointInTimeUserTypeQuery = null;
    private static final String[] NAMES = { "timestamp", "namespace" };
    private static PointInTime pit = null;
    private static String isoTime = null;
    private static long time = 1281045978450l;


    @BeforeClass
    @Suite( groups = { "checkintest" } )
    public static void beforeVistaPointInTimeUserTypeQueryTestClassSetUp( )
        throws Exception
    {
        vistaPointInTimeUserTypeQuery = new VistaPointInTimeUserTypeQuery();
        pit = new PointInTime();
        isoTime = DateTimeUtil.getISOTime( time, TimeZone.getDefault() );
        pit.setTimestamp( isoTime );
    }


    @Test
    @Suite( groups = { "checkintest" } )
    public void testNullSafeGetWithNullTimestamp( )
        throws HibernateException,
            SQLException
    {
        ResultSet rs = EasyMock.createMock( ResultSet.class );
        SessionImplementor sessionImplementor = EasyMock.createMock( SessionImplementor.class );

        expect( rs.getString( NAMES[0] ) ).andReturn( null );
        expect( rs.getString( NAMES[1] ) ).andReturn( "578" );

        EasyMock.replay( rs );
        PointInTime result = ( PointInTime )vistaPointInTimeUserTypeQuery.nullSafeGet( rs, NAMES, sessionImplementor, null );

        assertNull( result );
        EasyMock.verify( rs );
    }


    @Test
    @Suite( groups = { "checkintest" } )
    public void testNullSafeGetWithInvalidNamespace( )
        throws HibernateException,
            SQLException
    {
        ResultSet rs = EasyMock.createMock( ResultSet.class );
        SessionImplementor sessionImplementor = EasyMock.createMock( SessionImplementor.class );

        expect( rs.getString( NAMES[0] ) ).andReturn( Double.toString( CONVERTED_VALUE ) );
        expect( rs.getString( NAMES[1] ) ).andReturn( "abc" );
       
        expect( rs.wasNull() ).andReturn( false );

        EasyMock.replay( rs, sessionImplementor );

        gov.va.med.cds.template.generated.basedatatypes.PointInTime result = ( gov.va.med.cds.template.generated.basedatatypes.PointInTime )vistaPointInTimeUserTypeQuery.nullSafeGet( rs, NAMES, sessionImplementor, null );

        assertEquals( result.getLiteral(), TEST_DATE_WITH_NO_OFFSET );
        EasyMock.verify( sessionImplementor );
    }


    @Test
    @Suite( groups = { "checkintest" } )
    public void testNullSafeGet( )
        throws HibernateException,
            SQLException
    {
        ResultSet rs = EasyMock.createMock( ResultSet.class );
        SessionImplementor sessionImplementor = EasyMock.createMock( SessionImplementor.class );

        String dateTime = Double.toString( CONVERTED_VALUE );
        String nameSpace = "578";

        expect( rs.getString( NAMES[0] ) ).andReturn( dateTime );
        expect( rs.getString( NAMES[1] ) ).andReturn( nameSpace );
       
        expect( rs.wasNull() ).andReturn( false );

        EasyMock.replay( rs, sessionImplementor );
        gov.va.med.cds.template.generated.basedatatypes.PointInTime result = ( gov.va.med.cds.template.generated.basedatatypes.PointInTime )vistaPointInTimeUserTypeQuery.nullSafeGet( rs, NAMES, sessionImplementor, null );

        assertEquals( result.getLiteral(), TEST_DATE_MINUS_OFFSET_COMPARE );
        EasyMock.verify( sessionImplementor );
    }

    @Test
    @Suite( groups = { "checkintest" } )
    public void testNullSafeSetWithNull( )
        throws HibernateException,
            SQLException
    {
        int index = 1;
        PreparedStatement statement = EasyMock.createMock( PreparedStatement.class );
        SessionImplementor sessionImplementor = EasyMock.createMock( SessionImplementor.class );

        statement.setNull( index, StandardBasicTypes.DATE.sqlType() );

        EasyMock.replay( statement );
        vistaPointInTimeUserTypeQuery.nullSafeSet( statement, null, 1, sessionImplementor );
        EasyMock.verify( statement );
    }


    @Test
    @Suite( groups = { "checkintest" } )
    public void testNullSafeSetWithTimestamp( )
        throws HibernateException,
            SQLException
    {
        int index = 1;
        PreparedStatement statement = EasyMock.createMock( PreparedStatement.class );
        SessionImplementor sessionImplementor = EasyMock.createMock( SessionImplementor.class );

        String timest = "20101231235959.450-0600";
        PointInTime timestamp = new PointInTime();
        timestamp.setTimestamp( timest );

        statement.setDate( index, vistaPointInTimeUserTypeQuery.isoTime2Date( timest ) );

        EasyMock.replay( statement );
        vistaPointInTimeUserTypeQuery.nullSafeSet( statement, timestamp, 1, sessionImplementor );
        EasyMock.verify( statement );
    }


    @Test
    @Suite( groups = { "checkintest" } )
    public void testNullSafeSetWithNullTimestamp( )
        throws HibernateException,
            SQLException
    {
        int index = 1;
        PreparedStatement statement = EasyMock.createMock( PreparedStatement.class );
        SessionImplementor sessionImplementor = EasyMock.createMock( SessionImplementor.class );

        PointInTime timestamp = new PointInTime();
        timestamp.setTimestamp( null );

        statement.setNull( index, StandardBasicTypes.DATE.sqlType() );

        EasyMock.replay( statement );
        vistaPointInTimeUserTypeQuery.nullSafeSet( statement, timestamp, 1, sessionImplementor );
        EasyMock.verify( statement );
    }


    @Test( expected = HibernateException.class )
    @Suite( groups = { "checkintest" } )
    public void testNullSafeSetNonPointInTimeValue( )
        throws HibernateException,
            SQLException
    {
        int index = 1;
        PreparedStatement statement = EasyMock.createMock( PreparedStatement.class );
        SessionImplementor sessionImplementor = EasyMock.createMock( SessionImplementor.class );
        Object object = new String( "NotAnElementInstance" );
        //this should throw exception ...
        vistaPointInTimeUserTypeQuery.nullSafeSet( statement, object, index, sessionImplementor );
    }


    @Test( expected = UnsupportedOperationException.class )
    @Suite( groups = { "checkintest" } )
    public void testSetPropertyValue( )
        throws HibernateException,
            SQLException
    {
        //this should throw exception ...
        vistaPointInTimeUserTypeQuery.setPropertyValue( null, 1, pit );
    }


    @Test
    @Suite( groups = { "checkintest" } )
    public void testIsoTime2DoubleConversion( )
    {
        Double isoDouble = vistaPointInTimeUserTypeQuery.isoTime2Double( TEST_DATE_PLUS_OFFSET );
        assertEquals( isoDouble.doubleValue(), CONVERTED_VALUE, 0 );
        isoDouble = vistaPointInTimeUserTypeQuery.isoTime2Double( TEST_DATE_MINUS_OFFSET );
        assertEquals( isoDouble.doubleValue(), CONVERTED_VALUE, 0 );
        isoDouble = vistaPointInTimeUserTypeQuery.isoTime2Double( TEST_NO_CONVERTION );
        assertEquals( isoDouble.doubleValue(), NO_CONVERTION_VALUE, 0 );
        isoDouble = vistaPointInTimeUserTypeQuery.isoTime2Double( null );
        assertEquals( isoDouble.doubleValue(), NO_CONVERTION_VALUE, 0 );
        isoDouble = vistaPointInTimeUserTypeQuery.isoTime2Double( TEST_NON_CONFORMANT_DATE );
        assertEquals( isoDouble, null );
    }


    @Test
    @Suite( groups = { "checkintest" } )
    public void testIsoTime2DateConversion( )
    {
        //TODO - determine if this needs to be kept - isoTime2Date - not used
        Date date = vistaPointInTimeUserTypeQuery.isoTime2Date( pit.getTimestamp() );
        String dateSt = DateTimeUtil.getISOTime( date.getTime(), TimeZone.getDefault() );

        //now compare without nano/millis and timezone offset - isoTime2Date removes millis
        assertEquals( pit.getTimestamp().split( "\\." )[0], dateSt.split( "\\." )[0] );
    }


    @Test
    @Suite( groups = { "checkintest" } )
    public void testBasicGetValuesTest( )
    {
        assertEquals( vistaPointInTimeUserTypeQuery.getPropertyNames()[0], "timestamp" );
        assertEquals( vistaPointInTimeUserTypeQuery.getPropertyTypes()[0], StandardBasicTypes.STRING );

        Serializable cache = new String( "cache" );
        assertEquals( vistaPointInTimeUserTypeQuery.assemble( cache, null ), cache );
        assertEquals( vistaPointInTimeUserTypeQuery.assemble( cache, null, null ), cache );

        Object original = new String( "original" );
        Object almostOriginal = new String( "original" );
        assertEquals( vistaPointInTimeUserTypeQuery.replace( original, null, null, null ), original );
        assertEquals( vistaPointInTimeUserTypeQuery.deepCopy( original ), almostOriginal );
        assertEquals( vistaPointInTimeUserTypeQuery.disassemble( original, null ), ( Serializable )original );
        assertFalse( vistaPointInTimeUserTypeQuery.equals( original, almostOriginal ) );
        assertEquals( vistaPointInTimeUserTypeQuery.returnedClass(), PointInTime.class );
        assertFalse( vistaPointInTimeUserTypeQuery.isMutable() );

        String object = "object";
        assertEquals( vistaPointInTimeUserTypeQuery.hashCode( "object" ), object.hashCode() );
        assertEquals( vistaPointInTimeUserTypeQuery.hashCode( null ), 0 );

        //91 is the int value for Hibernate.DATE.sqlType() 
        assertEquals( vistaPointInTimeUserTypeQuery.sqlTypes()[0], 91 );

        String timestamp = pit.getTimestamp();
        assertEquals( isoTime, timestamp );

        assertEquals( vistaPointInTimeUserTypeQuery.getPropertyValue( pit, 2 ), timestamp );
        Double time = vistaPointInTimeUserTypeQuery.isoTime2Double( timestamp );
        assertEquals( vistaPointInTimeUserTypeQuery.getPropertyValue( pit, 1 ), time );
        assertNull( vistaPointInTimeUserTypeQuery.getPropertyValue( "someObject", 1 ) );

        Properties props = new Properties();
        props.put( VistaPointInTimeUserTypeQuery.SET_VALUES, VistaPointInTimeUserTypeQuery.SV_NUMERIC_ONLY );
        vistaPointInTimeUserTypeQuery.setParameterValues( props );
        assertEquals( vistaPointInTimeUserTypeQuery.setValues, VistaPointInTimeUserTypeQuery.SV_NUMERIC_ONLY );
    }
}
