
const ChatContext = require('./context.js').ChatContext
    ,ChatInfo = require('./context.js').ChatInfo
    ,Command = require('./context.js').Command
    ,Room = require('./room.js').Room
    ,Chatter = require('./chatter.js').Chatter
    ,PrivateMessageRoom = require('./room.js').PrivateMessageRoom;

const SLACK_TYPING_DELAY = 6000;

/**
 * @constructor
 * @extends {ChatInfo}
**/
function SlackTeam(teamId) {
    ChatInfo.call(this, teamId);

    /** @type {string} */
    this.domain;
    /** @type {string} */
    this.callApp;
    /** @type {string} */
    this.callAppName;
    /** @type {boolean} */
    this.fileUploadPermission;
    /** @type {boolean} */
    this.fileEditPermission;
    /** @type {boolean} */
    this.fileDeletePermission;
    /** @type {string} */
    this.icons = {
        small: ""
        ,large: ""
    };
};
SlackTeam.prototype = Object.create(ChatInfo.prototype);
SlackTeam.prototype.constructor = SlackTeam;

SlackTeam.prototype.update = function(teamData, t) {
    ChatInfo.prototype.update.call(this, teamData, t);
    if (teamData["domain"] !== undefined) this.domain = teamData["domain"];
    if (teamData["prefs"]) {
        this.callApp = teamData["prefs"]["calling_app_id"];
        this.callAppName = teamData["prefs"]["calling_app_name"];
        this.fileUploadPermission = teamData["prefs"]["disable_file_uploads"];
        this.fileEditPermission = teamData["prefs"]["disable_file_editing"];
        this.fileDeletePermission = teamData["prefs"]["disable_file_deleting"];
    }
    if (teamData["icon"]) {
        this.icons.small = teamData["icon"]["image_34"];
        this.icons.large = teamData["icon"]["image_230"];
    }
};

/**
 * @constructor
**/
function SlackChan(teamId, chanId, isGroup) {
    Room.call(this, teamId +chanId);
    /** @const @type {string} */
    this.remoteId = chanId;
    this.isPrivate = isGroup;
};

SlackChan.prototype = Object.create(Room.prototype);
SlackChan.prototype.constructor = SlackChan;

SlackChan.prototype.setNameFromMembers = function() {
    var userNames = [];
    for (var i in this.users)
        if (!this.users[i].deleted)
            userNames.push(this.users[i].name);
    userNames.sort();
    this.name = userNames.join(", ");
};

/**
 * @param {*} chanData
 * @param {ChatContext} ctx
 * @param {number} t
 * @param {string=} idPrefix
**/
SlackChan.prototype.update = function(chanData, ctx, t, idPrefix) {
    if (chanData["last_read"] !== undefined)
        chanData["last_read"] = parseFloat(chanData["last_read"]) * 1000;
    if (chanData["latest"] !== undefined)
        chanData["last_msg"] = parseFloat(chanData["latest"]) * 1000;
    return Room.prototype.update.call(this, chanData, ctx, t, idPrefix);
};

/**
 * @constructor
 * @extends {PrivateMessageRoom}
 * @param {string} id
 * @param {SlackChatter} user
**/
function SlackIms(teamId, id, user) {
    PrivateMessageRoom.call(this, teamId +id, user);

    /** @const @type {string} */
    this.remoteId = id;
};
SlackIms.prototype = Object.create(PrivateMessageRoom.prototype);
SlackIms.prototype.constructor = SlackIms;

/**
 * @param {*} chanData
 * @param {ChatContext} ctx
 * @param {number} t
 * @param {string=} idPrefix
**/
SlackIms.prototype.update = function(chanData, ctx, t, idPrefix) {
    if (chanData["last_read"] !== undefined)
        chanData["last_read"] = parseFloat(chanData["last_read"]) * 1000;
    if (chanData["latest"] !== undefined)
        chanData["last_msg"] = parseFloat(chanData["latest"]) * 1000;
    return PrivateMessageRoom.prototype.update.call(this, chanData, ctx, t, idPrefix);
};

/**
 * @constructor
 * @extends {Chatter}
**/
function SlackChatter(teamId, id) {
    Chatter.call(this, teamId +id);

    /** @type {string} */
    this.remoteId = id;

    /** @type {Object.<string, string>} */
    this.icons = {
        small: ""
        ,large: ""
    };
};
SlackChatter.prototype = Object.create(Chatter.prototype);
SlackChatter.prototype.constructor = SlackChatter;

SlackChatter.prototype.setPresence = function(presenceStr, t) {
    this.presence = presenceStr !== 'away';
    this.version = Math.max(t, this.version);
};

SlackChatter.prototype.update = function(userData, t) {
    Chatter.prototype.update.call(this, userData, t);

    if (userData["profile"]) {
        this.email = userData["profile"]["email"];
        this.firstName = userData["profile"]["first_name"];
        this.lastName = userData["profile"]["last_name"];
    }
};

SlackChatter.prototype.getSmallIcon = function() {
    return this.icons.small;
};

SlackChatter.prototype.getLargeIcon = function() {
    return this.icons.large;
};

/**
 * @constructor
 * @extends {SlackChatter}
**/
function SlackUser(teamId, id) {
    SlackChatter.call(this, teamId, id);
};
SlackUser.prototype = Object.create(SlackChatter.prototype);
SlackUser.prototype.constructor = SlackUser;

SlackUser.prototype.update = function(userData, t) {
    SlackChatter.prototype.update.call(this, userData, t);

    if (userData["profile"]) {
        this.icons.small = userData["profile"]["image_48"] || this.icons.small;
        this.icons.large = userData["profile"]["image_512"] || this.icons.large;
    }
};

