/*
 * Copyright 2016 higherfrequencytrading.com
 *
 * 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 net.openhft.chronicle.queue;

import net.openhft.chronicle.bytes.Bytes;
import net.openhft.chronicle.core.Jvm;
import net.openhft.chronicle.core.OS;
import net.openhft.chronicle.wire.*;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.util.HashSet;
import java.util.Set;

/**
 * Created by peter on 24/03/16.
 */
public class MethodReader {
    private static final Logger LOGGER = LoggerFactory.getLogger(MethodReader.class);
    private final ExcerptTailer tailer;
    private final WireParser<Void> wireParser;

    public MethodReader(ExcerptTailer tailer, Object... objects) {
        this.tailer = tailer;
        WireParselet defaultParselet = (s, v, $) ->
                LOGGER.warn("Unknown message " + s + ' ' + v.text());
        if (objects[0] instanceof WireParselet)
            defaultParselet = (WireParselet) objects[0];
        wireParser = WireParser.wireParser(defaultParselet);

        Set<String> methodsHandled = new HashSet<>();
        for (Object o : objects) {
            for (Method m : o.getClass().getMethods()) {
                if (Modifier.isStatic(m.getModifiers()))
                    continue;

                try {
                    Object.class.getMethod(m.getName(), m.getParameterTypes());
                    continue;
                } catch (NoSuchMethodException e) {
                    // not an Object method.
                }

                if (!methodsHandled.add(m.getName()))
                    continue;

                Class<?>[] parameterTypes = m.getParameterTypes();
                if (parameterTypes.length != 1)
                    continue;

                m.setAccessible(true); // turn of security check to make a little faster
                Class msgClass = parameterTypes[0];
                ReadMarshallable arg;
                try {
                    arg = (ReadMarshallable) msgClass.newInstance();
                } catch (Exception e) {
                    arg = (ReadMarshallable) OS.memory().allocateInstance(msgClass);
                }
                ReadMarshallable[] argArr = {arg};
                wireParser.register(m::getName, (s, v, $) -> {
                    try {
                        if (Jvm.isDebug())
                            logMessage(s, v);

                        v.marshallable(argArr[0]);
                        m.invoke(o, argArr);
                    } catch (Exception i) {
                        LOGGER.error("Failure to dispatch message: " + m.getName() + " " + argArr[0], i);
                    }
                });
            }
        }

        if (wireParser.lookup("history") == null) {
            wireParser.register(() -> "history", (s, v, $) -> {
                v.marshallable(ExcerptHistory.get());
            });
        }
    }

    static void logMessage(CharSequence s, ValueIn v) {
        String name = s.toString();
        String rest;

        if (v.wireIn() instanceof BinaryWire) {
            Bytes bytes = Bytes.elasticByteBuffer((int) (v.wireIn().bytes().readRemaining() * 3 / 2 + 64));
            long pos = v.wireIn().bytes().readPosition();
            v.wireIn().copyTo(new TextWire(bytes));
            v.wireIn().bytes().readPosition(pos);
            rest = bytes.toString();
        } else {
            rest = v.toString();
        }
        LOGGER.debug("read " + name + " - " + rest);
    }

    /**
     * reads one message
     *
     * @return true if there was a message, or false if not.
     */
    public boolean readOne() {
        ExcerptHistory.get().reset();
        try (DocumentContext context = tailer.readingDocument()) {
            if (!context.isData())
                return false;
            wireParser.accept(context.wire(), null);
        }
        return true;
    }
}

@@ -2307,32 +2307,84 @@
      continue;%0A%0A
+                Class msgClass = parameterTypes%5B0%5D;%0A
                 
@@ -2476,44 +2476,680 @@
     
-Class msgClass = parameterTypes%5B0%5D;%0A
+if (msgClass.isInterface() %7C%7C !ReadMarshallable.class.isAssignableFrom(msgClass)) %7B%0A                    Object%5B%5D argArr = %7Bnull%7D;%0A                    wireParser.register(m::getName, (s, v, $) -%3E %7B%0A                        try %7B%0A                            if (Jvm.isDebug())%0A                                logMessage(s, v);%0A%0A                            argArr%5B0%5D = v.object(msgClass);%0A                            m.invoke(o, argArr);%0A                        %7D catch (Exception i) %7B%0A                            LOGGER.error(%22Failure to dispatch message: %22 + m.getName() + %22 %22 + argArr%5B0%5D, i);%0A                        %7D%0A                    %7D);%0A%0A                %7D else %7B%0A    
     
@@ -3190,38 +3190,46 @@
                 
+    
 try %7B%0A
+    
                 
@@ -3289,32 +3289,36 @@
                 
+  
+  
 %7D catch (Excepti
@@ -3317,32 +3317,36 @@
 (Exception e) %7B%0A
+    
                 
@@ -3422,34 +3422,40 @@
                 
-%7D%0A
+    %7D%0A  
                 
@@ -3446,32 +3446,34 @@
                 
+  
 ReadMarshallable
@@ -3491,16 +3491,20 @@
  %7Barg%7D;%0A
+    
         
@@ -3570,32 +3570,36 @@
                 
+    
 try %7B%0A          
@@ -3604,32 +3604,36 @@
                 
+  
+  
 if (Jvm.isDebug(
@@ -3627,32 +3627,36 @@
 (Jvm.isDebug())%0A
+    
                 
@@ -3702,32 +3702,36 @@
                 
+    
 v.marshallable(a
@@ -3757,32 +3757,36 @@
                 
+  
+  
 m.invoke(o, argA
@@ -3782,32 +3782,36 @@
 oke(o, argArr);%0A
+    
                 
@@ -3830,32 +3830,36 @@
 (Exception i) %7B%0A
+    
                 
@@ -3960,34 +3960,42 @@
                 
+  
-%7D%0A
+  %7D%0A    
                 
@@ -3990,32 +3990,50 @@
             %7D);%0A
+                %7D%0A
             %7D%0A  

Add a multi-threaded performance test