// Path: BiliLiveLib/src/main/java/charlie/bililivelib/Globals.java
// public class Globals {
//     private static final String BILI_PASSPORT_HOST_ROOT = "passport.bilibili.com";
//     private static final String BILI_LIVE_HOST_ROOT = "live.bilibili.com";
//     @Getter
//     private static int CONNECTION_ALIVE_TIME_SECOND = 10;
//     private static Globals instance;
//     @Getter
//     private HttpHost biliLiveRoot;
//     @Getter
//     private HttpHost biliPassportHttpsRoot;
//     private ThreadLocal<Gson> gson;
// 
//     private HttpClientConnectionManager connectionPool;
//     @Getter
//     private SSLContext bilibiliSSLContext;
// 
//     public static Globals get() {
//         if (instance == null) {
//             instance = new Globals();
//             instance.init();
//         }
//         return instance;
//     }
// 
//     private void init() {
//         biliLiveRoot = new HttpHost(BILI_LIVE_HOST_ROOT);
//         //Visit bilibili passport via https. 443 is the https port.
//         biliPassportHttpsRoot = new HttpHost(BILI_PASSPORT_HOST_ROOT, 443, "https");
//         gson = ThreadLocal.withInitial(() -> new GsonBuilder()
//                 .setLenient()
//                 .create());
// 
//         try {
//             bilibiliSSLContext = SSLContextBuilder.create()
//                     .loadTrustMaterial(new BilibiliTrustStrategy())
//                     .build();
//         } catch (NoSuchAlgorithmException | KeyManagementException | KeyStoreException e) {
//             throw new AssertionError("failed initializing bilibili ssl context!");
//         }
//     }
// 
//     public Gson gson() {
//         return gson.get();
//     }
// 
//     public HttpClientConnectionManager getConnectionPool() {
//         if (connectionPool == null) {
//             reInitializeConnectionPool();
//         }
//         return connectionPool;
//     }
// 
//     public void reInitializeConnectionPool() {
//         connectionPool = new PoolingHttpClientConnectionManager(CONNECTION_ALIVE_TIME_SECOND, TimeUnit.SECONDS);
//     }
// 
//     public void setConnectionAliveTimeSecond(int connectionAliveTimeSecond) {
//         CONNECTION_ALIVE_TIME_SECOND = connectionAliveTimeSecond;
//         reInitializeConnectionPool();
//     }
// 
// 
// }
// 
// Path: BiliLiveLib/src/main/java/charlie/bililivelib/danmaku/event/DanmakuListener.java
// public interface DanmakuListener {
//     void danmakuEvent(@NotNull DanmakuEvent event);
// 
//     void watcherCountEvent(@NotNull DanmakuEvent event);
// 
//     void errorEvent(@NotNull DanmakuEvent event);
// 
//     void startStopEvent(@NotNull DanmakuEvent event);
// 
//     void statusEvent(@NotNull DanmakuEvent event);
// 
//     void welcomeVipEvent(@NotNull DanmakuEvent event);
// 
//     void giveGiftEvent(@NotNull DanmakuEvent event);
// 
//     void globalGiftEvent(@NotNull DanmakuEvent event);
// 
//     void globalAnnounceEvent(@NotNull DanmakuEvent event);
// }


import charlie.bililivelib.Globals;
import charlie.bililivelib.danmaku.event.DanmakuListener;
import com.google.gson.JsonObject;
import org.jetbrains.annotations.Contract;
import java.util.List;
package charlie.bililivelib.danmaku.dispatch;



/**
 * 对{@link Dispatcher}的抽象实现，用于事件内容是JSON文本时的处理。
 */
public abstract class AbstractJSONDispatcher implements Dispatcher {
    /**
     * 获取可以接受的事件命令。
     *
     * @return 事件命令列表
     */
    protected abstract String[] getAcceptableCommands();

    @Contract(pure = true)
    private boolean isValid(String body) {
        return body.startsWith("{");
    }

    private boolean checkCanDispatch(JsonObject rootObject) {
        String command = getCommand(rootObject);
        if (command == null) return false;
        for (String acceptable : getAcceptableCommands()) {
            if (command.equals(acceptable)) return true;
        }
        return false;
    }

