package com.axis.innovators.box.events;

import java.lang.reflect.Method;
import java.util.*;

/**
 * 事件总线
 * @author tzdwindows 7
 */
public class EventBus {
    private static int maxID = 0;
    private final int busID;
    private final Map<Class<?>, List<Subscriber>> eventSubscribers = new HashMap<>();
    private final Map<Object, List<Subscriber>> targetSubscribers = new HashMap<>();
    private boolean shutdown;

    public EventBus() {
        this.busID = maxID++;
    }

    private static class Subscriber {
        final Object target;
        final Method method;
        final Class<?> eventType;

        Subscriber(Object target, Method method, Class<?> eventType) {
            this.target = target;
            this.method = method;
            this.eventType = eventType;
        }
    }

    /**
     * 注册目标对象的事件监听器
     * @param target 目标对象
     */
    public void register(Object target) {
        if (targetSubscribers.containsKey(target)) {
            return;
        }

        List<Subscriber> subs = new ArrayList<>();
        for (Method method : getAnnotatedMethods(target)) {
            SubscribeEvent annotation = method.getAnnotation(SubscribeEvent.class);
            if (annotation == null) {
                continue;
            }

            Class<?>[] paramTypes = method.getParameterTypes();
            if (paramTypes.length != 1) {
                continue;
            }

            Class<?> eventType = paramTypes[0];
            Subscriber sub = new Subscriber(target, method, eventType);
            eventSubscribers.computeIfAbsent(eventType, k -> new ArrayList<>()).add(sub);
            subs.add(sub);
        }

        if (!subs.isEmpty()) {
            targetSubscribers.put(target, subs);
        }
    }

    /**
     * 获取目标对象中所有带有 @SubscribeEvent 注解的方法
     * @param target 目标对象
     * @return 方法集合
     */
    private Set<Method> getAnnotatedMethods(Object target) {
        Set<Method> methods = new HashSet<>();
        Class<?> clazz = target.getClass();
        while (clazz != null) {
            for (Method method : clazz.getDeclaredMethods()) {
                if (method.isAnnotationPresent(SubscribeEvent.class)) {
                    methods.add(method);
                }
            }
            clazz = clazz.getSuperclass();
        }
        return methods;
    }

    /**
     * 注销目标对象的事件监听器
     * @param target 目标对象
     */
    public void unregister(Object target) {
        List<Subscriber> subs = targetSubscribers.remove(target);
        if (subs == null) {
            return;
        }

        for (Subscriber sub : subs) {
            List<Subscriber> eventSubs = eventSubscribers.get(sub.eventType);
            if (eventSubs != null) {
                eventSubs.remove(sub);
                if (eventSubs.isEmpty()) {
                    eventSubscribers.remove(sub.eventType);
                }
            }
        }
    }

    /**
     * 发布事件
     * @param event 事件对象
     * @return 返回事件是否被取消的状态
     */
    public boolean post(Object event) {
        if (shutdown) {
            return false;
        }

        boolean cancelled = false;
        Class<?> eventType = event.getClass();
        List<Subscriber> subs = eventSubscribers.get(eventType);
        if (subs == null) {
            return cancelled;
        }

        // 创建副本以避免并发修改异常
        List<Subscriber> copySubs = new ArrayList<>(subs);
        for (Subscriber sub : copySubs) {
            try {
                sub.method.setAccessible(true);
                sub.method.invoke(sub.target, event);
            } catch (Exception e) {
                handleException(event, e);
            }
        }
        return cancelled;
    }

    /**
     * 关闭事件总线，停止处理事件
     */
    public void shutdown() {
        shutdown = true;
    }

    /**
     * 处理事件处理过程中出现的异常
     * @param event 事件
     * @param e 异常
     */
    private void handleException(Object event, Exception e) {
        e.printStackTrace();
    }
}