/* jshint sub: true */

var
    /**
     * @type {number} next period to wait before next retry in case of failure, in seconds
    **/
    NEXT_RETRY = 0,

    /**
     * @type {Room|null}
    **/
    SELECTED_ROOM = null,

    /**
     * @type {number}
    **/
    SELECTED_ROOM_UNREAD = -1,

    /**
     * @type {Element}
    **/
    UNREAD_INDICATOR_DOM = document.createElement("span"),

    /**
     * @type {SimpleChatSystem|null}
    **/
    SELECTED_CONTEXT = null,

    /** @type {Message|null} */
    REPLYING_TO = null,

    /** @type {Message|null} */
    EDITING = null,

    /** @const @type {number} */
    KEEP_MESSAGES = 100,

    /** @type {Array<{channel: string, text: string, isMe: boolean, pendingId: (string|undefined), dom: Element}>} */
    PENDING_MESSAGES = []
;

function initHljs() {
    HttpRequestWrapper("highlight.pack.js")
        .callbackSuccess(function(code, head, resp) {
            var script = document.createElement("script"),
                link = document.createElement("link");

            script.innerHTML = resp;
            script.language = "text/javascript";
            link.href = "hljs-androidstudio.css";
            link.rel = "stylesheet";
            document.head.appendChild(link);
            document.body.appendChild(script);
        })
        .callbackError(function() {
            console.error("Failure loading hljs required files");
        })
        .setTimeout(1000 * 60 * 1) // 1 min timeout
        .send();
}

/**
 * @param {Room} room
 * @param {function(boolean)} cb
**/
function fetchHistory(room, cb) {
    HttpRequestWrapper("api/hist?room=" +HttpRequestWrapper.encode(room.id))
        .setResponseType(HttpRequestResponseType.JSON)
        .callbackSuccess(function(code, status, resp) {
            if (resp) {
                var history = DATA.history[room.id],
                    updated,
                    now = Date.now();
                if (!history) {
                    history = DATA.history[room.id] = new UiRoomHistory(room, KEEP_MESSAGES, /** @type {Array} */ (resp), now);
                    updated = true;
                } else {
                    updated = !!history.pushAll(/** @type {Array} */ (resp), now);
                }
                if (updated) {
                    if (history.messages.length)
                        room.setLastMsg(history.lastMessage().ts, now);
                    onMsgReceived(DATA.context.getChannelContext(room.id).getChatContext(), room, /** @type {Array} */ (resp));
                    if (room === SELECTED_ROOM)
                        onRoomUpdated();
                }
            }
        }, this)
        .callback(function() {
            // TODO ui stop loading
        })
        .send();
}

function onConfigUpdated() {
    if (isObjectEmpty(CONFIG.services)) {
        Settings.setClosable(false).display(Settings.pages.services);
    }
    loadEmojiProvider(CONFIG.getEmojiProvider());

    if (CONFIG.isScrollAvatars()) {
        document.getElementById(R.id.currentRoom.content).addEventListener('scroll', updateAuthorAvatarImsOffset);
        updateAuthorAvatarImsOffset();
    } else {
        invalidateAllMessages();
        document.getElementById(R.id.currentRoom.content).removeEventListener('scroll', updateAuthorAvatarImsOffset);
    }
    for (var roomId in DATA.history) {
        DATA.history[roomId].messages.forEach(function(msg) {
            if (msg.dom)
                msg.updateDom();
        });
    }
    document.getElementById(R.id.stylesheet).innerHTML = CONFIG.compileCSS();
}

function poll(callback) {
    if (!IS_LOCAL) {
        HttpRequestWrapper("api?v=" +DATA.lastServerVersion)
            .callback(function(statusCode, statusText, resp) {
                var success = Math.floor(statusCode / 100) === 2;

                if (success) {
                    if (NEXT_RETRY) {
                        NEXT_RETRY = 0;
                        onNetworkStateUpdated(true);
                    }
                } else {
                    if (NEXT_RETRY) {
                        NEXT_RETRY += Math.floor((NEXT_RETRY || 5)/2);
                        NEXT_RETRY = Math.min(60, NEXT_RETRY);
                    } else {
                        NEXT_RETRY = 5;
                        onNetworkStateUpdated(false);
                    }
                }
                callback(success, resp);
            })
            .setResponseType(HttpRequestResponseType.JSON)
            .setTimeout(1000 * 60 * 1)
            .send();
    }
}

function outOfSync() {
    DATA.lastServerVersion = 0;
}

/**
 * @param {Room} room
**/
function sendTyping(room) {
    HttpRequestWrapper(HttpRequestMethod.POST, "api/typing?room=" +HttpRequestWrapper.encode(room.id)).send();
}

/**
 * @param {boolean} success
 * @param {*} response
**/
function onPollResponse(success, response) {
    if (success) {
        if (response) {
            DATA.update(response);
        }
        startPolling();
    } else {
        setTimeout(startPolling, NEXT_RETRY * 1000);
    }
}

