// Path: src/main/java/pl/joegreen/sergeants/api/listener/ChatMessageListener.java
// @FunctionalInterface
// public interface ChatMessageListener {
//     void onEvent(String chatRoom, ChatMessageApiResponse message);
// }
// 
// Path: src/main/java/pl/joegreen/sergeants/api/listener/NoArgsListener.java
// @FunctionalInterface
// public interface NoArgsListener {
//     void onEvent();
// }
// 
// Path: src/main/java/pl/joegreen/sergeants/api/listener/OneArgListener.java
// @FunctionalInterface
// public interface OneArgListener<T> {
//     void onEvent(T event);
// }


import pl.joegreen.sergeants.api.listener.ChatMessageListener;
import pl.joegreen.sergeants.api.listener.NoArgsListener;
import pl.joegreen.sergeants.api.listener.OneArgListener;
import com.fasterxml.jackson.databind.DeserializationFeature;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.datatype.jsonorg.JsonOrgModule;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import pl.joegreen.sergeants.api.response.*;
import java.util.Arrays;
import java.util.function.Consumer;
package pl.joegreen.sergeants.api;



public class GeneralsApi {
    private static final String ONE_VS_ONE_QUEUE = "1v1";
    private static final String TWO_VS_TWO_QUEUE = "2v2";
    private static final String FFA_QUEUE = "";
    private static final Logger LOGGER = LoggerFactory.getLogger(GeneralsApi.class);
    private Socket socket;

    private static ObjectMapper objectMapper = new ObjectMapper();

    static {
        objectMapper.registerModule(new JsonOrgModule());
        objectMapper.disable(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES);
    }

    private GeneralsApi(Socket socket) {
        this.socket = socket;

    }

    public static GeneralsApi create() {
        return new GeneralsApi(new RealSocket());
    }

    /**
     * For testing purposes.
     */
    public static GeneralsApi createWithCustomSocket(Socket socket) {
        return new GeneralsApi(socket);
    }

    public boolean isConnected() {
        return socket.connected();
    }

    /* LISTENERS */

    public GeneralsApi onEvent(String event, Consumer<Object[]> listener, boolean onlyOnce) {
        Consumer<Object[]> socketListener = args -> {
            LOGGER.trace("Received {}: {}", event, Arrays.toString(args));
            listener.accept(args);
        };
        if (onlyOnce) {
            socket.once(event, socketListener);
        } else {
            socket.on(event, socketListener);
        }
        return this;
    }

    public GeneralsApi onEvent(String event, Consumer<Object[]> listener) {
        return onEvent(event, listener, false);
    }


    public GeneralsApi onConnected(NoArgsListener listener) {
        return onEvent("connect", (args) -> listener.onEvent());
    }

    public GeneralsApi onceConnected(NoArgsListener listener) {
        return onEvent("connect", (args) -> listener.onEvent(), true);
    }

    public GeneralsApi onDisconnected(NoArgsListener listener) {
        return onEvent("disconnect", (args) -> listener.onEvent());
    }

    public GeneralsApi onGameStarted(OneArgListener<GameStartApiResponse> listener) {
        return onEvent("game_start", (args) -> listener.onEvent(objectMapper.convertValue(args[0], GameStartApiResponse.class)));
    }

    public GeneralsApi onGameUpdated(OneArgListener<GameUpdateApiResponse> listener) {
        return onEvent("game_update", (args) -> listener.onEvent(objectMapper.convertValue(args[0], GameUpdateApiResponse.class)));
    }

// Path: src/main/java/pl/joegreen/sergeants/api/listener/ChatMessageListener.java
// @FunctionalInterface
// public interface ChatMessageListener {
//     void onEvent(String chatRoom, ChatMessageApiResponse message);
// }
// 
// Path: src/main/java/pl/joegreen/sergeants/api/listener/NoArgsListener.java
// @FunctionalInterface
// public interface NoArgsListener {
//     void onEvent();
// }
// 
// Path: src/main/java/pl/joegreen/sergeants/api/listener/OneArgListener.java
// @FunctionalInterface
// public interface OneArgListener<T> {
//     void onEvent(T event);
// }


// Path: src/main/java/pl/joegreen/sergeants/api/GeneralsApi.java
import pl.joegreen.sergeants.api.listener.ChatMessageListener;
import pl.joegreen.sergeants.api.listener.NoArgsListener;
import pl.joegreen.sergeants.api.listener.OneArgListener;
import com.fasterxml.jackson.databind.DeserializationFeature;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.datatype.jsonorg.JsonOrgModule;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import pl.joegreen.sergeants.api.response.*;
import java.util.Arrays;
import java.util.function.Consumer;

package pl.joegreen.sergeants.api;



public class GeneralsApi {
    private static final String ONE_VS_ONE_QUEUE = "1v1";
    private static final String TWO_VS_TWO_QUEUE = "2v2";
    private static final String FFA_QUEUE = "";
    private static final Logger LOGGER = LoggerFactory.getLogger(GeneralsApi.class);
    private Socket socket;

    private static ObjectMapper objectMapper = new ObjectMapper();

    static {
        objectMapper.registerModule(new JsonOrgModule());
        objectMapper.disable(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES);
    }

    private GeneralsApi(Socket socket) {
        this.socket = socket;

    }

    public static GeneralsApi create() {
        return new GeneralsApi(new RealSocket());
    }

    /**
     * For testing purposes.
     */
    public static GeneralsApi createWithCustomSocket(Socket socket) {
        return new GeneralsApi(socket);
    }

    public boolean isConnected() {
        return socket.connected();
    }

    /* LISTENERS */

    public GeneralsApi onEvent(String event, Consumer<Object[]> listener, boolean onlyOnce) {
        Consumer<Object[]> socketListener = args -> {
            LOGGER.trace("Received {}: {}", event, Arrays.toString(args));
            listener.accept(args);
        };
        if (onlyOnce) {
            socket.once(event, socketListener);
        } else {
            socket.on(event, socketListener);
        }
        return this;
    }

    public GeneralsApi onEvent(String event, Consumer<Object[]> listener) {
        return onEvent(event, listener, false);
    }


    public GeneralsApi onConnected(NoArgsListener listener) {
        return onEvent("connect", (args) -> listener.onEvent());
    }

    public GeneralsApi onceConnected(NoArgsListener listener) {
        return onEvent("connect", (args) -> listener.onEvent(), true);
    }

    public GeneralsApi onDisconnected(NoArgsListener listener) {
        return onEvent("disconnect", (args) -> listener.onEvent());
    }

    public GeneralsApi onGameStarted(OneArgListener<GameStartApiResponse> listener) {
        return onEvent("game_start", (args) -> listener.onEvent(objectMapper.convertValue(args[0], GameStartApiResponse.class)));
    }

    public GeneralsApi onGameUpdated(OneArgListener<GameUpdateApiResponse> listener) {
        return onEvent("game_update", (args) -> listener.onEvent(objectMapper.convertValue(args[0], GameUpdateApiResponse.class)));
    }

    public GeneralsApi onGameLost(OneArgListener<GameLostApiResponse> listener) {