var
    /**
     * Minimum time between 2 notifications (ms)
     * @const
     * @type {number}
    **/
    NOTIFICATION_COOLDOWN = 30 * 1000, //30 sec

    /**
     * Maximum time the notification will stay visible (ms)
     * @const
     * @type {number}
    **/
    NOTIFICATION_DELAY = 5 * 1000, // 5 sec

    MSG_GROUPS = [],

    /** @type {number} */
    lastNotificationSpawn = 0;

function onContextUpdated() {
    var chanListFram = document.createDocumentFragment(),
        sortedChans = DATA.context.getChannelIds(function(chan) {
            return !chan.archived && chan.isMember !== false;
        }),
        starred = [],
        channels = [],
        privs = [],
        priv = [],
        chanNames = {};

    sortedChans.sort(function(a, b) {
        if (a[0] !== b[0]) {
            return a[0] - b[0];
        }
        var aChanCtx = DATA.context.getChannelContext(a).getChatContext(),
            bChanCtx = DATA.context.getChannelContext(b).getChatContext(),
            aChan = aChanCtx.channels[a],
            bChan = bChanCtx.channels[b];
        if (aChan.name === bChan.name) {
            chanNames[aChan.id] = locale.chanName(aChanCtx.team.name, aChan.name);
            chanNames[bChan.id] = locale.chanName(bChanCtx.team.name, bChan.name);
            return aChanCtx.team.name.localeCompare(bChanCtx.team.name);
        }
        return aChan.name.localeCompare(bChan.name);
    });
    sortedChans.forEach(function(chanId) {
        var chan = DATA.context.getChannel(chanId),
            chanListItem;
        if (chan instanceof PrivateMessageRoom) {
            if (!chan.user.deleted) {
                if ((chanListItem = createImsListItem(chan, chanNames[chan.id]))) {
                    if (chan.starred)
                        starred.push(chanListItem);
                    else
                        priv.push(chanListItem);
                }
            }
        } else {
            if ((chanListItem = createChanListItem(chan, chanNames[chan.id]))) {
                if (chan.starred)
                    starred.push(chanListItem);
                else if (chan.isPrivate)
                    privs.push(chanListItem);
                else
                    channels.push(chanListItem);
            }
        }
    });
    if (starred.length)
        chanListFram.appendChild(createChanListHeader(locale.starred));
    starred.forEach(function(dom) {
        chanListFram.appendChild(dom);
    });
    if (channels.length)
        chanListFram.appendChild(createChanListHeader(locale.channels));
    channels.forEach(function(dom) {
        chanListFram.appendChild(dom);
    });
    privs.forEach(function(dom) {
        chanListFram.appendChild(dom);
    });
    if (priv.length)
        chanListFram.appendChild(createChanListHeader(locale.privateMessageRoom));
    priv.forEach(function(dom) {
        chanListFram.appendChild(dom);
    });
    document.getElementById(R.id.chanList).textContent = "";
    document.getElementById(R.id.chanList).appendChild(chanListFram);
    filterChanList.apply(document.getElementById(R.id.chanFilter));
    if (setRoomFromHashBang()) {
        createContextBackground(SELECTED_CONTEXT.getChatContext().team.id, SELECTED_CONTEXT.getChatContext().users, function(imgData) {
            document.getElementById(R.id.context).style.backgroundImage = 'url(' +imgData +')';
        });
    } else if (isNative()) {
        var roomId = __native.getPreviousChannel();
        if (roomId)
            document.location.hash = roomId;
    }
}

function onTypingUpdated() {
    DATA.context.foreachContext(function(ctx) {
        var typing = ctx.typing;
        for (var chanId in ctx.self.channels) {
            if (!ctx.self.channels[chanId].archived) {
                var chanDom = document.getElementById("room_" +chanId);
                if (typing[chanId])
                    chanDom.classList.add(R.klass.chatList.typing);
                else
                    chanDom.classList.remove(R.klass.chatList.typing);
            }
        }
        for (var userId in ctx.users) {
            var ims = ctx.users[userId].privateRoom;
            if (ims && !ims.archived) {
                var userDom = document.getElementById("room_" +ims.id);
                if (userDom) {
                    if (typing[ims.id])
                        userDom.classList.add(R.klass.chatList.typing);
                    else
                        userDom.classList.remove(R.klass.chatList.typing);
                }
            }
        }
    });
    updateTypingChat();
}