/**
 * @constructor
 * @extends {SlackChatter}
**/
function SlackBot(teamId, id) {
    SlackChatter.call(this, teamId, id);
    /** @type {string} */
    this.appId;
    this.isBot = true;
};
SlackBot.prototype = Object.create(SlackChatter.prototype);
SlackBot.prototype.constructor = SlackBot;

/** @param {*} botData */
SlackBot.prototype.update = function(botData, t) {
    SlackChatter.prototype.update.call(this, botData, t);
    if (botData["app_id"] !== undefined) this.appId = botData["app_id"];
    this.isBot = true;
    if (botData["icons"]) {
        this.icons.small = botData["icons"]["image_48"] || this.icons.small;
        this.icons.large = botData["icons"]["image_72"] || this.icons.large;
    }
};

/**
 * @constructor
 * @extends {ChatContext}
**/
function SlackData(slack) {
    ChatContext.call(this);

    /**
     * Node serv handler
     * @type {*}
    **/
    this.slack = slack;
};
SlackData.prototype = Object.create(ChatContext.prototype);
SlackData.prototype.constructor = SlackData;

/**
 * @param {*} data
**/
SlackData.prototype.updateStatic = function(data, t) {
    if (data["team"] && !this.team) this.team = this.teamFactory(data["team"]["id"]);
    if (data["bots"]) for (var i =0, nbBots = data["bots"].length; i < nbBots; i++) {
        var botObj = this.users[this.team.id +'|' +data["bots"][i]["id"]];
        if (!botObj)
            botObj = this.users[this.team.id +'|' +data["bots"][i]["id"]] = new SlackBot(this.team.id +'|', data["bots"][i]["id"]);
        botObj.update(data["bots"][i], t);
    }
    ChatContext.prototype.updateStatic.call(this, data, t, this.team.id +'|');
    if (data["ims"]) for (var i =0, nbIms = data["ims"].length; i < nbIms; i++) {
        var user = this.users[this.team.id +'|' +data["ims"][i]["user"]];
        if (user) {
            if (!this.channels[this.team.id +'|' +data["ims"][i]["id"]])
                this.channels[this.team.id +'|' +data["ims"][i]["id"]] = new SlackIms(this.team.id +'|', data["ims"][i]["id"], user);
            this.channels[this.team.id +'|' +data["ims"][i]["id"]].update(data["ims"][i], this, t, this.team.id +'|');
        }
    }
    if (data["groups"])
        this.updateGroups(data["groups"], t);
    if (data["mpims"])
        this.updateGroups(data["mpims"], t);
};

SlackData.prototype.updateGroups = function(groupsData, t) {
    for (var i =0, nbGroups = groupsData.length; i < nbGroups; i++) {
        var groupData = groupsData[i]
            ,groupObj = this.channels[this.team.id +'|' +groupData["id"]];

        if (!groupObj)
            groupObj = this.channels[this.team.id +'|' +groupData["id"]] = new SlackChan(this.team.id +'|', groupData["id"], true);
        groupObj.update(groupData, this, t, this.team.id +'|');
        groupObj.archived |= groupData["is_open"] === false;
        groupObj.isMember = true;
        if (groupData["is_mpim"])
            groupObj.setNameFromMembers();
    }
};

SlackData.prototype.teamFactory = function(id) {
    return new SlackTeam('SLACK|' +id);
};

SlackData.prototype.userFactory = function(userData) {
    return new SlackUser(this.team.id +'|', userData["id"]);
};

SlackData.prototype.roomFactory = function(roomData) {
    return new SlackChan(this.team.id +'|', roomData["id"], false);
};

SlackData.prototype.commandFactory = function(data) {
    var getServiceName = (function() {
        if (data["service_name"])
            return data["service_name"];
        else if (data["app"]) {
            for (var i in this.users)
                if (this.users[i].appId === data["app"] && this.users[i].name)
                    return this.users[i].name;
            console.error("Unknown app " +data["app"]);
            return "";
        }
        return "Slack";
    }).bind(this);
    data["category"] = data["category"] || getServiceName();
    return new Command(data);
};

/**
 * @param {*} msg
 * @param {number} t
**/
SlackData.prototype.onMessage = function(msg, t) {
    if (msg["type"] === "presence_change") {
        var member = this.users[this.team.id +'|' +msg["user"]];
        if (member)
            member.setPresence(msg["presence"], t);
        this.staticV = Math.max(this.staticV, t);
    } else if (msg["type"] === "user_typing") {
        var chanId = this.team.id +'|' +msg["channel"];
        if (!this.typing[chanId])
            this.typing[chanId] = {};
        this.typing[chanId][this.team.id +'|' +msg["user"]] = t +SLACK_TYPING_DELAY;
        this.staticV = Math.max(this.staticV, t);
    } else if (msg["type"] === "im_marked" || msg["type"] === "channel_marked" || msg["type"] === "group_marked") {
        var channel = this.channels[this.team.id +'|' +msg["channel"]];
        if (channel) {
            channel.lastRead = parseFloat(msg["ts"]) * 1000;
            this.staticV = channel.version = Math.max(channel.version, t);
        }
    }
};

/**
 * @param {number} knownVersion
 * @param {number} now time at update check
 * @return {Object|undefined}
**/
SlackData.prototype.getUpdates = function(knownVersion, now) {
    if (this.staticV > knownVersion)
        return this.toStatic(knownVersion, now);
    return undefined;
};

/** @suppress {undefinedVars,checkTypes} */
(function() {
    if (typeof module !== "undefined") {
        module.exports.SlackData = SlackData;
    }
})();

