/*
 *  Licensed to the Apache Software Foundation (ASF) under one
 *  or more contributor license agreements.  See the NOTICE file
 *  distributed with this work for additional information
 *  regarding copyright ownership.  The ASF licenses this file
 *  to you under the Apache License, Version 2.0 (the
 *  "License"); you may not use this file except in compliance
 *  with the License.  You may obtain a copy of the License at
 *
 *    http://www.apache.org/licenses/LICENSE-2.0
 *
 *  Unless required by applicable law or agreed to in writing,
 *  software distributed under the License is distributed on an
 *  "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
 *  KIND, either express or implied.  See the License for the
 *  specific language governing permissions and limitations
 *  under the License.
 *
 */
package org.apache.mina.filterchain;

import org.apache.mina.api.IoFilter;
import org.apache.mina.api.IoService;
import org.apache.mina.api.IoSession;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

public class DefaultIoFilterController implements IoFilterController, ReadFilterChainController,
        WriteFilterChainController {

    private static final Logger LOG = LoggerFactory.getLogger(DefaultIoFilterController.class);

    /**
     * The list of {@link IoFilter} implementing this chain.
     */
    private final IoFilter[] chain;

    /**
     * The instance of {@link DefaultIoFilterController} with the {@link IoService} chain.
     */
    public DefaultIoFilterController(IoFilter[] chain) {
        this.chain = chain;
    }

    @Override
    public void processSessionCreated(IoSession session) {
        LOG.debug("processing session created event");
        for (IoFilter filter : chain) {
            filter.sessionCreated(session);
        }
    }

    @Override
    public void processSessionOpen(IoSession session) {
        LOG.debug("processing session open event");
        for (IoFilter filter : chain) {
            filter.sessionOpened(session);
        }
    }

    @Override
    public void processSessionClosed(IoSession session) {
        LOG.debug("processing session closed event");
        for (IoFilter filter : chain) {
            filter.sessionClosed(session);
        }
    }

    private int readChainPosition;

    @Override
    public void processMessageReceived(IoSession session, Object message) {
        LOG.debug("processing message '{}' received event ", message);
        if (chain.length < 1) {
            LOG.debug("Nothing to do, the chain is empty");
        } else {
            readChainPosition = 0;
            // we call the first filter, it's supposed to call the next ones using the filter chain controller
            chain[readChainPosition].messageReceived(session, message, this);
        }
    }

    private int writeChainPosition;

    @Override
    public void processMessageWriting(IoSession session, Object message) {
        LOG.debug("processing message '{}' writing event ", message);
        if (chain.length < 1) {
            LOG.debug("Nothing to do, the chain is empty, we just enqueue the message");
            session.enqueueWriteRequest(message);
        } else {
            writeChainPosition = chain.length - 1;
            // we call the first filter, it's supposed to call the next ones using the filter chain controller
            chain[writeChainPosition].messageWriting(session, message, this);
        }
    }

    @Override
    public void callWriteNextFilter(IoSession session, Object message) {
        LOG.debug("calling next filter for writing for message '{}' position : {}", message, writeChainPosition);
        writeChainPosition--;
        if (writeChainPosition < 0 || chain.length == 0) {
            // end of chain processing
            LOG.debug("end of write chan we enque the message in the session : {}", message);
            session.enqueueWriteRequest(message);
        } else {
            chain[writeChainPosition].messageWriting(session, message, this);
        }
        writeChainPosition++;
    }

    @Override
    public void callReadNextFilter(IoSession session, Object message) {
        readChainPosition++;
        if (readChainPosition >= chain.length) {
            // end of chain processing
        } else {
            chain[readChainPosition].messageReceived(session, message, this);
        }
        readChainPosition--;
    }

    @Override
    public String toString() {
        StringBuilder bldr = new StringBuilder("IoFilterChain {");
        int index = 0;
        for (IoFilter filter : chain) {
            bldr.append(index).append(":").append(filter).append(", ");
        }
        return bldr.append("}").toString();
    }
}
@@ -2265,29 +2265,88 @@
 ate 
-int readChainPosition
+static final ThreadLocal%3CInteger%3E readChainPosition = new ThreadLocal%3CInteger%3E()
 ;%0A%0A 
@@ -2643,20 +2643,23 @@
 Position
- = 0
+.set(0)
 ;%0A      
@@ -2790,32 +2790,38 @@
 eadChainPosition
+.get()
 %5D.messageReceive
@@ -2880,30 +2880,89 @@
 ate 
-int writeChainPosition
+static final ThreadLocal%3CInteger%3E writeChainPosition = new ThreadLocal%3CInteger%3E()
 ;%0A%0A 
@@ -3299,32 +3299,33 @@
        %7D else %7B%0A
+%0A
             writ
@@ -3338,19 +3338,21 @@
 Position
- = 
+.set(
 chain.le
@@ -3359,16 +3359,17 @@
 ngth - 1
+)
 ;%0A      
@@ -3501,32 +3501,38 @@
 iteChainPosition
+.get()
 %5D.messageWriting
@@ -3653,32 +3653,72 @@
 ject message) %7B%0A
+        if (LOG.isDebugEnabled()) %7B%0A    
         LOG.debu
@@ -3793,16 +3793,36 @@
 message,
+%0A                   
  writeCh
@@ -3832,27 +3832,43 @@
 Position
+.get()
 );%0A  
+ 
+     %7D%0A
       
+  
 writeCha
@@ -3873,26 +3873,58 @@
 hainPosition
---
+.set(writeChainPosition.get() - 1)
 ;%0A        if
@@ -3943,16 +3943,22 @@
 Position
+.get()
  %3C 0 %7C%7C 
@@ -4206,32 +4206,38 @@
 iteChainPosition
+.get()
 %5D.messageWriting
@@ -4294,26 +4294,58 @@
 hainPosition
-++
+.set(writeChainPosition.get() + 1)
 ;%0A    %7D%0A%0A   
@@ -4456,10 +4456,41 @@
 tion
-++
+.set(readChainPosition.get() + 1)
 ;%0A  
@@ -4516,16 +4516,22 @@
 Position
+.get()
  %3E= chai
@@ -4633,16 +4633,22 @@
 Position
+.get()
 %5D.messag
@@ -4721,10 +4721,41 @@
 tion
---
+.set(readChainPosition.get() - 1)
 ;%0A  

Use ThreadLocal for walking chain of filter