package failchat.funstream;

import com.fasterxml.jackson.databind.ObjectMapper;
import com.github.nkzawa.socketio.client.IO;
import com.github.nkzawa.socketio.client.Socket;
import failchat.core.*;
import failchat.handlers.CapsHandler;
import failchat.handlers.HtmlHandler;
import failchat.handlers.MessageObjectCleaner;
import failchat.handlers.SupportSmileHandler;
import org.json.JSONException;
import org.json.JSONObject;

import java.io.IOException;
import java.net.URI;
import java.util.ArrayList;
import java.util.List;
import java.util.Queue;
import java.util.logging.Level;
import java.util.logging.Logger;

public class FsChatClient implements ChatClient, ViewersCounter {
    private static final Logger logger = Logger.getLogger(FsChatClient.class.getName());
    private static final String FS_SOCKET_URL = "http://funstream.tv:3811";

    private ChatClientStatus status;
    private MessageManager messageManager = MessageManager.getInstance();
    private Moderation moderation = Moderation.getInstance();
    private Queue<failchat.core.Message> messageQueue = messageManager.getMessagesQueue();
    private List<MessageHandler<FsMessage>> messageHandlers;
    private List<MessageFilter<FsMessage>> messageFilters;
    private ObjectMapper objectMapper;
    private String channelName;
    private int channelId;
    private Socket socket;
    volatile private int viewersCount = -1;

    public FsChatClient(String channelName) {
        this.channelName = channelName;
        messageHandlers = new ArrayList<>();
        messageFilters = new ArrayList<>();
        objectMapper = new ObjectMapper();
        messageFilters.add(new SourceFilter());
        messageFilters.add(new DoubleMessageFilter());
        messageFilters.add(new AnnounceMessageFilter());
        //noinspection unchecked
        messageHandlers.add(MessageObjectCleaner.getInstance());
        messageHandlers.add(new SupportSmileHandler());
        messageHandlers.add(new CapsHandler());
        messageHandlers.add(new HtmlHandler());
        messageHandlers.add(new FsHighlightHandler(channelName));
        messageHandlers.add(new FsSmileHandler());
        status = ChatClientStatus.READY;
    }

    @Override
    public void goOnline() {
        if (status != ChatClientStatus.READY) {
            return;
        }

        new Thread(() -> {
            channelId = FsApiWorker.getChannelIdByName(channelName);
            if (channelId == -1) {
                logger.warning("Can't load funstream channel id");
                return;
            }
            socket = buildSocket();
            socket.connect();
        }).run();
    }

    @Override
    public void goOffline() {
        status = ChatClientStatus.SHUTDOWN;
        if (socket != null) {
            socket.disconnect();
        }
    }

    @Override
    public ChatClientStatus getStatus() {
        return status;
    }

    private Socket buildSocket() {
        try {
            URI chatApiUrl = URI.create(FS_SOCKET_URL);
            IO.Options options = new IO.Options();
            options.transports = new String[1];
            options.forceNew = true;
            options.transports[0] = "websocket";

            Socket socket =  IO.socket(chatApiUrl, options);
            socket.on(Socket.EVENT_CONNECT, objects -> {
                if (status == ChatClientStatus.SHUTDOWN) { //for quick close case
                    socket.disconnect();
                    return;
                }
                try {
                    JSONObject obj = new JSONObject();
                    obj.put("channel", "stream/" + channelId);
                    Object[] args1 = new Object[1];
                    args1[0] = obj;
                    socket.emit("/chat/join", args1, rObjects -> {
                        logger.info("Connected to sc2tv");
                        messageManager.sendInfoMessage(new InfoMessage(Source.SC2TV, "connected"));
                        status = ChatClientStatus.WORKING;
                    });
                } catch (JSONException e) {
                    logger.log(Level.WARNING, "Something goes wrong...", e);
                }
            }).on(Socket.EVENT_DISCONNECT, objects -> {
                status = ChatClientStatus.CONNECTING;
                logger.info("Disconnected from sc2tv");
                messageManager.sendInfoMessage(new InfoMessage(Source.SC2TV, "disconnected"));
            }).on("/chat/message", objects -> {
                try {
                    FsMessage fsMessage = objectMapper.readValue(objects[0].toString(), FsMessage.class);

                    //filter message
                    for (MessageFilter<FsMessage> messageFiller : messageFilters) {
                        if (messageFiller.filterMessage(fsMessage)) {
                            return;
                        }
                    }
                    //handle message
                    for (MessageHandler<FsMessage> messageHandler : messageHandlers) {
                        messageHandler.handleMessage(fsMessage);
                    }
                    messageQueue.add(fsMessage);
                    synchronized (messageQueue) {
                        messageQueue.notify();
                    }

                } catch (IOException e) {
                    logger.log(Level.WARNING, "Something goes wrong...", e);
                }
            });
//                .on("/chat/message/remove", objects -> {
//                try {
//                    JSONObject removeMessage = (JSONObject) objects[0];
//                    moderation.deleteFsMessage(removeMessage.getInt("id"));
//                } catch (JSONException e) {
//                    logger.log(Level.WARNING, "Something goes wrong...", e);
//                }
//            });

            return socket;
        }
        catch (Exception e) {
            logger.log(Level.WARNING, "Something goes wrong...", e);
            return null;
        }
    }

    @Override
    public int getViewersCount() {
        return viewersCount;
    }

    @Override
    public void updateViewersCount() {
        JSONObject obj = new JSONObject();
        try {
            obj.put("channel", "stream/" + channelId);
        } catch (JSONException e) {
            logger.log(Level.WARNING, "Illegal channel name", e);
        }
        Object[] args1 = new Object[1];
        args1[0] = obj;

        socket.emit("/chat/channel/list", args1, rObjects -> {
            JSONObject response = (JSONObject) rObjects[0];
            try {
                if (response.getString("status").equals("ok")) {
                    viewersCount = response.getJSONObject("result").getInt("amount");
                } else {
                    viewersCount = -1;
                    logger.warning("Bad response status, json: " + response.toString());
            }
            } catch (JSONException e) {
                logger.warning("Unexpected response json format: " + response.toString());
                viewersCount = -1;
            }
        });
    }
}
@@ -5391,20 +5391,17 @@
       %7D)
-;%0A//
+%0A
         
@@ -5449,18 +5449,16 @@
 ts -%3E %7B%0A
-//
         
@@ -5471,18 +5471,16 @@
   try %7B%0A
-//
         
@@ -5543,18 +5543,16 @@
 cts%5B0%5D;%0A
-//
         
@@ -5619,18 +5619,16 @@
 %22id%22));%0A
-//
         
@@ -5663,18 +5663,16 @@
 on e) %7B%0A
-//
         
@@ -5740,18 +5740,16 @@
 .%22, e);%0A
-//
         
@@ -5758,18 +5758,16 @@
       %7D%0A
-//
         

Add sc2tv moderation