    /**
     * 检查指定事件是否为JSON对象，且是否能被本监听器处理。
     * 如果可以则交由{@link AbstractJSONDispatcher#dispatch(List, JsonObject, Object)}进行分发。
     * @param listeners 监听器列表
     * @param body 事件内容
     * @param source 事件发生源
     */
    @Override
    public void tryDispatch(List<DanmakuListener> listeners, String body, Object source) {
        if (!isValid(body)) return;
// Path: BiliLiveLib/src/main/java/charlie/bililivelib/Globals.java
// public class Globals {
//     private static final String BILI_PASSPORT_HOST_ROOT = "passport.bilibili.com";
//     private static final String BILI_LIVE_HOST_ROOT = "live.bilibili.com";
//     @Getter
//     private static int CONNECTION_ALIVE_TIME_SECOND = 10;
//     private static Globals instance;
//     @Getter
//     private HttpHost biliLiveRoot;
//     @Getter
//     private HttpHost biliPassportHttpsRoot;
//     private ThreadLocal<Gson> gson;
// 
//     private HttpClientConnectionManager connectionPool;
//     @Getter
//     private SSLContext bilibiliSSLContext;
// 
//     public static Globals get() {
//         if (instance == null) {
//             instance = new Globals();
//             instance.init();
//         }
//         return instance;
//     }
// 
//     private void init() {
//         biliLiveRoot = new HttpHost(BILI_LIVE_HOST_ROOT);
//         //Visit bilibili passport via https. 443 is the https port.
//         biliPassportHttpsRoot = new HttpHost(BILI_PASSPORT_HOST_ROOT, 443, "https");
//         gson = ThreadLocal.withInitial(() -> new GsonBuilder()
//                 .setLenient()
//                 .create());
// 
//         try {
//             bilibiliSSLContext = SSLContextBuilder.create()
//                     .loadTrustMaterial(new BilibiliTrustStrategy())
//                     .build();
//         } catch (NoSuchAlgorithmException | KeyManagementException | KeyStoreException e) {
//             throw new AssertionError("failed initializing bilibili ssl context!");
//         }
//     }
// 
//     public Gson gson() {
//         return gson.get();
//     }
// 
//     public HttpClientConnectionManager getConnectionPool() {
//         if (connectionPool == null) {
//             reInitializeConnectionPool();
//         }
//         return connectionPool;
//     }
// 
//     public void reInitializeConnectionPool() {
//         connectionPool = new PoolingHttpClientConnectionManager(CONNECTION_ALIVE_TIME_SECOND, TimeUnit.SECONDS);
//     }
// 
//     public void setConnectionAliveTimeSecond(int connectionAliveTimeSecond) {
//         CONNECTION_ALIVE_TIME_SECOND = connectionAliveTimeSecond;
//         reInitializeConnectionPool();
//     }
// 
// 
// }
// 
// Path: BiliLiveLib/src/main/java/charlie/bililivelib/danmaku/event/DanmakuListener.java
// public interface DanmakuListener {
//     void danmakuEvent(@NotNull DanmakuEvent event);
// 
//     void watcherCountEvent(@NotNull DanmakuEvent event);
// 
//     void errorEvent(@NotNull DanmakuEvent event);
// 
//     void startStopEvent(@NotNull DanmakuEvent event);
// 
//     void statusEvent(@NotNull DanmakuEvent event);
// 
//     void welcomeVipEvent(@NotNull DanmakuEvent event);
// 
//     void giveGiftEvent(@NotNull DanmakuEvent event);
// 
//     void globalGiftEvent(@NotNull DanmakuEvent event);
// 
//     void globalAnnounceEvent(@NotNull DanmakuEvent event);
// }


// Path: BiliLiveLib/src/main/java/charlie/bililivelib/danmaku/dispatch/AbstractJSONDispatcher.java
import charlie.bililivelib.Globals;
import charlie.bililivelib.danmaku.event.DanmakuListener;
import com.google.gson.JsonObject;
import org.jetbrains.annotations.Contract;
import java.util.List;

package charlie.bililivelib.danmaku.dispatch;



/**
 * 对{@link Dispatcher}的抽象实现，用于事件内容是JSON文本时的处理。
 */
public abstract class AbstractJSONDispatcher implements Dispatcher {
    /**
     * 获取可以接受的事件命令。
     *
     * @return 事件命令列表
     */
    protected abstract String[] getAcceptableCommands();

    @Contract(pure = true)
    private boolean isValid(String body) {
        return body.startsWith("{");
    }

    private boolean checkCanDispatch(JsonObject rootObject) {
        String command = getCommand(rootObject);
        if (command == null) return false;
        for (String acceptable : getAcceptableCommands()) {
            if (command.equals(acceptable)) return true;
        }
        return false;
    }

    /**
     * 检查指定事件是否为JSON对象，且是否能被本监听器处理。
     * 如果可以则交由{@link AbstractJSONDispatcher#dispatch(List, JsonObject, Object)}进行分发。
     * @param listeners 监听器列表
     * @param body 事件内容
     * @param source 事件发生源
     */
    @Override
    public void tryDispatch(List<DanmakuListener> listeners, String body, Object source) {
        if (!isValid(body)) return;
        JsonObject rootObject = Globals.get().gson().fromJson(body, JsonObject.class);