const emojiPatterns = [
    { pattern: ':)', replacement: 'πŸ™‚' },
    { pattern: ':-)', replacement: 'πŸ™‚' },
    { pattern: ':D', replacement: 'πŸ˜€' },
    { pattern: ':-D', replacement: 'πŸ˜€' },
    { pattern: ';)', replacement: 'πŸ˜‰' },
    { pattern: ';-)', replacement: 'πŸ˜‰' },
    { pattern: ':(', replacement: '😞' },
    { pattern: ':-(', replacement: '😞' },
    { pattern: ':\'(', replacement: '😭' },
    { pattern: ':\'-(', replacement: '😭' },
    { pattern: ':\\', replacement: 'πŸ˜•' },
    { pattern: ':-\\', replacement: 'πŸ˜•' },
    { pattern: ':/', replacement: 'πŸ˜•' },
    { pattern: ':-/', replacement: 'πŸ˜•' },
    { pattern: ':|', replacement: '😐' },
    { pattern: ':-|', replacement: '😐' },
    { pattern: ':*', replacement: '😘' },
    { pattern: ':-*', replacement: '😘' },
    { pattern: '<3', replacement: '❀️' },
    { pattern: '(y)', replacement: 'πŸ‘' },
];

const socket = io();

$(() => {
    socket.on('serverHandshake', onServerHandshake);
    socket.on('serverLogin', onServerLogin);
    socket.on('serverKick', onServerKick);
});

function onServerHandshake() {
    resetChatListeners();
    resetLoginListeners();

    var template = $('#chat-login-template').html();
    $('#chat-host').empty();
    $('#chat-host').append(template);

    $('#chat-login-form').submit((e) => {
        e.preventDefault();

        socket.emit('login', $('#chat-login-form .username').val(), $('#chat-login-form .password').val());

        return false;
    });

    socket.on('usernameInvalid', () => {
        $('#chat-login-form .username').focus();
        $('#chat-host .loginError').text('Username invalid.');
    });

    socket.on('usernameTaken', () => {
        $('#chat-login-form .username').focus();
        $('#chat-host .loginError').text('Username already in use.');
    });

    socket.on('passwordRequired', () => {
        $('#chat-login-form .passwordWrapper').css('display', '');
        $('#chat-login-form .password').focus();
        $('#chat-host .loginError').text('FΓΌr diesen reservierten Benutzer ist ein Passwort erforderlich.');
    });

    socket.on('passwordWrong', () => {
        $('#chat-login-form .passwordWrapper').css('display', '');
        $('#chat-login-form .password').focus();
        $('#chat-host .loginError').text('Das eingegebene Passwort ist ungΓΌltig.');
    });
}

function onServerLogin(user, history) {
    resetChatListeners();
    resetLoginListeners();

    var template;
    if (user.admin) {
        template = $('#chat-admin-template').html()
    } else {
        template = $('#chat-template').html()
    }

    $('#chat-host').empty();
    $('#chat-host').append(template);

    $('#chat-host').append($('#chat-message-form-template').html());

    $('#chat-host .messageField').focus();

    for (const historyMsg of history) {
        appendUserMessage(historyMsg.msg, historyMsg.user, historyMsg.timestamp, user);
    }

    socket.on('usersUpdated', (userList) => {
        $('#chat-host .main .userlist').empty();

        userList.sort((userA, userB) => userA.name.localeCompare(userB.name));
        userList.sort((userA, userB) => {
            if (userA.admin && !userB.admin) {
                return Number.MIN_SAFE_INTEGER;
            } else if (!userA.admin && userB.admin) {
                return Number.MAX_SAFE_INTEGER;
            }

            return 0;
        });

        for (let listUser of userList) {
            let userString = listUser.name;
            if (listUser.admin) {
                userString = 'πŸ‘‘ ' + userString;
            }

            const userListEl = $('<li>');

            const usernameEl = $('<span>');
            usernameEl.text(userString);
            usernameEl.css('color', listUser.color);
            usernameEl.click(() => mentionUser(listUser.name));
            userListEl.append(usernameEl);

            if (user.admin) {
                const kickBtn = $('<button class="kickBtn">πŸ₯Ύ</button>');
                kickBtn.click(() => onKickUser(listUser));
                userListEl.append(kickBtn);
            }

            $('#chat-host .main .userlist').append(userListEl);
        }
    });

    if (user.admin) {
        socket.on('bannedUpdated', (bannedList) => {
            $('#chat-host .main .bannedlist').empty();

            for (let bannedItem of bannedList) {
                const bannedListEl = $('<li>');

                bannedListEl.text(bannedItem.ip + ' (' + bannedItem.user.name + ')');

                const removeBtn = $('<button class="removeBtn">βœ–</button>');
                removeBtn.click(() => onLiftBan(bannedItem.ip));
                bannedListEl.append(removeBtn);

                $('#chat-host .main .bannedlist').append(bannedListEl);
            }
        });
    }

    socket.on('message', (msg, msgUser, timestamp) => appendUserMessage(msg, msgUser, timestamp, user));

    socket.on('userJoined', (msg) => {
        appendMessage($('<li class="systemMsg user joined">').text(msg + ' hat den Chat betreten.'));
    });

    socket.on('userLeft', (msg) => {
        appendMessage($('<li class="systemMsg user left">').text(msg + ' hat den Chat verlassen.'));
    });

    $('#chat-host .messageForm').submit((e) => {
        e.preventDefault();
        socket.emit('message', $('#chat-host .messageForm .messageField').val());
        $('#chat-host .messageForm .messageField').val('');
        $('#chat-host .messageForm .messageField').focus();
        return false;
    });

    $("#chat-host .messageField").keypress((e) => {
        const field = $('#chat-host .messageForm .messageField');
        if (field.prop('selectionStart') == field.prop('selectionEnd')) {
            const val = field.val();
            const cursorPos = field.prop('selectionStart');
            const valBefore = val.substring(0, cursorPos);
            const valAfter = val.substring(cursorPos);

            if (!valBefore.endsWith('http:/') && !valBefore.endsWith('https:/')) {
                for (const emojiPatternItem of emojiPatterns) {
                    if (valBefore.endsWith(emojiPatternItem.pattern)) {
                        field.val(
                            valBefore.substring(0, valBefore.length - emojiPatternItem.pattern.length) + emojiPatternItem.replacement + valAfter
                        );
                        field.prop('selectionStart', cursorPos);
                        field.prop('selectionEnd', cursorPos);
                    }
                }
            }
        }

        if(e.key == 'Enter' && !e.shiftKey) {
            e.preventDefault();
            $('#chat-host .messageForm').submit();
            return false;
        }
    });
}

