package tv.twitch.twiglib;

import com.unity3d.player.UnityPlayer;

import java.util.HashMap;
import java.util.List;

import tv.twitch.CoreAPI;
import tv.twitch.ErrorCode;
import tv.twitch.UserInfo;
import tv.twitch.chat.ChatChannelInfo;
import tv.twitch.chat.ChatFirstTimeChatterNotice;
import tv.twitch.chat.ChatLiveMessage;
import tv.twitch.chat.ChatMessageToken;
import tv.twitch.chat.ChatMessageTokenType;
import tv.twitch.chat.ChatRaidNotice;
import tv.twitch.chat.ChatSubscriptionNotice;
import tv.twitch.chat.ChatTextToken;
import tv.twitch.chat.ChatUnraidNotice;
import tv.twitch.chat.ChatUserInfo;
import tv.twitch.sdk.ChatController;
import tv.twitch.sdk.SDKServicesController;

public class Chat {
    private final static String TAG = "Twitch_Chat";

    ChatController mChatController;
    HashMap<String, ReceiveCallBack> mReceiveCallBackMap = new HashMap<>();
    HashMap<String, Channel> mChannels = new HashMap<>();
    ChannelListener mChannelListener = new ChannelListener();

    public void SignIn(String clientId, String token){
        SDKServicesController.setClientId(clientId);
        SDKServicesController.getInstance().logIn(token, mApiLoginCallback);

        mChatController = SDKServicesController.getInstance().getChat();
        mChatController.addChannelListener(mChannelListener);
    }

    public boolean hasSignIn(){
        return SDKServicesController.getInstance().isSdkLoggedIn();
    }

    public void Join(String channelName, ReceiveCallBack receiveCallBack){
        mReceiveCallBackMap.put(channelName, receiveCallBack);
        SDKServicesController.getInstance().fetchUserInfo(channelName, mJoinCallback);
    }

    public boolean hasJoin(String channelName){
        if(!mChannels.containsKey(channelName))
            return false;
        if(mChatController.getChannelState(mChannels.get(channelName).getChannelId()) == ChatController.ChannelState.Connected)
            return true;
        else
            return false;
    }

    public void Depart(String channelName){
        if(!mChannels.containsKey(channelName)){
            Logger.e(TAG, "Hasn't joined channel " + channelName);
        }
        Channel channel = mChannels.get(channelName);
        mChatController.disconnect(mChatController.getUserId(), channel.getChannelId(), UnityPlayer.currentActivity.getApplication().toString());
        mChannels.remove(channelName);
    }

    public void SignOut(){
        SDKServicesController.getInstance().logOut();
        // TODO: Are they doing their job?
        mReceiveCallBackMap.clear();
        mChannels.clear();
    }

    public void SendLine(String channelName, String message){
        if(!mChannels.containsKey(channelName)){
            Logger.e(TAG, "Hasn't joined channel " + channelName);
        }
        Channel channel = mChannels.get(channelName);
        mChatController.sendMessage(channel.getChannelId(), message);
    }

    private CoreAPI.LogInCallback mApiLoginCallback = new CoreAPI.LogInCallback() {
        @Override
        public void invoke(ErrorCode ec, UserInfo userInfo) {
            if(ec.failed())
                Logger.e(TAG, "SignIn failed.");
        }
    };

    private CoreAPI.FetchUserInfoCallback mJoinCallback = new CoreAPI.FetchUserInfoCallback() {
        @Override
        public void invoke(ErrorCode ec, UserInfo userInfo) {
            if(ec.failed())
                Logger.e(TAG, "Join failed - no user");
            else{
                if(!mReceiveCallBackMap.containsKey(userInfo.userName)){
                    Logger.e(TAG, "No receiveCallBack for channel " + userInfo.userName);
                }
                mChatController.connect(mChatController.getUserId(), userInfo.userId, UnityPlayer.currentActivity.getApplication().toString());
                ReceiveCallBack callBack = mReceiveCallBackMap.get(userInfo.userName);
                Channel channel = new Channel(userInfo.userId, callBack);
                mChannels.put(userInfo.userName, channel);
                mReceiveCallBackMap.remove(userInfo.userName);
            }
        }
    };

    private class ChannelListener implements ChatController.IChannelListener{
        public void onChannelStateChanged(int channelId, ChatController.ChannelState state, ErrorCode ec) {};
        public void onChannelInfoChanged(int channelId, ChatChannelInfo channelInfo){};
        public void onChannelLocalUserChanged(int channelId, ChatUserInfo userInfo){};
        public void onChannelUserChange(int channelId, List<ChatUserInfo> users){};
        public void onChannelMessageReceived(int channelId, ChatLiveMessage[] messageList){
            for(ChatLiveMessage msg : messageList){
                if(msg.messageInfo.userName.isEmpty())
                    continue;

                String displayName = msg.messageInfo.displayName;
                String userName = msg.messageInfo.userName;
                String message = new String();
                Logger.d(TAG, "Received message from " + userName);
                for(ChatMessageToken token : msg.messageInfo.tokens){
                    if(token.type == ChatMessageTokenType.Text){
                        ChatTextToken textToken = (ChatTextToken)token;
                        Logger.d(TAG, displayName + ": " + textToken.text);
                        message = message + " " + textToken.text;
                    }
                }
                for(Channel channel : mChannels.values()){
                    if(channel.getChannelId() == channelId){
                        channel.getReceiveCallBack().invoke(userName, message);
                        break;
                    }
                }
            }
        };
        public void onChannelUserMessagesCleared(int channelId, int userId){};
        public void onChannelMessagesCleared(int channelId){};
        public void onChannelHostTargetChanged(int channelId, String targetChannelName, int numViewers){};
        public void onChannelNoticeReceived(int channelId, String noticeId, HashMap<String, String> params){};
        public void onChannelChatMessageSendError(int channelId, ErrorCode ec){};
        public void onChannelRaidNoticeReceived(int channelId, ChatRaidNotice chatRaidNotice){};
        public void onChannelUnraidNoticeReceived(int channelId, ChatUnraidNotice chatUnraidNotice){};
        public void onChannelFirstTimeChatterNoticeReceived(int channelId, int userId, ChatFirstTimeChatterNotice chatFirstTimeChatterNotice){};
        public void onChannelSubscriptionNoticeReceived(int channelId, int userId, ChatSubscriptionNotice chatSubscriptionNotice){};
    }
}