function updateTypingChat() {
    var typing;

    document.getElementById(R.id.typing).textContent = "";
    if (SELECTED_CONTEXT && SELECTED_ROOM && (typing = SELECTED_CONTEXT.getChatContext().typing[SELECTED_ROOM.id])) {
        var areTyping = document.createDocumentFragment(),
            isOutOfSync = false;
        for (var i in typing) {
            var member = DATA.context.getUser(i);
            if (member)
                areTyping.appendChild(makeUserIsTypingDom(member));
            else
                isOutOfSync = true;
        }
        if (isOutOfSync)
            outOfSync();
        document.getElementById(R.id.typing).appendChild(areTyping);
    }
}

function onNetworkStateUpdated(isNetworkWorking) {
    if (isNetworkWorking)
        document.body.classList.remove(R.klass.noNetwork);
    else
        document.body.classList.add(R.klass.noNetwork);
    updateTitle();
}

function onRoomSelected() {
    var name = SELECTED_ROOM.name || (SELECTED_ROOM.user ? SELECTED_ROOM.user.getName() : undefined);
    if (!name) {
        console.error("No name provided for ", SELECTED_ROOM);
        /** @type {Array.<string>} */
        var members = [];
        for (var userId in SELECTED_ROOM.users)
            members.push(SELECTED_ROOM.users[userId].getName());
        name = members.join(", ");
    }
    var roomLi = document.getElementById("room_" +SELECTED_ROOM.id);
    document.getElementById(R.id.currentRoom.title).textContent = name;
    onRoomUpdated();
    focusInput();

    document.getElementById(R.id.message.file.formContainer).classList.add(R.klass.hidden);
    markRoomAsRead(SELECTED_ROOM);
    if (REPLYING_TO) {
        REPLYING_TO = null;
        onReplyingToUpdated();
    }
    if (EDITING) {
        EDITING = null;
        onReplyingToUpdated();
    }
    updateTypingChat();
}

function onReplyingToUpdated() {
    if (REPLYING_TO) {
        document.body.classList.add(R.klass.replyingTo);
        var domParent = document.getElementById(R.id.message.replyTo),
            closeLink = document.createElement("a");
        closeLink.addEventListener("click", function() {
            REPLYING_TO = null;
            onReplyingToUpdated();
        });
        closeLink.className = R.klass.msg.replyTo.close;
        closeLink.textContent = 'x';
        domParent.textContent = "";
        domParent.appendChild(closeLink);
        domParent.appendChild(REPLYING_TO.duplicateDom());
        focusInput();
    } else {
        document.body.classList.remove(R.klass.replyingTo);
        document.getElementById(R.id.message.replyTo).textContent = "";
        focusInput();
    }
}

function onEditingUpdated() {
    if (EDITING) {
        document.body.classList.add(R.klass.replyingTo);
        var domParent = document.getElementById(R.id.message.replyTo),
            closeLink = document.createElement("a");
        closeLink.addEventListener("click", function() {
            EDITING = null;
            onEditingUpdated();
        });
        closeLink.className = R.klass.msg.replyTo.close;
        closeLink.textContent = 'x';
        domParent.textContent = "";
        domParent.appendChild(closeLink);
        domParent.appendChild(EDITING.duplicateDom());
        document.getElementById(R.id.message.input).value = EDITING.text;
        focusInput();
    } else {
        document.body.classList.remove(R.klass.replyingTo);
        document.getElementById(R.id.message.replyTo).textContent = "";
        focusInput();
    }
}

/**
 * @param {string} chanId
 * @param {string} msgId
 * @param {string} reaction
**/
window['toggleReaction'] = function(chanId, msgId, reaction) {
    var hist = DATA.history[chanId],
        msg,
        ctx;
    if ((hist = DATA.history[chanId]) && (msg = hist.getMessageById(msgId)) && (ctx = DATA.context.getChannelContext(chanId))) {
        if (msg.hasReactionForUser(reaction, ctx.getChatContext().self.id)) {
            removeReaction(chanId, msgId, reaction);
        } else {
            addReaction(chanId, msgId, reaction);
        }
    }
};