function startPolling() {
    poll(onPollResponse);
}

function setNativeTitle(title) {
    if (isNative()) {
        var enabled = !!(SELECTED_CONTEXT && SELECTED_CONTEXT.getChatContext().capacities["starChannel"]);
        __native.setTitle(title, enabled, enabled ? SELECTED_ROOM.starred : false);
    }
}

/**
 * @param {Room} room
**/
function selectRoom(room) {
    if (SELECTED_ROOM)
        unselectRoom();

    UNREAD_INDICATOR_DOM.className = R.klass.msg.unread;
    UNREAD_INDICATOR_DOM.textContent = locale.newMessage;

    document.getElementById("room_" +room.id).classList.add(R.klass.selected);
    document.body.classList.remove(R.klass.noRoomSelected);
    SELECTED_ROOM = room;
    SELECTED_CONTEXT = /** @type {SimpleChatSystem} */ (DATA.context.getChannelContext(room.id));
    SELECTED_ROOM_UNREAD = room.lastRead;
    if (isNative())
        __native.setCurrentChannel(room.id);
    onRoomSelected();
    roomInfo.hide();
    createContextBackground(SELECTED_CONTEXT.getChatContext().team.id, SELECTED_CONTEXT.getChatContext().users, function(imgData) {
        document.getElementById(R.id.context).style.backgroundImage = 'url(' +imgData +')';
    });
    if (!DATA.history[SELECTED_ROOM.id] || DATA.history[SELECTED_ROOM.id].messages.length < KEEP_MESSAGES)
        fetchHistory(SELECTED_ROOM, function(success) {});
    document.getElementById(R.id.mainSection).classList.remove(R.klass.noRoomSelected);
    document.getElementById(R.id.context).classList.remove(R.klass.opened);
}

function setRoomFromHashBang() {
    var hashId = document.location.hash.substr(1),
        room = DATA.context.getChannel(hashId);

    if (room && room !== SELECTED_ROOM) {
        selectRoom(room);
        return true;
    } else {
        var user = DATA.context.getUser(hashId);
        if (user && user.privateRoom) {
            selectRoom(user.privateRoom);
            return true;
        }
    }
    return false;
}

/**
 * @param {Room} room
**/
function starChannel(room) {
    HttpRequestWrapper(HttpRequestMethod.POST, "api/starChannel?room=" +HttpRequestWrapper.encode(room.id)).send();
}

/**
 * @param {Room} room
**/
function unstarChannel(room) {
    HttpRequestWrapper(HttpRequestMethod.POST, "api/unstarChannel?room=" +HttpRequestWrapper.encode(room.id)).send();
}

function unselectRoom() {
    document.getElementById("room_" +SELECTED_ROOM.id).classList.remove(R.klass.selected);
    document.getElementById(R.id.mainSection).classList.add(R.klass.noRoomSelected);
    UNREAD_INDICATOR_DOM.remove();
}

/**
 * @param {Room} chan
 * @param {string} filename
 * @param {File} file
 * @param {function(string?)} callback
**/
function uploadFile(chan, filename, file, callback) {
    var fileReader = new FileReader(),
        formData = new FormData();

    // FIXME NATIVE !
    formData.append("file", file);
    formData.append("filename", filename);
    HttpRequestWrapper(HttpRequestMethod.POST, "api/file?room=" +HttpRequestWrapper.encode(chan.id))
        .callbackSuccess(function() { callback(null); })
        .callbackError(function(sCode, sText, resp) { callback(sText); })
        .send(formData);
}

/**
 * @param {string} payload
 * @param {string} serviceId
 * @param {(function((string|null)))=} callback
**/
function sendCommand(payload, serviceId, callback) {
    var xhr = HttpRequestWrapper(HttpRequestMethod.POST, "api/attachmentAction?serviceId=" +HttpRequestWrapper.encode(serviceId));
    if (callback)
        xhr.callbackSuccess(function() {
            callback(null);
        }).callbackError(function(code, head, resp) {
            callback(head);
        });
    xhr.send(JSON.stringify(payload));
}

function getActionPayload(channelId, msg, attachment, action) {
    var payload = {
        "actions": [ action ],
        "attachment_id": attachment["id"],
        "callback_id": attachment["callback_id"],
        "channel_id": channelId,
        "is_ephemeral": msg instanceof NoticeMessage,
        "message_ts": msg["id"]
    };
    return payload;
}

/**
 * @param {Room} chan
 * @param {Command!} cmd
 * @param {string} args
**/
function doCommand(chan, cmd, args) {
    HttpRequestWrapper(HttpRequestMethod.POST, "api/cmd?room=" +HttpRequestWrapper.encode(chan.id) +"&cmd=" +HttpRequestWrapper.encode(cmd.name.substr(1)) +"&args=" +HttpRequestWrapper.encode(args)).send();
}

