

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


import gov.va.med.cds.exception.ErrorCodeEnum;
import gov.va.med.cds.util.QueryCacheUtil;

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

import org.dom4j.Document;
import org.dom4j.DocumentHelper;
import org.dom4j.Element;
import org.dom4j.Node;
import org.hibernate.HibernateException;
import org.hibernate.Session;
import org.hibernate.engine.spi.SessionImplementor;
import org.hibernate.type.StandardBasicTypes;
import org.hibernate.type.Type;
import org.hibernate.usertype.CompositeUserType;


public class VistaMHVLabChemHemRead1TestsUserType
    extends
        AbstractPointInTimeUserType
    implements
        CompositeUserType
{
    // no. of columns passing to the custom data type in Hibernate mapping file
    private static final int NO_OF_COLUMNS = 8;
    private static String[] PROPERTY_NAMES = new String[NO_OF_COLUMNS];
    private static Type[] PROPERTY_TYPES = new Type[NO_OF_COLUMNS];

    static
    {
        for ( int column = 0; column < NO_OF_COLUMNS; column++ )
        {
            PROPERTY_NAMES[column] = "";
            PROPERTY_TYPES[column] = StandardBasicTypes.STRING;
        }
    }


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


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


    @Override
    public Serializable disassemble( Object value, SessionImplementor session )
        throws HibernateException
    {
        return ( Serializable )value;
    }


    @Override
    public boolean equals( Object x, Object y )
        throws HibernateException
    {
        if ( x == null )
        {
            return false;
        }

        return ( x == y );
    }


    @Override
    public String[] getPropertyNames( )
    {
        return PROPERTY_NAMES;
    }


    @Override
    public Type[] getPropertyTypes( )
    {
        return PROPERTY_TYPES;
    }


    @Override
    public Object getPropertyValue( Object component, int property )
        throws HibernateException
    {
        throw new UnsupportedOperationException( new MethodNotImplementedException( ErrorCodeEnum.METHOD_NOT_IMPLEMENTED, this.getClass().getName() ) );
    }


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

        return object.hashCode();
    }


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


    @Override
    public Object nullSafeGet( ResultSet rs, String[] names, SessionImplementor session, Object owner )
        throws HibernateException,
            SQLException
    {
        Element labTestsRoot = null;

        Session labTest60DataSession = session.getFactory().openSession();

        Map<String, LabTestsParsedFromGlobal> labTestsParsedFromGlobalMap = null;

        LabTestsParsedFromGlobal labTestsParsedFromGlobal = null;

        VistaGlobalParser vistaGlobalParser = new VistaGlobalParser();

        // ConcatenatedResultExt
        // 122^30^CHOLESTEROL^
        // Value^FIELD_NUM^ColumnName^Message

        if ( rs == null )
        {
            return null;
        }
        else
        {
            int labTestCounter = 0;

            Document document = DocumentHelper.createDocument();

            labTestsRoot = document.addElement( "labTests" );

            String concatenatedResultsExt = rs.getString( names[0] );
            String concatenatedResultsManualExt = rs.getString( names[1] );

            if ( null == concatenatedResultsExt )
            {
                return null;
            }

            if ( ( concatenatedResultsManualExt != null ) && ( !concatenatedResultsManualExt.equalsIgnoreCase( "null" ) ) )
            {
                labTestsParsedFromGlobalMap = vistaGlobalParser.parseGlobal( concatenatedResultsManualExt );
            }

            for ( String testValues : concatenatedResultsExt.split( "," ) )
            {
                String[] testValuesIndividual = testValues.split( "\\^" );

                if ( testValuesIndividual.length >= 3 && !testValuesIndividual[testValuesIndividual.length - 1].toUpperCase().contains( "ERROR" ) && isNumeric( testValuesIndividual[1].trim() ) )
                {
                    String labTestFieldNum = testValuesIndividual[1].trim();

                    if ( labTestsParsedFromGlobalMap != null )
                    {
                        labTestsParsedFromGlobal = labTestsParsedFromGlobalMap.get( labTestFieldNum );
                    }

                    try
                    {
                        for ( Object labTestElement : new QueryCacheUtil().getNamedQueryList( labTest60DataSession, "MHVLabChemHemRead1.LabTestPromise.labTests", "labTestFieldNum", new BigDecimal(
                                        labTestFieldNum ) ) )
                        {
                            labTestCounter++ ;

                            String siteSpecimen = rs.getString( names[names.length - 1] );

                            if ( siteSpecimen == null )
                            {
                                break;
                            }

                            for ( Object chemistryResult : new QueryCacheUtil().getNamedQueryList( labTest60DataSession, "labTests.labTest.chemistryResults.chemistryResult", "labTestFieldNum",
                                            new BigDecimal( labTestFieldNum ), "siteSpecimen", new BigDecimal( siteSpecimen ) ) )
                            {

                                if ( labTestsParsedFromGlobal != null && labTestsParsedFromGlobal.getPerormanceOrgCode() != null && labTestsParsedFromGlobal.getPerormanceOrgCode().length() > 0 )
                                {

                                    for ( Object performingOrganizationElement : new QueryCacheUtil().getNamedQueryList( labTest60DataSession,
                                                    "labTests.labTest.chemistryResults.chemistryResult.inst4PerformingOrganization", "labTestFieldNum",
                                                    new BigDecimal( labTestsParsedFromGlobal.getPerormanceOrgCode() ) ) )
                                    {
                                        ( ( Element )chemistryResult ).add( ( Element )( ( ( Element )performingOrganizationElement ).clone() ) );

                                        break;
                                    }
                                }
                                else
                                {

                                    for ( Object performingOrganizationElement : new QueryCacheUtil().getNamedQueryList( labTest60DataSession,
                                                    "labTests.labTest.chemistryResults.chemistryResult.performingOrganization", "labTestFieldNum", new BigDecimal( labTestFieldNum ) ) )
                                    {

                                        ( ( Element )chemistryResult ).add( ( Element )( ( ( Element )performingOrganizationElement ).clone() ) );

                                        break;
                                        //TODO
                                        //performing organization should always be single and not multiples
                                    }
                                }

                                ( ( Element )( ( Element )labTestElement ).selectSingleNode( "chemistryResults" ) ).add( ( ( Element )( ( Element )chemistryResult ).detach() ) );

                                labTestsRoot.add( ( Element )labTestElement );

                                //labTestCounter++ ;

                                if ( ( ( Element )labTestsRoot.selectSingleNode( "//labTests/labTest[" + labTestCounter + "]/orderedTestCode" ) == null ) && ( labTestsParsedFromGlobal != null )
                                                && ( labTestsParsedFromGlobal.getLoincCode() != null ) && ( labTestsParsedFromGlobal.getNltReportedTestCode() != null )
                                                && ( !labTestsParsedFromGlobal.getLoincCode().equals( "" ) ) && ( !labTestsParsedFromGlobal.getNltReportedTestCode().equals( "" ) ) )
                                {
                                    for ( Object orderedTestCode : new QueryCacheUtil().getNamedQueryList( labTest60DataSession, "MHVLabChemHemRead1.LabTestPromises.labTests.orderedTestCode1",
                                                    "loincCode", new BigDecimal( labTestsParsedFromGlobal.getLoincCode() ), "workLoadCode", "%" + "labTestsParsedFromGlobal.getNltReportedTestCode()"
                                                                    + "%" ) )
                                    {
                                        ( ( Element )labTestElement ).add( ( Element )( ( ( Element )orderedTestCode ).detach() ) );
                                    }
                                }
                                else if ( ( ( Element )labTestsRoot.selectSingleNode( "//labTests/labTest[" + labTestCounter + "]/orderedTestCode" ) == null ) && ( labTestsParsedFromGlobal != null )
                                                && ( labTestsParsedFromGlobal.getLoincCode() != null ) && ( !labTestsParsedFromGlobal.getLoincCode().equals( "" ) ) )
                                {
                                    for ( Object orderedTestCode : new QueryCacheUtil().getNamedQueryList( labTest60DataSession, "MHVLabChemHemRead1.LabTestPromises.labTests.orderedTestCode2",
                                                    "loincCode", new BigDecimal( labTestsParsedFromGlobal.getLoincCode() ) ) )
                                    {
                                        ( ( Element )labTestElement ).add( ( Element )( ( ( Element )orderedTestCode ).detach() ) );
                                        break;
                                    }
                                }

                                Element test = ( Element )labTestsRoot.selectSingleNode( "//labTests/labTest[" + labTestCounter + "]/chemistryResults/chemistryResult" );

                                if ( test != null )
                                {
                                    float observationValue;
                                    float referenceLow;
                                    float referenceHigh;
                                    float criticalLow;
                                    float criticalHigh;

                                    if ( test.selectSingleNode( "//labTests/labTest[" + labTestCounter + "]/chemistryResults/chemistryResult/referenceRange" ) != null )
                                    {
                                        if ( ( ( labTestsParsedFromGlobal != null ) && ( labTestsParsedFromGlobal.getReferenceLow() != null ) && ( labTestsParsedFromGlobal.getReferenceHigh() != null ) && ( !labTestsParsedFromGlobal
                                                        .getReferenceLow().equals( "" ) ) ) && ( !labTestsParsedFromGlobal.getReferenceHigh().equals( "" ) ) )
                                        {
                                            test.selectSingleNode( "//labTests/labTest[" + labTestCounter + "]/chemistryResults/chemistryResult/referenceRange" ).setText(
                                                            labTestsParsedFromGlobal.getReferenceLow() + "-" + labTestsParsedFromGlobal.getReferenceHigh() );
                                        }
                                        else if ( ( labTestsParsedFromGlobal != null ) && ( labTestsParsedFromGlobal.getReferenceLow() != null )
                                                        && ( !labTestsParsedFromGlobal.getReferenceLow().equals( "" ) ) )
                                        {
                                            if ( !labTestsParsedFromGlobal.getReferenceLow().contains( ">" ) && isNumeric( labTestsParsedFromGlobal.getReferenceLow() ) )
                                            {
                                                labTestsParsedFromGlobal.setReferenceLow( ">" + labTestsParsedFromGlobal.getReferenceLow() );
                                            }

                                            test.selectSingleNode( "//labTests/labTest[" + labTestCounter + "]/chemistryResults/chemistryResult/referenceRange" ).setText(
                                                            labTestsParsedFromGlobal.getReferenceLow() );
                                        }
                                        else if ( ( labTestsParsedFromGlobal != null ) && ( labTestsParsedFromGlobal.getReferenceHigh() != null )
                                                        && ( !labTestsParsedFromGlobal.getReferenceHigh().equals( "" ) ) )
                                        {
                                            if ( !labTestsParsedFromGlobal.getReferenceHigh().contains( "<" ) && isNumeric( labTestsParsedFromGlobal.getReferenceHigh() ) )
                                            {
                                                labTestsParsedFromGlobal.setReferenceHigh( "<" + labTestsParsedFromGlobal.getReferenceHigh() );
                                            }

                                            test.selectSingleNode( "//labTests/labTest[" + labTestCounter + "]/chemistryResults/chemistryResult/referenceRange" ).setText(
                                                            labTestsParsedFromGlobal.getReferenceHigh() );
                                        }

                                    }

                                    if ( test.selectSingleNode( "//labTests/labTest[" + labTestCounter + "]/chemistryResults/chemistryResult/observationUnits" ) != null )
                                    {
                                        if ( ( labTestsParsedFromGlobal != null ) && ( labTestsParsedFromGlobal.getUnitsOfMeasure() != null )
                                                        && ( !labTestsParsedFromGlobal.getUnitsOfMeasure().equals( "" ) ) )
                                        {
                                            test.selectSingleNode( "//labTests/labTest[" + labTestCounter + "]/chemistryResults/chemistryResult/observationUnits/code" ).setText(
                                                            labTestsParsedFromGlobal.getUnitsOfMeasure() );
                                            test.selectSingleNode( "//labTests/labTest[" + labTestCounter + "]/chemistryResults/chemistryResult/observationUnits/displayText" ).setText(
                                                            labTestsParsedFromGlobal.getUnitsOfMeasure() );
                                        }
                                    }

                                    if ( ( test.selectSingleNode( "//labTests/labTest[" + labTestCounter + "]/chemistryResults/chemistryResult/testIdentifier/code" ) == null )
                                                    && ( labTestsParsedFromGlobal != null ) && ( labTestsParsedFromGlobal.getLoincCode() != null )
                                                    && ( isNumeric( labTestsParsedFromGlobal.getLoincCode() ) ) )
                                    {
                                        for ( Object testIdentifier : new QueryCacheUtil().getNamedQueryList( labTest60DataSession, "labTests.chemistryResults.testIdentifier", "loincCode",
                                                        BigDecimal.valueOf( Float.valueOf( labTestsParsedFromGlobal.getLoincCode() ) ) ) )
                                        {
                                            test.add( ( ( Element )testIdentifier ).detach() );
                                        }
                                    }
                                    else if ( test.selectSingleNode( "//labTests/labTest[" + labTestCounter + "]/chemistryResults/chemistryResult/testIdentifier/code" ) == null
                                                    && testValuesIndividual[2] != null )
                                    {
                                        test.selectSingleNode( "//labTests/labTest[" + labTestCounter + "]/chemistryResults/chemistryResult/testIdentifier/displayText" ).setText(
                                                        testValuesIndividual[2].trim() );
                                        test.selectSingleNode( "//labTests/labTest[" + labTestCounter + "]/chemistryResults/chemistryResult/testIdentifier/originalText" ).setText(
                                                        testValuesIndividual[2].trim() );
                                    }

                                    test.selectSingleNode( "observationValue" ).setText( testValuesIndividual[0].trim() );

                                    if ( ( labTestsParsedFromGlobal != null ) && ( labTestsParsedFromGlobal.getReferenceLow() != null ) && ( !labTestsParsedFromGlobal.getReferenceLow().equals( "" ) ) )
                                    {
                                        test.selectSingleNode( "referenceLow" ).setText( labTestsParsedFromGlobal.getReferenceLow() );
                                    }

                                    Node nodeReferenceLow = test.selectSingleNode( "referenceLow" );

                                    if ( ( labTestsParsedFromGlobal != null ) && ( labTestsParsedFromGlobal.getReferenceHigh() != null )
                                                    && ( !labTestsParsedFromGlobal.getReferenceHigh().equals( "" ) ) )
                                    {
                                        test.selectSingleNode( "referenceHigh" ).setText( labTestsParsedFromGlobal.getReferenceHigh() );
                                    }

                                    Node nodeReferenceHigh = test.selectSingleNode( "referenceHigh" );

                                    if ( ( labTestsParsedFromGlobal != null ) && ( labTestsParsedFromGlobal.getCriticalLow() != null ) && ( !labTestsParsedFromGlobal.getCriticalLow().equals( "" ) ) )
                                    {
                                        test.selectSingleNode( "criticalLow" ).setText( labTestsParsedFromGlobal.getCriticalLow() );
                                    }

                                    Node nodeCriticalLow = test.selectSingleNode( "criticalLow" );

                                    if ( ( labTestsParsedFromGlobal != null ) && ( labTestsParsedFromGlobal.getCriticalHigh() != null ) && ( !labTestsParsedFromGlobal.getCriticalHigh().equals( "" ) ) )
                                    {
                                        test.selectSingleNode( "criticalHigh" ).setText( labTestsParsedFromGlobal.getCriticalHigh() );
                                    }

                                    Node nodeCriticalHigh = test.selectSingleNode( "criticalHigh" );

                                    test.selectSingleNode( "observationValue" ).setText( testValuesIndividual[0].trim() );

                                    if ( ( labTestsParsedFromGlobal != null ) && ( labTestsParsedFromGlobal.getValueInterpretation() != null )
                                                    && ( !labTestsParsedFromGlobal.getValueInterpretation().equals( "" ) ) )
                                    {
                                        test.selectSingleNode( "valueInterpretation" ).setText( labTestsParsedFromGlobal.getValueInterpretation() );
                                    }
                                    else
                                    {
                                        if ( isNumeric( testValuesIndividual[0].trim() ) )
                                        {
                                            observationValue = Float.valueOf( testValuesIndividual[0].trim() );

                                            if ( nodeReferenceLow != null && isNumeric( nodeReferenceLow.getText() ) )
                                            {
                                                referenceLow = Float.valueOf( nodeReferenceLow.getText() );

                                                if ( nodeCriticalLow != null && isNumeric( nodeCriticalLow.getText() ) )
                                                {
                                                    criticalLow = Float.valueOf( nodeCriticalLow.getText() );

                                                    if ( observationValue < referenceLow && observationValue > criticalLow )
                                                    {
                                                        test.selectSingleNode( "valueInterpretation" ).setText( "L" );
                                                    }
                                                    else if ( observationValue <= criticalLow )
                                                    {
                                                        test.selectSingleNode( "valueInterpretation" ).setText( "L*" );
                                                    }
                                                }
                                                else if ( observationValue < referenceLow )
                                                {
                                                    test.selectSingleNode( "valueInterpretation" ).setText( "L" );
                                                }

                                            }

                                            if ( nodeReferenceHigh != null && isNumeric( nodeReferenceHigh.getText() ) )
                                            {
                                                referenceHigh = Float.valueOf( nodeReferenceHigh.getText() );

                                                if ( nodeCriticalHigh != null && isNumeric( nodeCriticalHigh.getText() ) )
                                                {
                                                    criticalHigh = Float.valueOf( nodeCriticalHigh.getText() );

                                                    if ( observationValue > referenceHigh && observationValue < criticalHigh )
                                                    {
                                                        test.selectSingleNode( "valueInterpretation" ).setText( "H" );
                                                    }
                                                    else if ( observationValue >= criticalHigh )
                                                    {
                                                        test.selectSingleNode( "valueInterpretation" ).setText( "H*" );
                                                    }

                                                }
                                                else if ( observationValue > referenceHigh )
                                                {
                                                    test.selectSingleNode( "valueInterpretation" ).setText( "H" );
                                                }
                                            }
                                        }
                                    }
                                }
                            }
                        }
                    }
                    catch ( Exception e )
                    {
                        //TODO - drop labTest element in the next maintainance release
                        Element labTestsElement = ( Element )labTestsRoot.selectSingleNode( "//labTests/labTest[" + labTestCounter + "]" );

                        if ( labTestsElement != null )
                        {
                            labTestsElement.detach();
                        }

                        --labTestCounter;
                    }
                }
            }
        }

        labTest60DataSession.disconnect();

        return labTestsRoot;
    }


    @Override
    public void nullSafeSet( PreparedStatement st, Object value, int index, SessionImplementor session )
        throws HibernateException,
            SQLException
    {
        throw new UnsupportedOperationException( new MethodNotImplementedException( ErrorCodeEnum.METHOD_NOT_IMPLEMENTED, this.getClass().getName() ) );
    }


    @Override
    public Object replace( Object original, Object target, SessionImplementor session, Object owner )
        throws HibernateException
    {
        return original;
    }


    @Override
    public Class<String> returnedClass( )
    {
        return java.lang.String.class;
    }


    @Override
    public void setPropertyValue( Object component, int property, Object value )
        throws HibernateException
    {
        throw new UnsupportedOperationException( new MethodNotImplementedException( ErrorCodeEnum.METHOD_NOT_IMPLEMENTED, this.getClass().getName() ) );
    }

}