/**
 * @param {number} unreadhi
 * @param {number} unread
**/
function setFavicon(unreadhi, unread) {
    if (!unreadhi && !unread)
        document.getElementById(R.id.favicon).href = "favicon_ok.png";
    else
        document.getElementById(R.id.favicon).href = "favicon.png?h="+unreadhi+"&m="+unread;
}

function setNetErrorFavicon() {
    document.getElementById(R.id.favicon).href = "favicon_err.png";
}

function updateTitle() {
    var hasHl = HIGHLIGHTED_CHANS.length,
        title = "";

    if (NEXT_RETRY) {
        title = '!' +locale.netErrorShort +' - Mimouchat';
        setNetErrorFavicon();

    } else if (hasHl) {
        title = "(!" +hasHl +")";
        setFavicon(hasHl, hasHl);
    } else {
        var hasUnread = 0;
        DATA.context.foreachChannels(function(i) {
            if (i.lastMsg > i.lastRead)
                hasUnread++;
        });
        if (hasUnread)
            title = "(" +hasUnread +")";
        setFavicon(0, hasUnread);
    }
    if (!title.length && SELECTED_ROOM)
        title = SELECTED_ROOM.name;
    title = title.length ? title : "Mimouchat";
    document.title = title;
    setNativeTitle(title);
}

function spawnNotification() {
    if (!("Notification" in window))
    {}
    else if (Notification.permission === "granted") {
        var now = Date.now();

        if (lastNotificationSpawn + NOTIFICATION_COOLDOWN < now) {
            var n = new Notification(locale.newMessage);
            lastNotificationSpawn = now;
            setTimeout(function() {
                n.close();
            }, NOTIFICATION_DELAY);
        }
    }
    else if (Notification.permission !== "denied")
        Notification.requestPermission();
}

function onRoomUpdated() {
    var chatFrag = document.createDocumentFragment(),
        currentRoomId = SELECTED_ROOM.id,
        prevMsg = null,
        firstTsCombo = 0,
        prevMsgDom = null,
        prevTs = 0,
        currentMsgGroupDom;

    if (SELECTED_ROOM.starred)
        document.getElementById(R.id.mainSection).classList.add(R.klass.starred);
    else
        document.getElementById(R.id.mainSection).classList.remove(R.klass.starred);
    MSG_GROUPS = [];
    if (DATA.history[currentRoomId])
        DATA.history[currentRoomId].messages.forEach(function(msg) {
            if (!msg.removed) {
                var dom = msg.getDom(),
                    newGroupDom = false,
                    sameDayThanPrevious = isSameDay(msg.ts, prevTs);

                if (prevMsg && prevMsg.userId === msg.userId && msg.userId && sameDayThanPrevious) {
                    if (Math.abs(firstTsCombo -msg.ts) < 30 && !(msg instanceof MeMessage))
                        prevMsgDom.classList.add(R.klass.msg.sameTs);
                    else
                        firstTsCombo = msg.ts;
                } else {
                    firstTsCombo = msg.ts;
                    newGroupDom = true;
                }
                if (SELECTED_ROOM_UNREAD > -1 &&
                        (!prevMsg || prevMsg.ts <= SELECTED_ROOM_UNREAD) &&
                        msg.ts > SELECTED_ROOM_UNREAD) {
                    dom.classList.add(R.klass.msg.firstUnread);
                    dom.insertBefore(UNREAD_INDICATOR_DOM, dom.firstChild);
                } else {
                    dom.classList.remove(R.klass.msg.firstUnread);
                }
                if (msg instanceof MeMessage) {
                    prevMsg = null;
                    prevMsgDom = null;
                    firstTsCombo = 0;
                    newGroupDom = true;
                    chatFrag.appendChild(dom);
                    currentMsgGroupDom = null;
                } else {
                    if (newGroupDom || !currentMsgGroupDom) {
                        currentMsgGroupDom = createMessageGroupDom(DATA.context.getUser(msg.userId), msg.username);
                        MSG_GROUPS.push(currentMsgGroupDom);
                        chatFrag.appendChild(currentMsgGroupDom);
                    }
                    prevMsg = msg;
                    prevMsgDom = dom;
                    currentMsgGroupDom.content.appendChild(dom);
                }
                if (sameDayThanPrevious) {
                    (currentMsgGroupDom || dom).classList.remove(R.klass.msg.firstOfDay);
                } else {
                    (currentMsgGroupDom || dom).classList.add(R.klass.msg.firstOfDay);
                    (currentMsgGroupDom || dom).dataset["date"] = locale.formatDay(msg.ts);
                }
                prevTs = msg.ts;
            } else {
                msg.removeDom();
            }
        });
    var isFirstPending = true;
    PENDING_MESSAGES.forEach(function(msg) {
        if (msg.channel === SELECTED_ROOM.id) {
            if (isFirstPending) {
                currentMsgGroupDom = createMessageGroupDom(SELECTED_CONTEXT.self, "");
                chatFrag.appendChild(currentMsgGroupDom);
                isFirstPending = false;
            }
            currentMsgGroupDom.content.appendChild(msg.dom);
        }
    });
    var content = document.getElementById(R.id.currentRoom.content);
    //TODO lazy add dom if needed
    content.textContent = "";
    content.appendChild(chatFrag);
    //TODO scroll lock
    content.scrollTop = content.scrollHeight -content.clientHeight;
    updateTitle();
    if (window.hasFocus)
        markRoomAsRead(SELECTED_ROOM);
}

