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

    /**
     * @type {SlackChan|SlackIms|SlackGroup}
    **/
    ,SELECTED_ROOM = null

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

    /** @type {SlackMessage|null} */
    ,EDITING = null
;

/**
 * @param {SlackChan|SlackIms|SlackGroup} room
 * @param {function(boolean)} cb
**/
function fetchHistory(room, cb) {
    var xhr = new XMLHttpRequest();
    xhr.open('GET', 'api/hist?room=' +room.id, true);
    xhr.send(null);
}

function poll(callback) {
    var xhr = new XMLHttpRequest();
    xhr.timeout = 1000 * 60 * 1; // 3 min timeout
    xhr.onreadystatechange = function(e) {
        if (xhr.readyState === 4) {
            if (xhr.status === 0) {
                if (NEXT_RETRY) {
                    NEXT_RETRY = 0;
                    onNetworkStateUpdated(true);
                }
                poll(callback); // retry on timeout
                return;
            }
            var resp = null
                ,success = Math.floor(xhr.status / 100) === 2;

            if (success) {
                if (NEXT_RETRY) {
                    NEXT_RETRY = 0;
                    onNetworkStateUpdated(true);
                }
                resp = xhr.response;
                try {
                    resp = JSON.parse(/** @type {string} */ (resp));
                } catch (e) {
                    resp = null;
                }
            } 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);
        }
    };
    xhr.open('GET', 'api?v=' +SLACK.lastServerVersion, true);
    xhr.send(null);
}

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

/**
 * @param {SlackChan|SlackIms|SlackGroup} room
**/
function sendTyping(room) {
    var xhr = new XMLHttpRequest()
        ,url = 'api/typing?room=' +room.id;
    xhr.open('POST', url, true);
    xhr.send(null);
}

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

function startPolling() {
    poll(onPollResponse);
}

/**
 * @param {SlackChan|SlackIms|SlackGroup} room
**/
function selectRoom(room) {
    if (SELECTED_ROOM)
        unselectRoom();
    document.getElementById(room.id).classList.add(R.klass.selected);
    document.body.classList.remove(R.klass.noRoomSelected);
    SELECTED_ROOM = room;
    onRoomSelected();
    if (SELECTED_ROOM.lastRead && !SLACK.history[SELECTED_ROOM.id])
        fetchHistory(SELECTED_ROOM, function(success) {});
}

function unselectRoom() {
    document.getElementById(SELECTED_ROOM.id).classList.remove(R.klass.selected);
}

/**
 * @param {SlackGroup|SlackChan|SlackIms} chan
 * @param {string} filename
 * @param {File} file
 * @param {function(string?)} callback
**/
function uploadFile(chan, filename, file, callback) {
    var fileReader = new FileReader()
        ,formData = new FormData()
        ,xhr = new XMLHttpRequest();

    formData.append("file", file);
    formData.append("filename", filename);
    xhr.onreadystatechange = function() {
        if (xhr.readyState === 4) {
            if (xhr.status === 204) {
                callback(null);
            } else {
                callback(xhr.statusText);
            }
        }
    }
    xhr.open('POST', 'api/file?room=' +chan.id);
    xhr.send(formData);
}

/**
 * @param {SlackChan|SlackGroup|SlackIms} chan
 * @param {SlackCommand!} cmd
 * @param {string} args
**/
function doCommand(chan, cmd, args) {
    var xhr = new XMLHttpRequest()
        ,url = 'api/cmd?room=' +chan.id +"&cmd=" +encodeURIComponent(cmd.name.substr(1)) +"&args=" +encodeURIComponent(args);
    xhr.open('POST', url, true);
    xhr.send(null);
}

/**
 * @param {SlackChan|SlackGroup|SlackIms} chan
 * @param {string} msg
 * @param {SlackMessage|null=} replyTo
**/
function sendMsg(chan, msg, replyTo) {
    var xhr = new XMLHttpRequest();
    var url = 'api/msg?room=' +chan.id +"&text=" +encodeURIComponent(msg);
    if (replyTo) {
        var sender = SLACK.context.getMember(replyTo.userId)
            ,footer = "Message";

        if (chan.id[0] === 'C') {
            footer = "Channel message";
        } else if (chan.id[0] === 'D') {
            footer = "Direct message";
        } else if (chan.id[0] === 'G') {
            footer = "Group message";
        }
        var attachment = {
            "fallback": replyTo.text
            ,"author_name": "<@" +sender.id +"|" +sender.name +">"
            ,"author_icon": sender.icons.image_48
            ,"text": replyTo.text
            ,"footer": footer
            ,"ts": replyTo.ts
        };
        url += "&attachments=" +encodeURIComponent(JSON.stringify([attachment]));
    }
    xhr.open('POST', url, true);
    xhr.send(null);
}

/**
 * @param {string} input
 * @param {boolean=} skipCommand
 * @return {boolean} true on recognized input
**/
function onTextEntered(input, skipCommand) {
    var success = true;
    if (EDITING) {
        editMsg(SELECTED_ROOM, input, EDITING);
        return true;
    }
    if (input[0] === '/' && skipCommand !== true) {
        var endCmd = input.indexOf(' ')
            ,cmd = input.substr(0, endCmd === -1 ? undefined : endCmd)
            ,args = endCmd === -1 ? "" : input.substr(endCmd)
            ,cmdObject = SLACK.context.commands.data[cmd];

        if (cmdObject) {
            doCommand(SELECTED_ROOM, cmdObject, args.trim());
            return true;
        }
        return false;
    }
    sendMsg(SELECTED_ROOM, input, REPLYING_TO);
    return true;
}

/**
 * @param {SlackChan|SlackGroup|SlackIms} chan
 * @param {string} text
 * @param {SlackMessage|null=} msg
**/
function editMsg(chan, text, msg) {
    var xhr = new XMLHttpRequest();
    var url = 'api/msg?room=' +chan.id +"&ts=" +msg.id +"&text=" +encodeURIComponent(text);
    xhr.open('PUT', url, true);
    xhr.send(null);
}

/**
 * @param {SlackChan|SlackGroup|SlackIms} chan
 * @param {SlackMessage|null=} msg
**/
function removeMsg(chan, msg) {
    var xhr = new XMLHttpRequest();
    var url = 'api/msg?room=' +chan.id +"&ts=" +msg.id;
    xhr.open('DELETE', url, true);
    xhr.send(null);
}

/**
 * @param {SlackChan|SlackGroup|SlackIms} chan
 * @param {number} ts
**/
function sendReadMArker(chan, ts) {
    var xhr = new XMLHttpRequest();
    var url = 'api/markread?room=' +chan.id +"&ts=" +ts;
    xhr.open('POST', url, true);
    xhr.send(null);
}

/**
 * @param {string} channelId
 * @param {string} msgId
 * @param {string} reaction
**/
function addReaction(channelId, msgId, reaction) {
    var xhr = new XMLHttpRequest();
    var url = 'api/reaction?room=' +channelId +"&msg=" +msgId +"&reaction=" +encodeURIComponent(reaction);
    xhr.open('POST', url, true);
    xhr.send(null);
}

/**
 * @param {string} channelId
 * @param {string} msgId
 * @param {string} reaction
**/
function removeReaction(channelId, msgId, reaction) {
    var xhr = new XMLHttpRequest();
    var url = 'api/reaction?room=' +channelId +"&msg=" +msgId +"&reaction=" +encodeURIComponent(reaction);
    xhr.open('DELETE', url, true);
    xhr.send(null);
}

