

package gov.va.med.cds.mediator.formatter;


import gov.va.med.cds.hapi.HL7SupportException;
import gov.va.med.cds.hapi.HL7SupportHAPI;
import gov.va.med.cds.hapi.HL7SupportInterface;
import gov.va.med.cds.mediator.work.WorkStateInterface;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.dom4j.Document;
import org.dom4j.DocumentHelper;
import org.dom4j.Element;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;


public class ChdrBatchResponseFormatter
    extends
        AbstractXsltToHl7ResponseFormatter
{
    private static final String BATCH_SEGMENTS = "BHS|ERR|QPD|RCP|BTS";
    private static final String BTS_FINAL_COUNT = "{FINAL_BTS_COUNT}";
    
    private Log logger = LogFactory.getLog( ChdrBatchResponseFormatter.class );


    public String formatResponse( HL7SupportInterface hl7Support, String unformatted, WorkStateInterface workState )
    {
        // do the transform from VHIM to HL7-XML
        Map<String, Object> parameters = new HashMap<String, Object>();
        parameters.put( "global.hl7Support", hl7Support );
        parameters.put( "global.workState", workState );
        parameters.put( "global.hl7Utility", hl7Utility );

        Document batchDocument = xsltHelper.transformMessage( unformatted, stylesheet, parameters );

        List<Document> clinicalDocuments = extractClinicalMessagesFromDocument( batchDocument );
        List<String> clinicalMessages = convertClinicalDocumentsToClinicalMessages( clinicalDocuments, workState );
        String batchSegments = createBatchSegmentsFromBatchDocument( batchDocument );

        return createCompletedBatchMessage( batchSegments, clinicalMessages );
    }


    private String createCompletedBatchMessage( String batchSegments, List<String> clinicalMessages )
    {
        StringBuilder sbBatchMessage = new StringBuilder();

        for ( String batchSegment : batchSegments.split( "\r" ) )
        {
            if ( batchSegment.startsWith( "BTS" ) )
            {
                for ( String clinicalMessage : clinicalMessages )
                {
                    sbBatchMessage.append( clinicalMessage );
                }
            }

            sbBatchMessage.append( batchSegment ).append( '\r' );
        }

        return adjustFinalBtsCount(sbBatchMessage.toString(), clinicalMessages.size());
    }
    
    private String adjustFinalBtsCount(String batchMessage, int count){
        
       return batchMessage.replace( BTS_FINAL_COUNT, count+"" );
    }


    @SuppressWarnings( "unchecked" )
    private List<Document> extractClinicalMessagesFromDocument( Document batchDocument )
    {
        Element element = null;
        Iterator i = batchDocument.getRootElement().elements().iterator();
        List<Document> clinicalDocuments = new ArrayList<Document>();

        while ( i.hasNext() )
        {
            // grab the next segment
            element = ( Element )i.next();

            // if it's not one of the batch segments, add the element to our list to process
            if ( !isBatchSegmentElement( element ) )
            {
                clinicalDocuments.add( DocumentHelper.createDocument( element.createCopy() ) );
            }
        }

        return clinicalDocuments;
    }


    private boolean isBatchSegmentElement( Element segmentElement )
    {
        return BATCH_SEGMENTS.contains( segmentElement.getName() );
    }


    private List<String> convertClinicalDocumentsToClinicalMessages( List<Document> clinicalDocuments, WorkStateInterface workState )
    {
        List<String> clinicalMessages = new ArrayList<String>();
        for ( Document clinicalDocument : clinicalDocuments )
        {
            try
            {
                /*
                 * if any of these messages fail parse/validation the rest of the
                 * records that do not fail parsing/validation must be returned
                */
                HL7SupportInterface hl7SupportIf = HL7SupportHAPI.createTranslationSupport( clinicalDocument.asXML() );
                String er7 = hl7SupportIf.toEr7();
                clinicalMessages.add( er7 );
            }
            catch ( HL7SupportException hse )
            {
                //log the exception - CHDR does not want these exceptions returned
                logger.error( hse );
            }
        }

        return clinicalMessages;

    }


    @SuppressWarnings( "unchecked" )
    private String createBatchSegmentsFromBatchDocument( Document batchDocument )
    {
        Element rootElement = DocumentHelper.createElement( batchDocument.getRootElement().getName() );
        Document temporaryDocument = DocumentHelper.createDocument( rootElement );

        Iterator i = batchDocument.getRootElement().elements().iterator();
        Element e = null;

        while ( i.hasNext() )
        {
            e = ( Element )i.next();
            if ( isBatchSegmentElement( e ) )
            {
                rootElement.add( e.createCopy() );
            }
        }

        String[] batchSegments = HL7SupportHAPI.createTranslationSupport( temporaryDocument.asXML() ).toEr7().split( "\r" );
        StringBuilder sbBatchMessage = new StringBuilder();

        appendSingleSegment( "BHS", batchSegments, sbBatchMessage );
        appendAllSegments( "ERR", batchSegments, sbBatchMessage );
        appendSingleSegment( "QPD", batchSegments, sbBatchMessage );
        appendSingleSegment( "RCP", batchSegments, sbBatchMessage );
        appendSingleSegment( "BTS", batchSegments, sbBatchMessage );

        return sbBatchMessage.toString();
    }


    private static void appendSingleSegment( String startsWith, String[] segments, StringBuilder sbAppendTo )
    {
        for ( String segment : segments )
        {
            if ( segment.startsWith( startsWith ) )
            {
                sbAppendTo.append( segment ).append( '\r' );
                break;
            }
        }
    }


    private static void appendAllSegments( String startsWith, String[] segments, StringBuilder sbAppendTo )
    {
        for ( String segment : segments )
        {
            if ( segment.startsWith( startsWith ) )
            {
                sbAppendTo.append( segment ).append( '\r' );
            }
        }
    }

}