function onMsgClicked(target, msg) {
    if (target.classList.contains(R.klass.msg.hover.reply)) {
        if (EDITING) {
            EDITING = null;
            onEditingUpdated();
        }
        if (REPLYING_TO !== msg) {
            REPLYING_TO = msg;
            onReplyingToUpdated();
        }
    } else if (target.classList.contains(R.klass.msg.hover.reaction)) {
        var currentRoomId = SELECTED_ROOM.id,
            currentMsgId = msg.id;
        EMOJI_BAR.spawn(document.body, SELECTED_CONTEXT, function(emoji) {
            if (emoji)
                addReaction(currentRoomId, currentMsgId, emoji);
        });
    } else if (target.classList.contains(R.klass.msg.hover.edit)) {
        if (REPLYING_TO) {
            REPLYING_TO = null;
            onReplyingToUpdated();
        }
        if (EDITING !== msg) {
            EDITING = msg;
            onEditingUpdated();
        }
    } else if (target.classList.contains(R.klass.msg.hover.star)) {
        if (msg.starred)
            unstarMsg(SELECTED_ROOM, msg);
        else
            starMsg(SELECTED_ROOM, msg);
    } else if (target.classList.contains(R.klass.msg.hover.pin)) {
        if (msg.pinned)
            unpinMsg(SELECTED_ROOM, msg);
        else
            pinMsg(SELECTED_ROOM, msg);
    } else if (target.classList.contains(R.klass.msg.hover.remove)) {
        //TODO prompt confirm
        if (REPLYING_TO) {
            REPLYING_TO = null;
            onReplyingToUpdated();
        }
        if (EDITING) {
            EDITING = null;
            onEditingUpdated();
        }
        removeMsg(SELECTED_ROOM, msg);
    }
}

/**
 * @param {Room} channel
 * @param {string} input
 * @param {boolean} isMeMessage
**/
function displayTmpMessage(channel, input, isMeMessage) {
    var obj = {
        channel: channel.id,
        text: input.trim(),
        isMe: isMeMessage,
        dom: createTmpMsgDom(input, isMeMessage)
    };
    PENDING_MESSAGES.push(obj);
    if (channel === SELECTED_ROOM)
        onRoomUpdated();
    return obj;
}

