/**
 * Copyright 2010 OpenEngSB Division, Vienna University of Technology
 *
 * Licensed 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.openengsb.core.common.internal;

import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.net.URI;
import java.util.HashMap;
import java.util.Map;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.openengsb.core.common.OpenEngSBService;
import org.openengsb.core.common.communication.CallRouter;
import org.openengsb.core.common.communication.MethodCall;
import org.openengsb.core.common.communication.MethodReturn;
import org.openengsb.core.common.communication.Port;
import org.osgi.framework.BundleContext;
import org.osgi.framework.InvalidSyntaxException;
import org.osgi.framework.ServiceReference;
import org.springframework.osgi.context.BundleContextAware;

public class CallRouterImpl implements CallRouter, BundleContextAware {

    private Log log = LogFactory.getLog(CallRouterImpl.class);

    private class PortHandler implements Runnable {
        private Port port;

        public PortHandler(Port port) {
            this.port = port;
        }

        @Override
        public void run() {
            while (true) {
                MethodCall call = port.receive();
                String serviceId = call.getServiceId();

                ServiceReference[] allServiceReferences;
                try {
                    allServiceReferences =
                        bundleContext.getServiceReferences(OpenEngSBService.class.getName(), "(id=" + serviceId
                                + ")");
                } catch (InvalidSyntaxException e1) {
                    throw new RuntimeException(e1);
                }
                if (allServiceReferences == null) {
                    throw new IllegalArgumentException("service with id " + serviceId + " not found");
                }
                if (allServiceReferences.length != 1) {
                    throw new IllegalStateException("mutliple services matching (id=" + serviceId + ")");
                }
                Object service = bundleContext.getService(allServiceReferences[0]);
                Class<?>[] argTypes = getArgTypes(call.getArgs());

                Method method;
                try {
                    method = service.getClass().getMethod(call.getMethodName(), argTypes);
                } catch (NoSuchMethodException e) {
                    throw new IllegalArgumentException(e);
                }
                try {
                    method.invoke(service, call.getArgs());
                } catch (InvocationTargetException e) {
                    throw new RuntimeException(e);
                } catch (IllegalAccessException e) {
                    throw new RuntimeException(e);
                }

                log.info(call);
            }
        }

        private Class<?>[] getArgTypes(Object[] args) {
            // TODO Auto-generated method stub
            return null;
        }
    }

    private Map<String, Port> ports = new HashMap<String, Port>();
    private BundleContext bundleContext;

    @Override
    public void registerPort(String scheme, Port port) {
        ports.put(scheme, port);
        PortHandler portHandler2 = new PortHandler(port);
        new Thread(portHandler2).start();
    }

    @Override
    public void call(String portId, final URI destination, final MethodCall call) {
        final Port port = ports.get(portId);
        Runnable callHandler = new Runnable() {
            @Override
            public void run() {
                port.send(destination, call);
            }
        };
        new Thread(callHandler).start();
    }

    @Override
    public MethodReturn callSync(String portId, URI destination, MethodCall call) {
        // TODO Auto-generated method stub
        return null;
    }

    public void start() {

    }

    public void stop() {

    }

    @Override
    public void setBundleContext(BundleContext bundleContext) {
        this.bundleContext = bundleContext;
    }

}

@@ -827,16 +827,99 @@
 il.Map;%0A
+import java.util.concurrent.ExecutorService;%0Aimport java.util.concurrent.Executors;
 %0Aimport 
@@ -1608,24 +1608,97 @@
 pl.class);%0A%0A
+    private ExecutorService executor = Executors.newCachedThreadPool();%0A%0A
     private 
@@ -3960,17 +3960,16 @@
 tHandler
-2
  = new P
@@ -3994,26 +3994,31 @@
         
-new Thread
+executor.submit
 (portHan
@@ -4025,17 +4025,8 @@
 dler
-2).start(
 );%0A 
@@ -4359,18 +4359,23 @@
     
-new Thread
+executor.submit
 (cal
@@ -4387,16 +4387,8 @@
 ler)
-.start()
 ;%0A  
@@ -4616,24 +4616,55 @@
 id stop() %7B%0A
+        executor.shutdownNow();
 %0A    %7D%0A%0A    

use threadpool