/**
 *
 */


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


import java.io.Serializable;
import java.math.BigDecimal;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;

import org.hibernate.HibernateException;
import org.hibernate.engine.spi.SessionImplementor;
import org.hibernate.type.StandardBasicTypes;
import org.hibernate.type.Type;


public class VistaPointInTimeUserType
    extends
        AbstractPointInTimeUserType
{

    public Object getPropertyValue( Object value, int property )
        throws HibernateException
    {
        if ( value instanceof PointInTime )
        {
            PointInTime pointInTime = ( PointInTime )value;
            return ( property == 1 ) ? isoTime2Double( pointInTime.getTimestamp() ) : pointInTime.getTimestamp();
        }
        else if(value instanceof gov.va.med.cds.template.generated.basedatatypes.PointInTime){
        	gov.va.med.cds.template.generated.basedatatypes.PointInTime pointInTime = ( gov.va.med.cds.template.generated.basedatatypes.PointInTime )value;
            return ( property == 1 ) ? isoTime2Double( pointInTime.getLiteral() ) : pointInTime.getLiteral();
        }

        return null;
    }
    
    


    public String[] getPropertyNames( )
    {
        return new String[] { "timestamp" };
    }


    public Type[] getPropertyTypes( )
    {
        return new Type[] { StandardBasicTypes.STRING };
    }


    public Object assemble( Serializable cached, Object owner )
        throws HibernateException
    {
        return cached;
    }


    public Object assemble( Serializable cached, SessionImplementor sessionImplementor, Object owner )
        throws HibernateException
    {
        return cached;
    }


    public Object deepCopy( Object value )
        throws HibernateException
    {
        return value;
    }


    public Serializable disassemble( Object value, SessionImplementor sessionImplementor )
        throws HibernateException
    {
        return ( Serializable )value;
    }


    public boolean equals( Object x, Object y )
        throws HibernateException
    {
        return false;
    }


    public int hashCode( Object x )
        throws HibernateException
    {
        if ( x == null )
        {
            return 0;
        }

        return x.hashCode();
    }


    public Object nullSafeGet( ResultSet rs, String[] names, SessionImplementor sessionImplementor, Object owner )
        throws HibernateException,
            SQLException
    {
        String ts = rs.getString( names[0] );
        if ( ts == null )
        {
            return null;
        }

        String timestamp = vistaTime2String( ts );

        gov.va.med.cds.template.generated.basedatatypes.PointInTime pointInTime = new gov.va.med.cds.template.generated.basedatatypes.PointInTime();
       
        pointInTime.setLiteral(timestamp);
        
        return pointInTime;
    }


    public void nullSafeSet( PreparedStatement st, Object value, int index, SessionImplementor sessionImplementor )
        throws HibernateException,
            SQLException
    {
        //this method is implemented to set query parameters in the sql for a read - but not to write data to vista.
        if ( value == null )
        {
            st.setNull( index, StandardBasicTypes.DATE.sqlType() );
        }
        else
        {
            if ( value instanceof PointInTime )
            {
                PointInTime pointInTime = ( PointInTime )value;
                java.sql.Date timestamp = isoTime2Date( pointInTime.getTimestamp() );
                if ( timestamp != null )
                {
                    st.setDate( index, timestamp );
                }
                else
                {
                    st.setNull( index, StandardBasicTypes.DATE.sqlType() );
                }
            }
            else if(value instanceof gov.va.med.cds.template.generated.basedatatypes.PointInTime)
            {
            	gov.va.med.cds.template.generated.basedatatypes.PointInTime pointInTime = (gov.va.med.cds.template.generated.basedatatypes.PointInTime)value;
            	java.sql.Date timestamp = isoTime2Date( pointInTime.getLiteral() );
                if ( timestamp != null )
                {
                    st.setDate( index, timestamp );
                }
                else
                {
                    st.setNull( index, StandardBasicTypes.DATE.sqlType() );
                }
            }
            else
            {
                throw new HibernateException( "The value parameter must of type PointInTime. The actual type was " + value.getClass().getName() );
            }
        }
    }


    public Class<PointInTime> returnedClass( )
    {
        return PointInTime.class;
    }


    /**
     * builds the new date string from the historical date format to match HDR2.
     *  
     *  Historical Date Conversion: 
     *  
     *  Input                       Converted Output
     *  0                           - 0
     *  308                         - 20080000
     *  3081219                     - 20081219
     *  30812191219                 - 20081219
     *  3081219.170515              - 20081219170515
     *  30812191219.170515	    	- 20081219170515	
     *  
     *  Rules:
     *  Date not all numeric or Date length < 3 -- returns unconverted value
     *  Date length > 3 and date length < 7 -- adds 1700 to year and pads with zeros to make length 7
     *  Date length >= 7 -- adds 1700 to year and ignores characters beyond 7
     *  
     *  Time always attached to converted date (No offset as time is numeric)
     * 
     * @param vistaDate
     * @return builds the date string to match HDR2 
     */
    /*
    private String vistaTime2String( String vistaDate )
    {
        String retVal;
        String assigningFacility = null;

        if ( vistaDate != null )
        {
            // trim off the nano-seconds
            String[] dateTime = vistaDate.split( "\\." );
            String dtTime = dateTime[0];
            retVal = dtTime.replaceAll( "[- :]", "" );
            
            if( dateTime[1] != null && !dateTime[1].equals( "" ))
            {
                String[] remainder = dateTime[1].split( "\\^" );
                assigningFacility = remainder[1];
            }
        }
        else
        {
            retVal = vistaDate;
        }
        
        retVal = getOffset( retVal, assigningFacility );

        return retVal;
    }
    */

    public String vistaTime2String( String vistaDate )
    {
        String retVal = null;

        if ( vistaDate != null && vistaDate.length() >= 3 )
        {
            // trim off the time
            //            String[] dateTime = vistaDate.split( "\\." );
            String[] dateTime = vistaDate.split( "[.^]+" );
            vistaDate = dateTime[0];

            // Filter out non-conformant string dates
            if ( isNonConformatString( vistaDate ) )
            {
                return vistaDate;
            }

            // split the time up into date and time parts.
            int dateMaxIndex = ( vistaDate.length() >= 7 ) ? 7 : vistaDate.length();
            String datePart = vistaDate.substring( 0, dateMaxIndex ) + "0000".substring( 0, 7 - dateMaxIndex );

            // add 1700 from the year to match Cache date format
            datePart = ( Integer.parseInt( datePart.substring( 0, 3 ) ) + 1700 ) + datePart.substring( 3 );
            retVal = datePart;

            // if dateTime array has 3 elements, then order is date, time, facilityId, else date, facilityId
            int timeLength = dateTime.length > 2 ? dateTime[1].length() : 0;
            String timePart = ( timeLength > 0 ) ? dateTime[1].concat( "000000".substring( 0, 6 - timeLength ) ) : "";
            String facilityId = dateTime.length == 2 ? dateTime[1] : dateTime[2];

            // build the new return string from date and time parts
            retVal = datePart + timePart;

            // add timezone offset
            retVal = getOffset( retVal, facilityId );
        }
        else
        {
            retVal = vistaDate;
        }

        return retVal;
    }


    public int[] sqlTypes( )
    {
        return new int[] { StandardBasicTypes.DATE.sqlType() };
    }


    /**
     * Converts hdr date time to cache date time format
     * @param value
     * @return
     */

    public String convertToCacheDate( String value )
    {
        String dateValue = value.substring( 0, 8 );
        String timeValue = value.substring( 9 );
        BigDecimal intValue = BigDecimal.valueOf( Long.parseLong( dateValue ) );
        BigDecimal constValue = new BigDecimal( "17000000" );
        BigDecimal newValue = intValue.subtract( constValue );
        String paramValue = newValue.toString().concat( "." + timeValue );
        return paramValue;
    }

}