function chatClickDelegate(e) {
    var target = e.target,
        getMessageId = function(e, target) {
            target = target || e.target;
            while (target !== e.currentTarget && target) {
                if (target.id && target.classList.contains(R.klass.msg.item)) {
                    return target.id;
                }
                target = target.parentElement;
            }
        };
    while (target !== e.currentTarget && target) {
        if (target.classList.contains(R.klass.msg.hover.container)) {
            return;
        }
        var messageId,
            msg;
        if (target.parentElement && target.classList.contains(R.klass.msg.attachment.actionItem)) {
            var attachmentIndex = target.dataset["attachmentIndex"],
                actionIndex = target.dataset["actionIndex"];

            messageId = getMessageId(e, target);
            if (messageId && attachmentIndex !== undefined && actionIndex !== undefined) {
                messageId = messageId.substr(messageId.lastIndexOf("_") +1);
                msg = DATA.history[SELECTED_ROOM.id].getMessageById(messageId);

                if (msg && msg.attachments[attachmentIndex] && msg.attachments[attachmentIndex].actions && msg.attachments[attachmentIndex].actions[actionIndex]) {
                    confirmAction(SELECTED_ROOM.id, msg, msg.attachments[attachmentIndex], msg.attachments[attachmentIndex].actions[actionIndex]);
                }
                return;
            }
        }
        if (target.parentElement && target.parentElement.classList.contains(R.klass.msg.hover.container)) {
            if ((messageId = getMessageId(e, target))) {
                messageId = messageId.substr(messageId.lastIndexOf("_") +1);
                msg = DATA.history[SELECTED_ROOM.id].getMessageById(messageId);

                if (msg)
                    onMsgClicked(target, msg);
            }
            return;
        }
        target = target.parentElement;
    }
}

function confirmAction(roomId, msg, attachmentObject, actionObject) {
    var confirmed = function() {
        var payload = getActionPayload(roomId, msg, attachmentObject, actionObject);

        sendCommand(payload, msg.userId);
    };
    if (actionObject["confirm"]) {
        (new ConfirmDialog(actionObject["confirm"]["title"], actionObject["confirm"]["text"]))
            .setButtonText(actionObject["confirm"]["ok_text"], actionObject["confirm"]["dismiss_text"])
            .onConfirm(confirmed)
            .spawn();
    } else {
        confirmed();
    }
}

function updateAuthorAvatarImsOffset() {
    if (CONFIG.isDisplayAvatars() && CONFIG.isScrollAvatars()) {
        var chatDom = document.getElementById(R.id.currentRoom.content),
            chatTop = chatDom.getBoundingClientRect().top;

        MSG_GROUPS.forEach(function(group) {
            var imgDom = group.authorImgWrapper,
                imgSize = imgDom.clientHeight,
                domRect = group.getBoundingClientRect(),
                _top = 0;

            imgDom.style.top = Math.max(0, Math.min(chatTop -domRect.top, domRect.height -imgSize -(imgSize /2))) +"px";
        });
    }
}

function onEmojiReady() {
    var emojiButton = document.getElementById(R.id.message.emoji);

    invalidateAllMessages();
    EMOJI_BAR.reset();

    if ('makeEmoji' in window) {
        emojiButton.style.backgroundImage = 'url("smile.svg")';
        emojiButton.classList.remove(R.klass.hidden);
    } else {
        emojiButton.classList.add(R.klass.hidden);
    }
}

function _toggleMenu() {
    var menu = document.getElementById(R.id.context);
    if (menu.classList.contains(R.klass.opened))
        menu.classList.remove(R.klass.opened);
    else
        menu.classList.add(R.klass.opened);
}

