

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


import org.dom4j.Attribute;
import org.dom4j.Document;
import org.dom4j.DocumentException;
import org.dom4j.DocumentHelper;
import org.dom4j.Element;
import org.dom4j.Node;
import org.dom4j.io.SAXReader;
import org.springframework.core.io.ClassPathResource;
import org.springframework.util.StringUtils;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Scanner;


public class ReferenceMappingVistaResponseBlobStrategy
    implements
        VistaStoredProcedureResponseStrategyInterface
{
    public static final String TYPE_ATTRIBUTE = "type";
    public static final String COLUMN_ATTRIBUTE = "column";
    public static final String FORMULA_ATTRIBUTE = "formula";
    public static final String REQUIRED_ATTRIBUTE = "required";
    public static final String REPEATING_ELEMENT = "REPEATING_ELEMENT";
    private static final String TYPE = "type";

    private Map<String, ClassPathResource> refXmlMap;


    protected List<Map<String, Object>> formatResultList( List<Map<String, Object>> resultList )
        throws IOException
    {
        BufferedReader reader = new BufferedReader( new InputStreamReader( new java.io.ByteArrayInputStream( ( byte[] )resultList.get( 0 ).get(
                        "stream" ) ) ) );

        StringBuffer sb;
        sb = new StringBuffer( reader.readLine() );

        sb.delete( 0, 5 );
        sb.delete( sb.length() - 7, sb.length() );

        Scanner scanner = new Scanner( sb.toString() ).useDelimiter( "\\^" );
        List<String> columnNames = new ArrayList<String>();
        while ( scanner.hasNext() )
        {
            columnNames.add( scanner.next() );
        }

        columnNames.add( "SITE ID" );
        String siteID = null;
        if ( resultList.get( 0 ).get( "siteID" ) != null )
        {
            siteID = ( String )resultList.get( 0 ).get( "siteID" );
        }

        //Retrieve each row
        List<Map<String, Object>> newResultList = new ArrayList<Map<String, Object>>();

        String s = null;

        while ( ( s = reader.readLine() ) != null )
        {
            if ( s.length() > 0 )
            {
                Map<String, Object> rowMap = new HashMap<String, Object>();
                scanner = new Scanner( s ).useDelimiter( "\\^" );
                for ( String columnName : columnNames )
                {
                    if ( columnName.startsWith( "SITE ID" ) )
                    {
                        rowMap.put( columnName, siteID );
                    }
                    else if ( scanner.hasNext() )
                    {
                        String token = scanner.next();
                        if ( token != null && token.length() > 0 )
                        {
                            rowMap.put( columnName, token );
                        }
                    }
                }
                newResultList.add( rowMap );
            }
        }

        return newResultList;
    }


    @Override
    public Element formatResponse( List<Map<String, Object>> resultList, String domainEntryPoint, String templateId )
        throws Exception
    {
        resultList = formatResultList( resultList );
        Element results = DocumentHelper.createElement( "results" );
        for ( Map<String, Object> map : resultList )
        {
            Document refDoc = getRefDocument( domainEntryPoint, templateId );
            treeWalk( refDoc.getRootElement(), map );
            results.add( refDoc.getRootElement() );
        }

        return results;
    }


    protected Document getRefDocument( String domainEntryPoint, String templateId )
        throws DocumentException,
            IOException
    {
        Document doc = null;
        InputStream is = ( ( ClassPathResource )refXmlMap.get( domainEntryPoint + "-" + templateId ) ).getInputStream();
        try
        {
            doc = new SAXReader().read( is );
        }
        finally
        {
            if ( is != null )
            {
                try
                {
                    is.close();
                }
                finally
                {
                    is = null;
                }
            }
        }
        
        return doc;
    }


    protected void treeWalk( Element parentElement, Map<String, Object> map )
    {
        for ( int i = 0, size = parentElement.nodeCount(); i < size; i++ )
        {
            Node node = parentElement.node( i );
            if ( node instanceof Element )
            {
                Element element = ( Element )node;
                Attribute columnAttribute = element.attribute( COLUMN_ATTRIBUTE );
                if ( columnAttribute != null && columnAttribute.getValue() != null )
                {
                    String rawAttributeValue = columnAttribute.getValue();

                    String attributeValue = rawAttributeValue;
                    String constantAppendix = null;
                    if ( rawAttributeValue.indexOf( "||" ) > 0 )
                    {
                        Scanner scanner = new Scanner( rawAttributeValue ).useDelimiter( "\\|\\|" );
                        attributeValue = scanner.next();
                        constantAppendix = scanner.next();
                    }

                    String columnValue = ( String )map.get( attributeValue );
                    if ( columnValue != null && columnValue.length() > 0 )
                    {
                        if ( constantAppendix != null )
                        {
                            element.setText( columnValue + constantAppendix );
                        }
                        else
                        {
                            Attribute typeAttribute = element.getParent().attribute( TYPE );                            
                            if ( columnValue.contains( "~" ) && ( typeAttribute != null && typeAttribute.getValue() != null ) && ( REPEATING_ELEMENT.equals( typeAttribute.getValue() ) ) )
                            {
                                element.getParent().attribute( TYPE ).detach();

                                Scanner scanner = new Scanner( columnValue ).useDelimiter( "~" );

                                do
                                {
                                    String repeatingElementValue = scanner.next();

                                    if ( element.getText().equals( "" ) )
                                    {
                                        element.setText( repeatingElementValue );
                                    }
                                    else
                                    {
                                        Element repeatingElement = DocumentHelper.createElement( element.getName() );

                                        repeatingElement.setText( repeatingElementValue );

                                        element.getParent().add( repeatingElement );
                                    }
                                }
                                while ( scanner.hasNext() );
                            }
                            else
                            {
                                element.setText( columnValue );
                            }
                        }
                    }

                    element.remove( columnAttribute );
                }
                else
                {
                    treeWalk( element, map );
                }
            }
        }

        for ( int i = 0, size = parentElement.nodeCount(); i < size; i++ )
        {
            Node node = parentElement.node( i );
            if ( node instanceof Element )
            {
                Element element = ( Element )node;

                if ( element.elements().size() == 0 && ( element.getTextTrim() == null || element.getTextTrim().length() == 0 ) )
                {
                    parentElement.remove( element );
                }
                else
                {
                    Attribute requiredAttribute = element.attribute( REQUIRED_ATTRIBUTE );
                    if ( requiredAttribute != null && requiredAttribute.getValue() != null )
                    {
                        Element requiredElement = parentElement.element( requiredAttribute.getValue() );
                        if ( requiredElement == null )
                        {
                            parentElement.remove( element );
                        }
                        else
                        {
                            element.remove( requiredAttribute );
                        }
                    }
                }
            }
        }
    }


    public Map<String, ClassPathResource> getRefXmlMap( )
    {
        return refXmlMap;
    }


    public void setRefXmlMap( Map<String, ClassPathResource> refXmlMap )
    {
        this.refXmlMap = refXmlMap;
    }
}
