import BaseController from './base-controller.js';
import ChatPanel from '../components/chat-panel.js';
import BuddyChatHistoryManager from '../managers/buddy-chat-history-manager.js';
import TicTacToeGameManager from '../managers/tic-tac-toe-game-manager.js';
import { SFSEvent, SFSBuddyEvent, EventParam, SFSBuddyVariable, ReservedBuddyVariables, VariableType } from 'sfs3-api';
import { LeaveRoomRequest, PublicMessageRequest, SetBuddyVariablesRequest, AddBuddyRequest } from 'sfs3-api';
import * as bootstrap from 'bootstrap';

export default class GameController extends BaseController
{
    constructor(app, view, name)
    {
        super(app, view, name);

        //------------------------------------------------------

        this.leaveGameButton = view.querySelector('#leaveGameButton');
        this.leaveGameModalButton = view.querySelector('#leaveGameModalButton');
        this.chatTab = view.querySelector('#chatTab');
        this.chatPanel = view.querySelector('#chatPanel');
        this.gameContainer = view.querySelector('#gameContainer');
    }

    //----------------------------------------------------------
    // Overridden methods
    //----------------------------------------------------------

    initialize()
    {
        super.initialize();

        // Add leave game buttons click listener
        this.boundOnLeaveGameButtonClick = this.onLeaveGameButtonClick.bind(this);
        this.leaveGameButton.addEventListener('click', this.boundOnLeaveGameButtonClick);
        this.leaveGameModalButton.addEventListener('click', this.boundOnLeaveGameButtonClick);

        // Add tabs show listener
        this.boundOnChatTabShow = this.onChatTabShow.bind(this);
        this.chatTab.addEventListener('shown.bs.tab', this.boundOnChatTabShow);
                
        // Create timeout modal
        this.timeoutModal = new bootstrap.Modal(this.view.querySelector('#timeoutModal'), {
            backdrop: true,
            focus: true,
            keyboard: true
        });

        // Add custom event "message submit" listener to chat panel
        this.boundOnChatPanelMsgSubmit = this.onChatPanelMsgSubmit.bind(this);
        this.chatPanel.addEventListener(ChatPanel.MESSAGE_SUBMIT_EVT, this.boundOnChatPanelMsgSubmit);

        // Print system message
        this.chatPanel.printSystemMessage('Game joined as ' + (this.sfs.mySelf.isPlayer ? 'player' : 'spectator'));

        // Add SFS event listeners
        this.sfs.addEventListener(SFSEvent.PUBLIC_MESSAGE, this.onPublicMessage, this);
        this.sfs.addEventListener(SFSEvent.USER_ENTER_ROOM, this.onUserEnterRoom, this);
        this.sfs.addEventListener(SFSEvent.USER_EXIT_ROOM, this.onUserExitRoom, this);
		this.sfs.addEventListener(SFSEvent.SPECTATOR_TO_PLAYER, this.onSpectatorToPlayer, this);
        this.sfs.addEventListener(SFSBuddyEvent.BUDDY_MESSAGE, this.onBuddyMessage, this);
		this.sfs.addEventListener(SFSBuddyEvent.BUDDY_ADD, this.onBuddyAdd, this);

        // Initialize game manager
        // The SmartFox class instance is passed, so that the game manager can add its own listeners
        // and interact with the server-side Room Extension containing the game logic
        this.gameManager = new TicTacToeGameManager(this.sfs, this.gameContainer);
        this.gameManager.init();

        // If user is the first player in the Room, set a timeout
        // Having a timeout is usefult to suggest the user to leave the game if not yet started within some time
        // For example this could mean that the invited buddy refused the invitation, or the server couldn't locate other players to invite
        if (this.sfs.mySelf.isPlayer && this.sfs.lastJoinedRoom.playerList.length == 1)
            this.waitTimeout = setTimeout(this.onPlayerWaitTimeout.bind(this), 20000);

        // Show view
        this.showView();
    }

    destroy()
    {
        super.destroy(); // This also calls removeSmartFoxListeners method!

        this.gameManager.destroy()
        this.gameManager = null;

        // Dispose timeout modal
        clearTimeout(this.waitTimeout);
        this.timeoutModal.dispose();

        // Remove leave game buttons click listener
        this.leaveGameButton.removeEventListener('click', this.boundOnLeaveGameButtonClick);
        this.leaveGameModalButton.removeEventListener('click', this.boundOnLeaveGameButtonClick);

        // Show initial tab
        const triggerFirstTabEl = this.view.querySelector('#gameViewTabs li:first-child button');
        bootstrap.Tab.getInstance(triggerFirstTabEl).show();

        // Reset unread message counter
        this.updateUnreadMsgCounter(true);

        // Remove custom event "message submit" listener from chat panel
        this.chatPanel.removeEventListener(ChatPanel.MESSAGE_SUBMIT_EVT, this.boundOnChatPanelMsgSubmit);

        // Reset chat panel
        this.chatPanel.reset();
    }

