package com.khmer9.yuki.remote.service;

import static java.util.Collections.singletonList;
import static java.util.Collections.singletonMap;

import android.util.Log;

import com.khmer9.yuki.config.Config;
import com.khmer9.yuki.util.BaseObservable;

import org.json.JSONObject;

import java.net.URI;
import java.net.URISyntaxException;

import io.socket.client.IO;
import io.socket.client.Socket;
import io.socket.emitter.Emitter;

public class SocketService extends BaseObservable<EventListener> {

    private static final String TAG = "Socket";

    private static final String SOCKET_URL = Config.SOCKET_URL;

    private static final String EVENT_CONNECT = Socket.EVENT_CONNECT;
    private static final String EVENT_CONNECT_ERROR = Socket.EVENT_CONNECT_ERROR;
    private static final String EVENT_DISCONNECT = Socket.EVENT_DISCONNECT;

    private static final String EVENT_CLIENT_NEW_GAME = "client-new-game";
    private static final String EVENT_CLIENT_RANDOM = "client-random";
    private static final String EVENT_CLIENT_RESET = "client-reset";
    private static final String EVENT_CLIENT_RESULT = "client-result";
    private static final String EVENT_CLIENT_UPDATE_INFO = "client-update-info";
    private static final String EVENT_CLIENT_TIME = "client-time";

    private Socket socket;

    public void startListening(String token) throws URISyntaxException {
        IO.Options options = IO.Options.builder()
                .setExtraHeaders(singletonMap("Authorization", singletonList("Bearer " + token)))
                .build();

        if (socket == null) {
            socket = IO.socket(URI.create(SOCKET_URL), options);
        }

        socket.on(EVENT_CONNECT, connectListener);
        socket.on(EVENT_CLIENT_NEW_GAME, clientNewGameListener);
        socket.on(EVENT_CLIENT_RANDOM, clientRandomListener);
        socket.on(EVENT_CLIENT_RESET, clientResetListener);
        socket.on(EVENT_CLIENT_RESULT, clientResultListener);
        socket.on(EVENT_CLIENT_UPDATE_INFO, clientUpdateInfoListener);
        socket.on(EVENT_CLIENT_TIME, clientTimeListener);
        socket.on(EVENT_DISCONNECT, disconnectListener);
        socket.on(EVENT_CONNECT_ERROR, connectionErrorListener);
        socket.connect();
    }

    private Emitter.Listener clientNewGameListener = args -> {
        Log.d("Socket", "clientNewGameListener s...");
        final String rawMessage = args[0].toString();
        for (EventListener listener : getListeners())
            listener.onClientNewGameListener(rawMessage);
    };

    private Emitter.Listener clientRandomListener = args -> {
        Log.d("Socket", "clientRandomListener s...");
        final String rawMessage = args[0].toString();
        for (EventListener listener : getListeners())
            listener.onClientRandomListener(rawMessage);
    };

    private Emitter.Listener clientResetListener = args -> {
        Log.d("Socket", "clientResetListener s...");
        final String rawMessage = args[0].toString();
        for (EventListener listener : getListeners())
            listener.onClientResetListener(rawMessage);
    };

    private Emitter.Listener clientResultListener = args -> {
        Log.d("Socket", "clientResultListener s...");
        final String rawMessage = args[0].toString();
        for (EventListener listener : getListeners())
            listener.onClientResultListener(rawMessage);
    };

    private Emitter.Listener clientUpdateInfoListener = args -> {
        Log.d("Socket", "clientUpdateInfoListener s...");
        final String rawMessage = args[0].toString();
        for (EventListener listener : getListeners())
            listener.onClientUpdateInfoListener(rawMessage);
    };

    private Emitter.Listener clientTimeListener = args -> {
        Log.d("Socket", "clientTimeListener s...");
        final String rawMessage = args[0].toString();
        for (EventListener listener : getListeners())
            listener.onClientTimeListener(rawMessage);
    };

    private Emitter.Listener connectListener = args -> {
        Log.d("Socket", "onConnect s...");
        for (EventListener listener : getListeners())
            listener.onConnect();
    };

    private Emitter.Listener connectionErrorListener = args -> {
        Log.d(TAG, "onConnectionError s..." + args[0].toString());
        for (EventListener listener : getListeners())
            listener.onConnectionError();
    };

    private Emitter.Listener disconnectListener = args -> {
        Log.d(TAG, "onDisconnect s..." + args[0].toString());
        socket.off();
        for (EventListener listener : getListeners()) {
            listener.onDisconnect();
        }
    };

    public void startEmitCfCreateBetting(JSONObject joinObject) {
        socket.emit("player-betting", joinObject);
    }

    public void stopListening() {
        if (socket != null)
            socket.disconnect();
    }
}