function onServerKick() {
    resetChatListeners();
    resetLoginListeners();

    var template = $('#chat-kick-template').html();
    $('#chat-host').empty();
    $('#chat-host').append(template);
}

function resetChatListeners() {
    socket.removeAllListeners('usersUpdated');
    socket.removeAllListeners('message');
    socket.removeAllListeners('userJoined');
    socket.removeAllListeners('userLeft');
}

function resetLoginListeners() {
    socket.removeAllListeners('usernameInvalid');
    socket.removeAllListeners('usernameTaken');
    socket.removeAllListeners('passwordRequired');
    socket.removeAllListeners('passwordWrong');
}

function appendMessage(element) {
    const messagesListEl = $('#chat-host .main .messages')[0];
    const scrollMax = messagesListEl.scrollHeight - messagesListEl.clientHeight;
    const scrolled = messagesListEl.scrollTop != scrollMax;

    $('#chat-host .main .messages').append(element);

    // TODO: Scrolling
    //if (!scrolled) {
        messagesListEl.scrollTop = messagesListEl.scrollHeight - messagesListEl.clientHeight;
    //}
}

function appendUserMessage(msg, msgUser, timestamp, user) {
    const messageWrapper = $('<li class="userMsg">');
    if(user.name ==  msgUser.name) {
        messageWrapper.addClass('self');
    }
    messageWrapper.css('borderColor', msgUser.color);

    const avaEl = $('<img class="avatar" />');
    avaEl.prop('src', '/img/ava/' + msgUser.name);
    avaEl.css('backgroundColor', msgUser.color);
    avaEl.click(() => mentionUser(msgUser.name));
    messageWrapper.append(avaEl);

    const timeEl = $('<span class="time">');
    timeEl.text(new Date(timestamp).toLocaleTimeString());
    timeEl.prop('title', new Date(timestamp).toLocaleString());
    messageWrapper.append(timeEl);

    const userEl = $('<span class="user">');
    let userString = msgUser.name;
    if (msgUser.admin) {
        userString = 'πŸ‘‘ ' + userString;
    }
    userEl.text(userString);
    userEl.css('color', msgUser.color);
    userEl.click(() => mentionUser(msgUser.name));
    messageWrapper.append(userEl);

    const messageEl = $('<span class="message">');
    messageEl.html(msg)
    messageWrapper.append(messageEl);

    appendMessage(messageWrapper);
}

function onKickUser(user) {
    if (confirm('MΓΆchten Sie den Benutzer "' + user.name + '" wirklich bannen?\nDies wird sich auf alle Benutzer mit der gleichen IP-Adresse auswirken.')) {
        socket.emit('requestKick', user);
    }
}

function onLiftBan(ip) {
    socket.emit('requestLiftBan', ip);
}

function mentionUser(username) {
    const field = $("#chat-host .messageField");
    if (field) {
        const mention = '@' + username + ' ';

        const val = field.val();
        var cursorPos = +field.prop('selectionStart');
        const valBefore = val.substring(0, cursorPos);
        const valAfter = val.substring(cursorPos);

        field.val(valBefore + mention + valAfter);

        cursorPos += mention.length;

        field.prop('selectionStart', cursorPos);
        field.prop('selectionEnd', cursorPos);

        field.focus();
    }
}