package uk.nhs.kch.rassyeyanie.framework.route;

import static org.apache.camel.component.hl7.HL7.terser;

import java.util.Map;

import org.apache.camel.CamelContext;
import org.apache.camel.Exchange;
import org.apache.camel.ExchangePattern;
import org.apache.camel.LoggingLevel;
import org.apache.camel.Processor;
import org.apache.camel.Route;
import org.apache.camel.component.hl7.HL7Constants;
import org.apache.camel.component.mina.MinaConstants;
import org.apache.camel.model.ChoiceDefinition;
import org.apache.camel.model.RouteDefinition;
import org.apache.commons.lang.StringUtils;
import org.apache.mina.common.IoSession;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import uk.nhs.kch.rassyeyanie.framework.HL7AdditionalConstants;

public class GenericListener
    extends AbstractListener
{
    
    private static final Logger logger = LoggerFactory
        .getLogger(GenericListener.class);
    
    private String queue;
    private int workerTotal;
    
    public String getQueue()
    {
        return this.queue;
    }
    
    public void setQueue(String queue)
    {
        this.queue = queue;
    }
    
    @Override
    protected void createRoute(ListenerConfig listenerConfig)
    {
        String endpoint = listenerConfig.getEndpoint();
        boolean autoStart = listenerConfig.isAutoStart();
        final String routeName = listenerConfig.getName();
        
        if (StringUtils.isEmpty(routeName)) { throw new IllegalArgumentException(
            "routeName"); }
        
        if (StringUtils.isEmpty(endpoint)) { throw new IllegalArgumentException(
            "endpoint"); }
        
        logger.info("Attempting to create route {}", routeName);
        
        RouteDefinition route = this.from(endpoint);
        route.routeId(routeName);
        route.autoStartup(autoStart);
        route.process(new Processor() {
            
            @Override
            public void process(Exchange exchange)
                throws Exception
            {
                String connectionStatus = "Connection Status";
                CamelContext camelContext = exchange.getContext();
                Route camelRoute = camelContext.getRoute(routeName);
                Map<String, Object> routeProperties =
                    camelRoute.getProperties();
                
                IoSession session =
                    (IoSession) exchange.getIn().getHeader(
                        MinaConstants.MINA_IOSESSION);
                
                routeProperties.put(connectionStatus, new IoSessionStatus(
                    session));
                
            }
        });
        
        this
            .createExceptionBlock(route, listenerConfig.getExceptionProcessor());
        
        if (listenerConfig.getPreFormatter() != null)
        {
            route.unmarshal(listenerConfig.getPreFormatter());
        }
        
        if (listenerConfig.getIcmAckCleaner() != null)
        {
            route.process(listenerConfig.getIcmAckCleaner());
        }
        
        route.unmarshal().custom("hl7dataformat");
        route
            .choice()
            .when(this.body().contains("PID"))
            .setHeader(
                HL7AdditionalConstants.HL7_EXTERNAL_PATIENT_ID,
                terser("/.PID-2"))
            .setHeader(
                HL7AdditionalConstants.HL7_INTERNAL_PATIENT_ID,
                terser("/.PID-3"))
            .end();
        route.convertBodyTo(String.class);
        route.process(new HeaderOverrideProcessor(listenerConfig, this
            .getWorkerTotal()));
        
        ChoiceDefinition workerChoice = route.choice();
        for (int i = 0; i < this.getWorkerTotal(); i++)
        {
            workerChoice
                .when(
                    this
                        .header(HL7AdditionalConstants.HL7_PATIENT_GROUP)
                        .isEqualTo(i + ""))
                .setExchangePattern(ExchangePattern.InOnly)
                .toF(this.queue, i)
                .log(
                    String
                        .format(
                            "Processing MsgId: ${header.%s} / ${id} / %s / ${header.%s} / ${header.%s}",
                            HL7Constants.HL7_MESSAGE_CONTROL,
                            i,
                            HL7AdditionalConstants.HL7_INTERNAL_PATIENT_ID,
                            HL7AdditionalConstants.HL7_EXTERNAL_PATIENT_ID));
        }
        
        workerChoice
            .otherwise()
            .setExchangePattern(ExchangePattern.InOnly)
            .toF(this.queue, 0)
            .log(
                String
                    .format(
                        "Processing MsgId: ${header.%s} / ${id} / %s / ${header.%s} / ${header.%s}",
                        HL7Constants.HL7_MESSAGE_CONTROL,
                        "default",
                        HL7AdditionalConstants.HL7_INTERNAL_PATIENT_ID,
                        HL7AdditionalConstants.HL7_EXTERNAL_PATIENT_ID));
        
        // log message
        route.log(String.format(
            "Processing MsgId: ${header.%s} / ${id}",
            HL7Constants.HL7_MESSAGE_CONTROL));
        route.log("body");
        route.unmarshal().custom("hl7dataformat");
        // acknowledge
        this.createAcknowledgementBlock(
            route,
            listenerConfig.getAcknowledgementProcessor());
        
        // log acknowledgement
        route.log("body");
    }
    
    private void createAcknowledgementBlock(RouteDefinition route,
                                            Processor acknowledgementProcessor)
    {
        route.process(acknowledgementProcessor);
    }
    
    private void createExceptionBlock(RouteDefinition route,
                                      Processor exceptionHandler)
    {
        route
            .onException(Exception.class)
            .log(LoggingLevel.ERROR, this.exceptionMessage().toString())
            .to(ERROR_LOG)
            .handled(true)
            .process(exceptionHandler)
            .end();
    }
    
    public int getWorkerTotal()
    {
        return this.workerTotal;
    }
    
    public void setWorkerTotal(int workerTotal)
    {
        this.workerTotal = workerTotal;
    }
    
    private class IoSessionStatus
    {
        private final IoSession session;
        
        public IoSessionStatus(IoSession session)
        {
            this.session = session;
        }
        
        @Override
        public String toString()
        {
            return this.session.isConnected() ? "Connected" : "Disconnected";
        }
        
    }
}

@@ -340,16 +340,66 @@
 .Route;%0A
+import org.apache.camel.builder.PredicateBuilder;%0A
 import o
@@ -3205,35 +3205,549 @@
 hen(
-this.body().c
+PredicateBuilder.in(%0A            %09%09this.header(HL7Constants.HL7_MESSAGE_TYPE).isEqualTo(%22ORM%22),%0A            %09%09this.header(HL7Constants.HL7_MESSAGE_TYPE).isEqualTo(%22ORU%22),%0A            %09%09this.header(HL7C
 on
+s
 ta
-ins(%22PID%22)
+nts.HL7_MESSAGE_TYPE).isEqualTo(%22ORG%22),%0A            %09%09this.header(HL7Constants.HL7_MESSAGE_TYPE).isEqualTo(%22OMG%22),%0A                    PredicateBuilder.and(this.header(HL7Constants.HL7_MESSAGE_TYPE).isEqualTo(%22ADT%22),%0A                    %09%09this.header(HL7Constants.HL7_TRIGGER_EVENT).isNotEqualTo(%22ACK%22))%0A%0A            %09%09)%0A            %09%09
 )%0A  

Fix to issue around patient hashing