package org.bdware.sc.conn;

import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.bdware.sc.get.GetMessage;
import org.bdware.sc.util.JsonUtil;

import java.io.ByteArrayOutputStream;
import java.io.PrintStream;
import java.lang.reflect.Method;
import java.util.HashMap;
import java.util.Map;

public class MsgHandler {
    private static final Logger LOGGER = LogManager.getLogger(MsgHandler.class);
    Map<String, MethodInfo> methodInfos;

    public MsgHandler() {
        methodInfos = new HashMap<>();
        Class<?> clz = this.getClass();
        for (Method method : clz.getDeclaredMethods()) {
            Description anno = method.getAnnotation(Description.class);
            if (anno != null && method.getParameterCount() == 2) {
                methodInfos.put(
                        method.getName(),
                        new MethodInfo(method.getName(), anno.value(), method, anno.isAsync()));
            }
        }
    }

    public void handle(GetMessage message, ResultCallback rc) {
        try {
            MethodInfo info = methodInfos.get(message.method);
            if (info == null) {
                rc.onResult("{ \"msg\":\"can't handle action:" + message.method + "\"}");
                LOGGER.warn("unsupported action: " + message.method);
            } else if (info.isAsync) {
                ServiceServer.executor.execute(() -> {
                    try {
                        info.m.invoke(MsgHandler.this, message, rc);
                    } catch (Exception e) {
                        ByteArrayOutputStream bo = new ByteArrayOutputStream();
                        e.printStackTrace(new PrintStream(bo));
                        rc.onResult("{ \"msg\":\"" + bo + "\"}");
                    }
                });
            } else {
                info.m.invoke(this, message, rc);
            }
        } catch (Throwable e) {
            Method m2;
            try {
                m2 = MsgHandler.class.getDeclaredMethod(
                        message.method, GetMessage.class, ResultCallback.class);
                m2.invoke(this, message, rc);
            } catch (Exception e1) {
                ByteArrayOutputStream bo = new ByteArrayOutputStream();
                e.printStackTrace(new PrintStream(bo));
                rc.onResult("{ \"msg\":\"" + bo + "\"}");
            }
        }
    }

    @Description("help info")
    public void help(GetMessage msg, ResultCallback cb) {
        cb.onResult(JsonUtil.toPrettyJson(methodInfos));
    }

    static class MethodInfo {
        transient Method m;
        String method;
        String desc;
        boolean isAsync;

        public MethodInfo(String name, String value, Method m, boolean async) {
            method = name;
            desc = value;
            isAsync = async;
            this.m = m;
        }
    }
}
