/**

 * 

 */


package gov.va.med.cds.socket.server.handler;


import gov.va.med.cds.exception.ErrorCodeEnum;
import gov.va.med.cds.hapi.HL7SupportException;
import gov.va.med.cds.hapi.HL7SupportInterface;

import java.util.List;
import java.util.Map;
import java.util.Set;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;


/**
 * 
 * Class used to check whether the incoming message can be passed thru to the
 * message mediator for further processing. Checks are based on the information
 * in the MSH segment of the incoming HL7 message. A map of the parameter to
 * check for and its sequence number in the MSH segment is used to extract the
 * value from the incoming HL7 message and compared against the allowable values
 * for that parameter.
 * 
 * The methods in the class throw an AccessValidationException if: 1. The
 * incoming HL7 message cannot be parsed using HAPI 2. The incoming HL7 message
 * does not have the expected sequence in the MSH segment 3. Any MSH segment
 * sequence value does not match with an allowable value for that sequence in
 * MSH
 * 
 * @author susarlan
 * 
 */

public class MllpAccessValidator
    implements
        AccessValidatorInterface
{

    // Declare logger
    private static Log logger = LogFactory.getLog( MllpAccessValidator.class );

    // Allowable parameter values for each access parameter
    // (/MSH-4, {CHDR}), (/MSH-9, {QBP}) and others
    private Map<String, Map<String, List<String>>> allowableParameterValues = null;


    /*
     * (non-Javadoc)
     * 
     * @see
     * gov.va.med.cds.socket.server.handler.AccessValidatorInterface#validateAccessT
     * (gov.va.med.cds.hapi.HL7SupportInterface)
     */
    public void validateAccess( HL7SupportInterface hl7Support )
    {

        // Check if values in message match allowable values
        checkAccess( hl7Support );

    }


    /**
     * Check if the value in the incoming request matches the allowable values
     * 
     */
    private void checkAccess( HL7SupportInterface hl7Support )
        throws AccessValidatorException
    {
        try
        {
            if ( hl7Support == null )
            {
                throw new AccessValidatorException( ErrorCodeEnum.HL7_SUPPORT_HAPI_PARSE_EXCEPTION );
            }

            String allowableParametesForMessageType = hl7Support.getSendingApplication();
            Map<String, List<String>> clientSpecificAllowableParameterValues = allowableParameterValues.get( allowableParametesForMessageType );

            if ( clientSpecificAllowableParameterValues == null )
            {
                String valueInReq = hl7Support.getSendingApplication();
                String receivingValueInReq = hl7Support.getReceivingApplication();
                String spec = "/MSH-3";
                String secondSpec = "/MSH-5";
                throw new AccessValidatorException( ErrorCodeEnum.SOCKET_ADAPTER_ACCESS_DENIED_EXCEPTION_COMBINATION, valueInReq, receivingValueInReq, spec, secondSpec );

            }

            // Get the parameters to check for
            Set<String> terserStrings = clientSpecificAllowableParameterValues.keySet();

            for ( String spec : terserStrings )
            {

                try
                {

                    // Get the parameter value from the request
                    String valueInReq = hl7Support.getMessageData( spec );

                    if ( valueInReq == null )
                    {

                        logger.warn( "No value found for sequence: " + spec );

                        // set up value in request as an empty string to avoid
                        // testing for null values in the processing
                        valueInReq = "";

                    }

                    // Get the list of allowable values
                    List<String> allowedValues = clientSpecificAllowableParameterValues.get( spec );

                    // Compare value in request against allowable values
                    boolean isAllowable = false;

                    for ( String val : allowedValues )
                    {

                        if ( valueInReq.equals( val ) )
                        {

                            // Value in request is a permitted value
                            isAllowable = true;

                            break;
                        }

                    }

                    if ( !isAllowable )
                    {

                        logger.error( "Value " + valueInReq + " not allowed at sequence " + spec );

                        // Value in request for parameter does not match with
                        // any allowable value
                        AccessValidatorException vex = new AccessValidatorException( ErrorCodeEnum.SOCKET_ADAPTER_ACCESS_DENIED_EXCEPTION,
                                        valueInReq, spec );

                        // appLogger.logMessage( "Value " + valueInReq +
                        // " not allowed at sequence " + spec, vex );
                        throw vex;
                    }

                }
                catch ( HL7SupportException e )
                {

                    throw new AccessValidatorException( ErrorCodeEnum.HL7_SUPPORT_HAPI_TERSE_EXCEPTION, e, spec );

                }

            }
        }
        catch ( AccessValidatorException ave )
        {
            throw ave;
        }
        catch ( Exception ex )
        {
            throw new AccessValidatorException( ErrorCodeEnum.SOCKET_ADAPTER_ACCESS_UNEXPECTED_EXCEPTION, ex );
        }

    }


    /**
     * Set allowable parameter values map
     * 
     * @param allowableParameterValues
     *            - Map of permitted/allowable values for each access parameter
     */
    public void setAllowableParameterValues( Map<String, Map<String, List<String>>> allowableParameterValues )
    {

        this.allowableParameterValues = allowableParameterValues;

    }

}