/**
 * @param {Room} chan
 * @param {string} msg
 * @param {Object=} pendingObj
 * @param {Message|null=} replyTo
**/
function sendMsg(chan, msg, pendingObj, replyTo) {
    var url = 'api/msg?room=' +HttpRequestWrapper.encode(chan.id) +"&text=" +HttpRequestWrapper.encode(msg);
    if (replyTo) {
        var sender = DATA.context.getUser(replyTo.userId),
            footer = chan.isPrivate ? locale.message : chan.name;

        var attachment = {
            "fallback": replyTo.text,
            "author_name": sender.getName(),
            "text": replyTo.text,
            "footer": footer,
            "ts": replyTo.ts
        };
        url += "&attachments=" +HttpRequestWrapper.encode(JSON.stringify([attachment]));
    }
    SELECTED_ROOM_UNREAD = -1;
    UNREAD_INDICATOR_DOM.remove();
    HttpRequestWrapper(HttpRequestMethod.POST, url).setResponseType(HttpRequestResponseType.JSON).callbackSuccess(function(code, text, resp) {
        if (resp && resp["pendingId"] !== undefined) {
            pendingObj.pendingId = resp["pendingId"];
        }
    }).send();
}

/**
 * @param {Room} chan
 * @param {string} text
 * @param {Message} msg
**/
function editMsg(chan, text, msg) {
    HttpRequestWrapper(HttpRequestMethod.PUT, "api/msg?room=" +HttpRequestWrapper.encode(chan.id) +"&ts=" +msg.id +"&text=" +HttpRequestWrapper.encode(text)).send();
}

/**
 * @param {Room} chan
 * @param {Message} msg
**/
function removeMsg(chan, msg) {
    HttpRequestWrapper(HttpRequestMethod.DELETE, "api/msg?room=" +HttpRequestWrapper.encode(chan.id) +"&ts=" +msg.id).send();
}

/**
 * @param {Room} chan
 * @param {Message} msg
**/
function pinMsg(chan, msg) {
    HttpRequestWrapper(HttpRequestMethod.POST, "api/pinMsg?room=" +HttpRequestWrapper.encode(chan.id) +"&msgId=" +msg.id).send();
}

/**
 * @param {Room} chan
 * @param {Message} msg
**/
function starMsg(chan, msg) {
    HttpRequestWrapper(HttpRequestMethod.POST, "api/starMsg?room=" +HttpRequestWrapper.encode(chan.id) +"&msgId=" +msg.id).send();
}

/**
 * @param {Room} chan
 * @param {Message} msg
**/
function unpinMsg(chan, msg) {
    HttpRequestWrapper(HttpRequestMethod.DELETE, "api/pinMsg?room=" +HttpRequestWrapper.encode(chan.id) +"&msgId=" +msg.id).send();
}

/**
 * @param {Room} chan
 * @param {Message} msg
**/
function unstarMsg(chan, msg) {
    HttpRequestWrapper(HttpRequestMethod.DELETE, "api/starMsg?room=" +HttpRequestWrapper.encode(chan.id) +"&msgId=" +msg.id).send();
}

/**
 * @param {Room} chan
 * @param {string} id
 * @param {number} ts
**/
function sendReadMarker(chan, id, ts) {
    HttpRequestWrapper(HttpRequestMethod.POST, "api/markread?room=" +HttpRequestWrapper.encode(chan.id) +"&id=" +id +"&ts=" +ts).send();
}

/**
 * @param {string} channelId
 * @param {string} msgId
 * @param {string} reaction
**/
function addReaction(channelId, msgId, reaction) {
    HttpRequestWrapper(HttpRequestMethod.POST, "api/reaction?room=" +HttpRequestWrapper.encode(channelId) +"&msg=" +msgId +"&reaction=" +HttpRequestWrapper.encode(reaction)).send();
}

/**
 * @param {string} channelId
 * @param {string} msgId
 * @param {string} reaction
**/
function removeReaction(channelId, msgId, reaction) {
    HttpRequestWrapper(HttpRequestMethod.DELETE, "api/reaction?room=" +HttpRequestWrapper.encode(channelId) +"&msg=" +msgId +"&reaction=" +HttpRequestWrapper.encode(reaction)).send();
}

function logout() {
    HttpRequestWrapper(HttpRequestMethod.POST, "api/logout").send();
    document.cookie = "sessID=;Path=/;expires=Thu, 01 Jan 1970 00:00:01 GMT;";
    document.location.reload();
    if (isNative())
        __native.logout();
}

/**
 * @this {Element}
**/
function filterChanList() {
    var chans = {},
        matchingChans = [],
        val = this.value;
    DATA.context.foreachChannels(function(chan) {
        chans[chan.id] = chan.matchString(val, Utils);
    });
    for (var chanId in chans) {
        var chanDom = document.getElementById("room_" +chanId);
        if (chanDom) {
            if (chans[chanId].name + chans[chanId].members + chans[chanId].topic +chans[chanId].purpose) {
                chanDom.classList.remove(R.klass.hidden);
                matchingChans.push(chanId);
            } else {
                chanDom.classList.add(R.klass.hidden);
            }
        }
    }
    //TODO sort
}