    removeSmartFoxListeners()
    {
        if (this.sfs != null)
        {
            this.sfs.removeEventListener(SFSEvent.PUBLIC_MESSAGE, this.onPublicMessage);
            this.sfs.removeEventListener(SFSEvent.USER_ENTER_ROOM, this.onUserEnterRoom);
            this.sfs.removeEventListener(SFSEvent.USER_EXIT_ROOM, this.onUserExitRoom);
		    this.sfs.removeEventListener(SFSEvent.SPECTATOR_TO_PLAYER, this.onSpectatorToPlayer);
            this.sfs.removeEventListener(SFSBuddyEvent.BUDDY_MESSAGE, this.onBuddyMessage);
		    this.sfs.removeEventListener(SFSBuddyEvent.BUDDY_ADD, this.onBuddyAdd);
        }
    }

    //----------------------------------------------------------
    // Other methods
    //----------------------------------------------------------

    updateUnreadMsgCounter(forceReset = false)
    {
        const counterNode = this.view.querySelector('#unreadPublicMsgCnt');

        let value = 0;

        // If public chat is visible, always set number of unread messages to zero, otherwise increase current value
        const isChatVisible = this.view.querySelector('#chatPane').checkVisibility();
        if (!isChatVisible && !forceReset)
        {
            // Get current value and increase it by one
            value = Number(counterNode.innerText) + 1;
        }

        // Set value
        counterNode.innerText = value;

        // If value is 0, hide badge
        this.view.querySelector('#unreadPublicMsgContainer').hidden = (value == 0);
    }

    //----------------------------------------------------------
    // UI event listeners
    //----------------------------------------------------------

    onLeaveGameButtonClick(e)
	{
		// Leave current game room
        this.sfs.send(new LeaveRoomRequest());
        
        // Set state to "Available" in Buddy List system
        if (this.sfs.buddyManager.myOnlineState)
        {
            const buddyVars = new Array();
            buddyVars.push(new SFSBuddyVariable(ReservedBuddyVariables.BV_STATE, 'Available', VariableType.STRING));
            this.sfs.send(new SetBuddyVariablesRequest(buddyVars));
        }

        // Return to lobby view
        this.app.goToView('lobby');
	}

    onChatTabShow(e)
    {
        this.updateUnreadMsgCounter();
    }

    onPlayerWaitTimeout(e)
    {
        this.timeoutModal.show();
    }

    onChatPanelMsgSubmit(e)
    {
        const msg = e.detail.message;

        // Send public message to Room
        this.sfs.send(new PublicMessageRequest(msg));
    }

    //----------------------------------------------------------
    // SmartFoxServer event listeners
    //----------------------------------------------------------
    
    onPublicMessage(evtParams)
    {
        const sender = evtParams[EventParam.Sender];
        const message = evtParams[EventParam.Message];

        // Display chat message
        this.chatPanel.printChatMessage(message, sender.name, sender == this.sfs.mySelf);

        // Update unread messages counter
        if (sender != this.sfs.mySelf)
            this.updateUnreadMsgCounter();
    }

    onUserEnterRoom(evtParams)
    {
        const user = evtParams[EventParam.User];
        const room = evtParams[EventParam.Room];

        // Display system message
        this.chatPanel.printSystemMessage(`User <strong>${user.name}</strong> joined this game as ${user.isPlayerInRoom(room) ? 'player' : 'spectator'}`);

        if (user.isPlayerInRoom(room))
        {
            // Stop timeout
            clearTimeout(this.waitTimeout);

            // Hide leave modal
            this.timeoutModal.hide();
        }
    }

    onUserExitRoom(evtParams)
    {
        const user = evtParams[EventParam.User];

        if (user != this.sfs.mySelf)
        {
            // Display system message
            this.chatPanel.printSystemMessage(`User <strong>${user.name}</strong> left the game`);
        }
    }
    
    onSpectatorToPlayer(evtParams)
    {
        const user = evtParams[EventParam.User];

        // Display system message
        if (user == this.sfs.mySelf)
            this.chatPanel.printSystemMessage('Game joined as player');
        else
        {
            this.chatPanel.printSystemMessage(`User <strong>${user.name}</strong> is now a player`);

            if (user.isPlayer)
            {
                // Stop timeout
                clearTimeout(this.waitTimeout);

                // Hide leave modal
                this.timeoutModal.hide();
            }
        }
    }

    onBuddyMessage(evtParams)
    {
        const sender = evtParams[EventParam.Buddy];
        const isItMe = evtParams[EventParam.IsItMe];
        const message = evtParams[EventParam.Message];
        const recipient = evtParams[EventParam.Data].getString('recipient');

        const targetBuddyName = isItMe ? recipient : sender.name;
        let senderDisplayName;
        
        if (isItMe)
            senderDisplayName = (this.sfs.buddyManager.myNickname ? this.sfs.buddyManager.myNickname : this.sfs.mySelf.name);
        else
            senderDisplayName = sender.nickname ? sender.nickname : sender.name;

        // Add message to queue
        const chatMsg = BuddyChatHistoryManager.addMessage(targetBuddyName, senderDisplayName, isItMe, message);

        // Increase unread messages count
        // NOTE: there's no need to make sure the sender is not the current user,
        // as in this example the current user can't send buddy messages in other ways than
        // the chat panel (which would make the above if statement true)
        BuddyChatHistoryManager.increaseUnreadMessagesCount(chatMsg.target);
    }

    onBuddyAdd(evtParams)
    {
        const buddy = evtParams[EventParam.Buddy];
        const user = this.sfs.lastJoinedRoom.getUserByName(buddy.name);
        
        // When a buddy is added, hide Add Buddy button in game interface
		this.gameManager.updatePlayerTags();
    }
}