

package gov.va.med.cds.response.sequencer;


import gov.va.med.cds.clinicaldata.TemplateMetaDataInterface;
import gov.va.med.cds.exception.ErrorCodeEnum;
import gov.va.med.cds.template.TemplateManager;
import gov.va.med.cds.xml.schema.SchemaHelper;
import gov.va.med.cds.xml.schema.SchemaHelperInterface;

import org.dom4j.Document;
import org.dom4j.DocumentException;
import org.dom4j.DocumentHelper;
import org.dom4j.Element;

import java.io.StringReader;
import java.sql.Blob;
import java.util.ArrayList;
import java.util.Hashtable;
import java.util.List;
import java.util.Map;

import javax.annotation.Resource;
import javax.xml.transform.stream.StreamSource;
import javax.xml.validation.Validator;


public class HAVitalSignsResponseSchemaSequencer
    implements
        ResponseSequencerInterface
{
    private TemplateManager templateManager;
    private final Map<String, Blob> TEMPLATE_SCHEMA_BLOB_CACHE = new Hashtable<String, Blob>();


    @Override
    public Document sequence( String templateId, String sourceXml, boolean validateReadResponse )
    {
        if ( sourceXml == null || sourceXml.isEmpty() )
        {
            throw new SequencerException( ErrorCodeEnum.SCHEMA_VALIDATION_FAILED, "Source document is null" );
        }

        Document sourceDoc = null;
        try
        {
            sourceDoc = DocumentHelper.parseText( sourceXml );
        }
        catch ( DocumentException dex )
        {
            throw new SequencerException( ErrorCodeEnum.SCHEMA_VALIDATION_FAILED, dex, dex.getMessage() );
        }

        return sequence( templateId, sourceDoc, validateReadResponse );
    }


    @Override
    public Document sequence( String templateId, Document sourceDoc, boolean validateReadResponse )
    {
        if ( templateId == null || templateId.isEmpty() )
        {
            throw new SequencerException( ErrorCodeEnum.READ_REQUEST_TEMPLATE_ID_NULL );
        }

        if ( sourceDoc == null || !sourceDoc.hasContent() )
        {
            throw new SequencerException( ErrorCodeEnum.SCHEMA_XML_NULL );
        }

        SchemaHelperInterface schemaHelper = new SchemaHelper( getBlobJar( templateId ), templateId );
        Map<String, List<String>> elementMap = schemaHelper.getSymbolMap();
        Element updatedElement = updateRootElement( sourceDoc );     
        List<String> elementOrder = elementMap.get( "CLINICALDATA.CLINICALDATA" );
        Element rootElement = reorderElementsInElement( updatedElement, elementOrder, elementMap );
        Document document = null;
        try
        {
            document = DocumentHelper.parseText( rootElement.asXML() );
        }
        catch ( DocumentException e1 )
        {
            // TODO Auto-generated catch block
            e1.printStackTrace();
        }

        if ( validateReadResponse && ( null != document ) )
        {
            try
            {
                Validator validator = templateManager.getTemplateValidator( templateId );
                if ( null != validator )
                {
                    validator.validate( new StreamSource( new StringReader( document.asXML() ) ) );
                }
                else
                {
                    throw new SequencerException( ErrorCodeEnum.CANNOT_FIND_TEMPLATE_VALIDATOR, templateId );
                }
            }
            catch ( Exception e )
            {
                throw new SequencerException( ErrorCodeEnum.SCHEMA_VALIDATION_FAILED, e, e.getMessage() );
            }
        }

        return document;
    }

    @SuppressWarnings( "unchecked" )
    private Element reorderElementsInElement( Element element, List<String> elementOrder, Map<String, List<String>> elementMap )
    {
        List<Element> orderedElementList = new ArrayList<Element>();
        for ( String xsdElementDetail : elementOrder )
        {
            int indexOfSlash = xsdElementDetail.indexOf( '/' );

            String elementName = xsdElementDetail.substring( 0, indexOfSlash );
            String elementType = xsdElementDetail.substring( indexOfSlash + 1 );

            for ( Element e : ( List<Element> )element.elements( elementName ) )
            {
                if ( elementType.contains( "." ) )
                {
                    reorderElementsInElement( e, elementMap.get( elementType ), elementMap );
                }
                orderedElementList.add( e );
            }
        }

        element.setContent( orderedElementList );

        return element;
    }

    @SuppressWarnings( "unchecked" )
    private Element updateRootElement( Document sourceDoc )
    {
        Element qualifiers = null;
        Element qualifiersToAdd = null;
        Element vsoList = null;
        Element vitalSignObservation = null;
        Element rootElement = sourceDoc.getRootElement();  
       
        Element patientEnteredVitalSigns = rootElement.element("patients").element("patient").element("patientEnteredVitalSigns");
        List<Element>patientEnteredVitalSignsList=null;
        if(patientEnteredVitalSigns !=null){
            patientEnteredVitalSignsList= patientEnteredVitalSigns.elements( "patientEnteredVitalSign" );
        }
        else{
            return rootElement;
        }
        
        for ( Element patientEnteredVitalSignElement : patientEnteredVitalSignsList )
        {
            qualifiers = ( Element )patientEnteredVitalSignElement.element("vitalSignObservationEvent").element( "vitalSignObservation").element("vsoQlfsList").element( "qualifiers" );
            qualifiersToAdd = ( Element )qualifiers.detach();

            vsoList = ( Element )patientEnteredVitalSignElement.element("vitalSignObservationEvent").element( "vitalSignObservation").element("vsoQlfsList");
            vsoList.detach();

            if ( qualifiersToAdd != null && qualifiersToAdd.hasContent() )
            {
                vitalSignObservation = patientEnteredVitalSignElement.element( "vitalSignObservationEvent" ).element( "vitalSignObservation" );
                vitalSignObservation.add( qualifiersToAdd );
            }
        }

        return rootElement;
    }


    private Blob getBlobJar( String templateId )
    {
        Blob schemaJar = TEMPLATE_SCHEMA_BLOB_CACHE.get( templateId );

        if ( schemaJar == null )
        {
            try
            {
                TemplateMetaDataInterface templateMetaData = templateManager.getTemplateMetaData( templateId );
                schemaJar = new javax.sql.rowset.serial.SerialBlob( templateMetaData.getTemplateJar() );
                TEMPLATE_SCHEMA_BLOB_CACHE.put( templateId, schemaJar );
            }
            catch ( Exception e )
            {
                throw new SequencerException( ErrorCodeEnum.READ_REQUEST_TEMPLATE_ID_INVALID, templateId, e.getMessage() );
            }

        }

        return schemaJar;
    }


    @Resource
    public void setTemplateManager( TemplateManager templateManager )
    {
        this.templateManager = templateManager;
    }

}
