package co.phoenixlab.discord.api;

import org.java_websocket.client.WebSocketClient;
import org.java_websocket.handshake.ServerHandshake;
import org.json.simple.JSONObject;
import org.json.simple.parser.JSONParser;
import org.json.simple.parser.ParseException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.net.URI;

public class DiscordWebSocketClient extends WebSocketClient {

    private static final Logger LOGGER = LoggerFactory.getLogger("DiscordApiClient");

    private final DiscordApiClient apiClient;
    private final JSONParser parser;

    public DiscordWebSocketClient(DiscordApiClient apiClient, URI serverUri) {
        super(serverUri);
        this.apiClient = apiClient;
        this.parser = new JSONParser();
    }

    @Override
    public void onOpen(ServerHandshake handshakedata) {
        Thread.currentThread().setName("WebSocketClient");
        LOGGER.info("WebSocket connection opened");
        send("{\"op\":2,\"d\":{\"token\":\"" + apiClient.getToken() + "\",\"properties\":{\"$os\":\"Linux\",\"" +
                "$browser\":\"DesuBot\",\"$device\":\"DesuBot\",\"$referrer\":\"\",\"$referring_domain\"" +
                ":\"\"},\"v\":2}}");
    }

    @Override
    public void onMessage(String message) {
        LOGGER.debug("Recieved message: {}", message);
        try {
            JSONObject msg = (JSONObject) parser.parse(message);
            String errorMessage = (String) msg.get("message");
            if (errorMessage != null) {
                if (errorMessage.isEmpty()) {
                    LOGGER.warn("Discord returned an unknown error");
                } else {
                    LOGGER.warn("Discord returned error: {}", errorMessage);
                }
                return;
            }
            String type = (String) msg.get("t");
            JSONObject data = (JSONObject) msg.get("d");
            switch (type) {
                //  TODO
            }
        } catch (ParseException e) {
            LOGGER.warn("Unable to parse message", e);
        }
    }

    @Override
    public void onClose(int code, String reason, boolean remote) {
        LOGGER.info("Closing WebSocket {}: {} {}", code, reason, remote ? "remote" : "local");
    }

    @Override
    public void onError(Exception ex) {
        LOGGER.warn("WebSocket error", ex);
    }
}

@@ -29,16 +29,101 @@
 d.api;%0A%0A
+import co.phoenixlab.discord.api.entities.ReadyMessage;%0Aimport com.google.gson.Gson;%0A
 import o
@@ -418,16 +418,99 @@
 net.URI;
+%0Aimport java.util.concurrent.ScheduledFuture;%0Aimport java.util.concurrent.TimeUnit;
 %0A%0Apublic
@@ -735,16 +735,90 @@
  parser;
+%0A    private final Gson gson;%0A    private ScheduledFuture keepAliveFuture;
 %0A%0A    pu
@@ -992,16 +992,48 @@
 rser();%0A
+        this.gson = new Gson();%0A
     %7D%0A%0A 
@@ -2201,132 +2201,1771 @@
     
-//  TODO%0A            %7D%0A        %7D catch (ParseException e) %7B%0A            LOGGER.warn(%22Unable to parse message%22, e);%0A        %7D
+case %22READY%22:%0A                    handleReadyMessage(data);%0A                    break;%0A%0A                //  TODO%0A%0A                default:%0A                    LOGGER.warn(%22Unknown message type %7B%7D:%5Cn%7B%7D%22, type, data.toJSONString());%0A            %7D%0A        %7D catch (ParseException e) %7B%0A            LOGGER.warn(%22Unable to parse message%22, e);%0A        %7D%0A    %7D%0A%0A    private void handleReadyMessage(JSONObject data) %7B%0A        //  Because this doesnt come often and to simplify matters%0A        //  we'll serialize the subobject to string and have Gson parse out the object%0A        String json = data.toJSONString();%0A        LOGGER.debug(json);%0A        ReadyMessage readyMessage = gson.fromJson(json, ReadyMessage.class);%0A        apiClient.setSessionId(readyMessage.getSessionId());%0A        LOGGER.info(%22Using sessionId %7B%7D%22, apiClient.getSessionId());%0A        startKeepAlive(readyMessage.getHeartbeatInterval());%0A        LOGGER.info(%22Sending keepAlive every %7B%7D ms%22, readyMessage.getHeartbeatInterval());%0A        LOGGER.info(%22Connected to %7B%7D servers%22, readyMessage.getServers().length);%0A        LOGGER.info(%22Holding %7B%7D private conversations%22, readyMessage.getPrivateChannels().length);%0A        //  TODO%0A    %7D%0A%0A    @SuppressWarnings(%22unchecked%22)%0A    private void startKeepAlive(long keepAliveInterval) %7B%0A        if (keepAliveFuture != null) %7B%0A            keepAliveFuture.cancel(true);%0A        %7D%0A        keepAliveFuture = apiClient.getExecutorService().scheduleAtFixedRate(() -%3E %7B%0A            JSONObject keepAlive = new JSONObject();%0A            keepAlive.put(%22op%22, 1);%0A            keepAlive.put(%22d%22, System.currentTimeMillis());%0A            LOGGER.debug(%22Sending keepAlive%22);%0A            send(keepAlive.toJSONString());%0A        %7D, 0, keepAliveInterval, TimeUnit.MILLISECONDS);
 %0A   
@@ -4140,24 +4140,115 @@
 : %22local%22);%0A
+        if (keepAliveFuture != null) %7B%0A            keepAliveFuture.cancel(true);%0A        %7D%0A
     %7D%0A%0A    @

Implement basic ready msg handling