document.addEventListener('DOMContentLoaded', function() {
    initLang();

    // FIXME load config
    initHljs();

    initMsgInput();

    var chanSearch = document.getElementById(R.id.chanFilter);
    chanSearch.addEventListener("input", filterChanList);
    chanSearch.addEventListener("blur", filterChanList);

    document.getElementById(R.id.currentRoom.content).addEventListener("click", chatClickDelegate);
    window.addEventListener("hashchange", function(e) {
        if (document.location.hash && document.location.hash[0] === '#') {
            setRoomFromHashBang();
        }
    });

    document.addEventListener("mouseover", function(e) {
        var t = e.target;
        if (roomInfo.isParentOf(t)) {
            roomInfo.cancelHide();
            return;
        }
        while (t && t !== this) {
            if (t.nodeName === 'A') {
                var href = t.href,
                    sepPosition = href.indexOf('#');
                if (sepPosition >= 0) {
                    href = href.substr(sepPosition +1);
                    var channelCtx = DATA.context.getChannelContext(href);
                    if (channelCtx) {
                        roomInfo.populate(channelCtx, channelCtx.getChatContext().channels[href]).show(t);
                        return;
                    }
                    channelCtx = DATA.context.getUserContext(href);
                    if (channelCtx) {
                        var room = channelCtx.getChatContext().users[href].privateRoom;
                        if (room) {
                            roomInfo.populate(channelCtx, room).show(t);
                            return;
                        }
                    }
                }
            }
            t = t.parentElement;
        }
        roomInfo.hideDelayed();
    });

    document.getElementById(R.id.contextButton).addEventListener("click", _toggleMenu);

    document.getElementById(R.id.currentRoom.starButton).addEventListener("click", function(e) {
        e.preventDefault();
        if (SELECTED_ROOM) {
            if (SELECTED_ROOM.starred) {
                unstarChannel(SELECTED_ROOM);
            } else {
                starChannel(SELECTED_ROOM);
            }
        }
        return false;
    });
    document.getElementById(R.id.message.file.cancel).addEventListener("click", function(e) {
        e.preventDefault();
        document.getElementById(R.id.message.file.error).classList.add(R.klass.hidden);
        document.getElementById(R.id.message.file.formContainer).classList.add(R.klass.hidden);
        document.getElementById(R.id.message.file.fileInput).value = "";
        return false;
    });
    document.getElementById(R.id.settings.logout).addEventListener("click", logout);
    document.getElementById(R.id.settings.menuButton).addEventListener("click", function(e) {
        e.preventDefault();
        Settings.display().setClosable(true);
    });
    document.getElementById(R.id.message.file.form).addEventListener("submit", function(e) {
        e.preventDefault();
        var fileInput = document.getElementById(R.id.message.file.fileInput),
            filename = fileInput.value;

        if (filename) {
            filename = filename.substr(filename.lastIndexOf('\\') +1);
            uploadFile(SELECTED_ROOM, filename, fileInput.files[0], function(errorMsg) {
                var error = document.getElementById(R.id.message.file.error);
                if (errorMsg) {
                    error.textContent = errorMsg;
                    error.classList.remove(R.klass.hidden);
                } else {
                    error.classList.add(R.klass.hidden);
                    document.getElementById(R.id.message.file.fileInput).value = "";
                    document.getElementById(R.id.message.file.formContainer).classList.add(R.klass.hidden);
                }
            });
        }
        return false;
    });
    document.getElementById(R.id.message.file.bt).addEventListener("click", function(e) {
        e.preventDefault();
        if (SELECTED_ROOM) {
            document.getElementById(R.id.message.file.formContainer).classList.remove(R.klass.hidden);
        }
        return false;
    });
    document.getElementById(R.id.message.submitButton).addEventListener("click", function(e) {
        e.preventDefault();
        onMsgFormSubmit();
        return false;
    });
    document.getElementById(R.id.message.form).addEventListener("submit", function(e) {
        e.preventDefault();
        onMsgFormSubmit();
        return false;
    });
    window.addEventListener('blur', function() {
        window.hasFocus = false;
    });
    window.addEventListener('focus', function() {
        window.hasFocus = true;
        lastNotificationSpawn = 0;
        if (SELECTED_ROOM)
            markRoomAsRead(SELECTED_ROOM);
        focusInput();
    });
    window.hasFocus = true;

    var emojiButton = document.getElementById(R.id.message.emoji);
    emojiButton.addEventListener("click", function() {
        if (SELECTED_CONTEXT)
            EMOJI_BAR.spawn(document.body, SELECTED_CONTEXT.getChatContext(), function(e) {
                if (e) document.getElementById(R.id.message.input).value += ":"+e+":";
                focusInput();
            });
    });

    if (isNative()) {
        __native.readSmsPermission(CALLBACK.makeCallback(function() {
            var response = JSON.parse(__native.getStatic());
            if (response) {
                DATA.update(response);
            }
        }));
        document.body.classList.add(R.klass.native);
    }
    if (!IS_LOCAL)
        startPolling();
});

function onMsgFormSubmit() {
    var input = document.getElementById(R.id.message.input);
    if (SELECTED_ROOM && input.value) {
        if (onTextEntered(input.value)) {
            input.value = "";
            if (REPLYING_TO) {
                REPLYING_TO = null;
                onReplyingToUpdated();
            }
            if (EDITING) {
                EDITING = null;
                onReplyingToUpdated();
            }
            document.getElementById(R.id.message.slashComplete).textContent = '';
            updateInputRowCount(input);
        }
    }
    focusInput();
}

