
/**
 * @return {Element!}
**/
function createTypingDisplay() {
    var dom = document.createElement("span")
        ,dot1 = document.createElement("span")
        ,dot2 = document.createElement("span")
        ,dot3 = document.createElement("span");

    dom.className = R.klass.typing.container;
    dot1.className = R.klass.typing.dot1;
    dot2.className = R.klass.typing.dot2;
    dot3.className = R.klass.typing.dot3;

    dot1.textContent = dot2.textContent = dot3.textContent = '.';

    dom.appendChild(dot1);
    dom.appendChild(dot2);
    dom.appendChild(dot3);
    return dom;
}

/**
 * @param {Room} chan
 * @param {string|undefined} alternateChanName
 * @return {Element}
**/
function createChanListItem(chan, alternateChanName) {
    var dom = document.createElement("li")
        ,link = document.createElement("a");

    dom.id = "room_" +chan.id;
    link.href = '#' +chan.id;
    if (chan.isPrivate) {
        dom.className = R.klass.chatList.entry + " " +R.klass.chatList.typePrivate;
        dom.dataset["count"] = Object.keys((chan.users) || {}).length;
    } else {
        dom.className = R.klass.chatList.entry + " " +R.klass.chatList.typeChannel;
    }
    if (SELECTED_ROOM === chan)
        dom.classList.add(R.klass.selected);
    link.textContent = alternateChanName || chan.name;
    dom.appendChild(createTypingDisplay());
    dom.appendChild(link);
    if (chan.lastMsg !== chan.lastRead && chan.lastMsg !== undefined) {
        dom.classList.add(R.klass.unread);
        if (HIGHLIGHTED_CHANS.indexOf(chan) >= 0)
            dom.classList.add(R.klass.unreadHi);
    }
    return dom;
}

/** @type {function(string):Element} */
var createChanListHeader = (function() {
    var cache = {};

    return function(title) {
        var dom = cache[title];
        if (!dom) {
            dom = cache[title] = document.createElement("header");
            dom.textContent = title;
        }
        return dom;
    };
})();

/**
 * @param {PrivateMessageRoom} ims
 * @param {string|undefined} alternateChanName
 * @return {Element}
**/
function createImsListItem(ims, alternateChanName) {
    var dom = document.createElement("li")
        ,link = document.createElement("a");

    dom.id = "room_" +ims.id;
    link.href = '#' +ims.id;
    dom.className = R.klass.chatList.entry + " " +R.klass.chatList.typeDirect +" " +R.klass.presenceIndicator;
    link.textContent = alternateChanName || ims.user.getName();
    dom.appendChild(createTypingDisplay());
    dom.appendChild(link);

    if (!ims.user.presence)
        dom.classList.add(R.klass.presenceAway);
    if (SELECTED_ROOM === ims)
        dom.classList.add(R.klass.selected);

    if (ims.lastMsg !== ims.lastRead && ims.lastMsg !== undefined) {
        dom.classList.add(R.klass.unread);
        dom.classList.add(R.klass.unreadHi); // Always HI on private message
    }
    return dom;
}

/**
 * Try to resolve emoji from customized context
 * @param {string} emoji
 * @return {Element|string}
**/
function tryGetCustomEmoji(emoji) {
    var loop = {},
        emojiName = emoji;

    if (SELECTED_CONTEXT) {
        var ctx = SELECTED_CONTEXT.getChatContext();

        while (!loop[emoji]) {
            var emojisrc= ctx.emojis.data[emoji];
            if (emojisrc) {
                if (emojisrc.substr(0, 6) == "alias:") {
                    loop[emoji] = true;
                    emoji = emojisrc.substr(6);
                } else {
                    var dom = document.createElement("span");
                    dom.className = R.klass.emoji.custom +' ' +R.klass.emoji.emoji;
                    dom.style.backgroundImage = "url('" +emojisrc +"')";
                    dom.textContent = ':' +emojiName +':';
                    dom.title = emojiName;
                    return dom;
                }
            } else {
                return emoji; // Emoji not found, fallback to std emoji
            }
        }
    }
    return emojiName; //loop detected, return first emoji
}

function makeEmojiDom(emojiCode) {
    if ("makeEmoji" in window) {
        var emoji = tryGetCustomEmoji(emojiCode);

        if (typeof emoji === "string")
            emoji = window["makeEmoji"](emoji);
        return typeof emoji === "string" ? null : emoji;
    }
    return null;
}

/**
 * @param {string} chanId
 * @param {string} msgId
 * @param {string} reaction
 * @param {Array.<string>} users
 * @return {Element|null}
**/
function createReactionDom(chanId, msgId, reaction, users) {
    var emojiDom = makeEmojiDom(reaction);
    if (emojiDom) {
        var dom = document.createElement("li")
            ,a = document.createElement("a")
            ,emojiContainer = document.createElement("span")
            ,userList = document.createElement("span")
            ,userNames = [];

        for (var i =0, nbUser = users.length; i < nbUser; i++) {
            var user = DATA.context.getUser(users[i]);
            if (user)
                userNames.push(user.getName());
        }
        userNames.sort();
        userList.textContent = userNames.join(", ");
        emojiContainer.appendChild(emojiDom);
        emojiContainer.className = R.klass.emoji.small;
        a.href = "javascript:toggleReaction('" +chanId +"', '" +msgId +"', '" +reaction +"')";
        a.appendChild(emojiContainer);
        a.appendChild(userList);
        dom.className = R.klass.msg.reactions.item;
        dom.appendChild(a);
        return dom;
    } else {
        console.warn("Reaction id not found: " +reaction);
    }
    return null;
}

/**
 * @param {Element} hover
 * @param {UiMessage|UiMeMessage|UiNoticeMessage} msg
**/
function addHoverButtons(hover, msg) {
    var capacities = msg.context.getChatContext().capacities,
        isMe = DATA.context.isMe(msg.userId);

    if (capacities["replyToMsg"]) {
        var hoverReply = document.createElement("li");
        hoverReply.className = R.klass.msg.hover.reply;
        hoverReply.style.backgroundImage = 'url("repl.svg")';
        hover.appendChild(hoverReply);
    }

    if (capacities["reactMsg"]) {
        var hoverReaction = document.createElement("li");
        hoverReaction.className = R.klass.msg.hover.reaction;
        hoverReaction.style.backgroundImage = 'url("smile.svg")';
        hover.appendChild(hoverReaction);
    }

    if (isMe && capacities["editMsg"] || capacities["editOtherMsg"]) {
        var hoverEdit = document.createElement("li");
        hoverEdit.className = R.klass.msg.hover.edit;
        hoverEdit.style.backgroundImage = 'url("edit.svg")';
        hover.appendChild(hoverEdit);
    }

    if (capacities["starMsg"]) {
        hover.hoverStar = document.createElement("li")
        hover.hoverStar.className = R.klass.msg.hover.star;
        hover.appendChild(hover.hoverStar);
    }

    if (capacities["pinMsg"]) {
        var hoverPin = document.createElement("li")
        hoverPin.className = R.klass.msg.hover.pin;
        hover.appendChild(hoverPin);
        hoverPin.style.backgroundImage = 'url("pin.svg")';
    }

    if (isMe && capacities["removeMsg"] || capacities["moderate"]) {
        var hoverRemove = document.createElement("li")
        hoverRemove.className = R.klass.msg.hover.remove;
        hoverRemove.style.backgroundImage = 'url("remove.svg")';
        hover.appendChild(hoverRemove);
    }

}

/**
 * @param {UiMessage|UiMeMessage|UiNoticeMessage|null} msg
 * @return {Element}
**/
function doCreateMessageDom(msg) {
    var dom = document.createElement("div")
        ,msgBlock = document.createElement("div")
        ,hoverReaction = document.createElement("li");
    dom.hover = document.createElement("ul");

    dom.attachments = document.createElement("ul");
    dom.reactions = document.createElement("ul");
    dom.ts = document.createElement("div");
    dom.textDom = document.createElement("div");
    dom.authorName = document.createElement("span");

    dom.className = R.klass.msg.item;
    dom.ts.className = R.klass.msg.ts;
    dom.textDom.className = R.klass.msg.msg;
    dom.authorName.className = R.klass.msg.authorname;

    if (msg) {
        dom.id = msg.channelId +"_" +msg.id;
        addHoverButtons(dom.hover, msg);
        dom.hover.className = R.klass.msg.hover.container;
    }

    msgBlock.appendChild(dom.authorName);
    msgBlock.appendChild(dom.textDom);
    msgBlock.appendChild(dom.ts);
    msgBlock.appendChild(dom.attachments);

    dom.edited = document.createElement("div");
    dom.edited.className = R.klass.msg.edited;
    msgBlock.appendChild(dom.edited);

    msgBlock.appendChild(dom.reactions);
    msgBlock.className = R.klass.msg.content;
    dom.attachments.className = R.klass.msg.attachment.list;
    dom.reactions.className = R.klass.msg.reactions.container;
    dom.appendChild(msgBlock);
    dom.appendChild(dom.hover);
    return dom;
}

function makeUserColor(username) {
    if (!username.length) {
        return "black";
    }

    var hue = 0,
        saturation = 0,
        charCodes = [],
        sumCodes = 0,
        deviation = 0,
        maxDev = 0,
        avgCodes;

    for (var i =0, nbChars = username.length; i < nbChars; i++) {
        charCodes[i] = username.charCodeAt(i);
        sumCodes += charCodes[i];
    }
    avgCodes = sumCodes / username.length;
    charCodes.forEach(function(i) {
        var dev = Math.abs(avgCodes - i);
        deviation += dev;
        maxDev = Math.max(dev, maxDev);

        hue = i + ((hue << 5) - hue);
    });
    deviation /= username.length;

    hue = 360 * (Math.abs(hue - 60) % 199) / 199;
    saturation = maxDev === 0 ? 100 : Math.round(Math.min(100, Math.max(75, (deviation / maxDev) * 25 + 75)));
    return "hsl(" +hue +", 100%, " +saturation +"%)";
}

/**
 * @param {Chatter} user
 * @param {string} userName
 * @return {Element}
**/
function createMessageGroupDom(user, userName) {
    var dom = document.createElement("div")
        ,authorBlock = document.createElement("div")
        ,authorName = document.createElement("a")
        ,authorImg = document.createElement("img");

    dom.authorImgWrapper = document.createElement("span")
    dom.authorImgWrapper.className = R.klass.msg.authorAvatarWrapper;
    authorImg.className = R.klass.msg.authorAvatar;
    authorName.className = R.klass.msg.authorname;
    authorName.href = "#" +user.id;
    if (user) {
        authorName.textContent = user.getName();
        authorName.style.backgroundColor = makeUserColor(user.getName());
        authorImg.src = user.getSmallIcon();
    } else {
        authorName.textContent = userName || "?";
        authorImg.src = "";
    }
    dom.authorImgWrapper.appendChild(authorImg);
    authorBlock.appendChild(dom.authorImgWrapper);
    authorBlock.appendChild(authorName);
    authorBlock.className = R.klass.msg.author;
    dom.className = R.klass.msg.authorGroup;
    dom.appendChild(authorBlock);
    dom.content = document.createElement("div");
    dom.content.className = R.klass.msg.authorMessages;
    dom.appendChild(dom.content);
    return dom;
}

/**
 * @param {string=} colorText
 * @return {string}
**/
function getColor(colorText) {
    /** @const @type {Object.<string, string>} */
    var colorMap = {
        "good": "#2fa44f"
        ,"warning": "#de9e31"
        ,"danger": "#d50200"
    };
    if (colorText) {
        if (colorText[0] === '#')
            return colorText;
        else if (colorMap[colorText])
            return colorMap[colorText];
    }
    return "#e3e4e6";
}

/**
 * @param {string} channelId
 * @param {UiNoticeMessage|UiMessage} msg
 * @param {*} attachment
 * @param {number} attachmentIndex
 * @return {Element|null}
**/
function createAttachmentDom(channelId, msg, attachment, attachmentIndex) {
    var rootDom = document.createElement("li")
        ,attachmentBlock = document.createElement("div")

        ,pretext = document.createElement("div")

        ,titleBlock = document.createElement("a")

        ,authorBlock = document.createElement("div")
        ,authorImg = document.createElement("img")
        ,authorName = document.createElement("a")

        ,textBlock = document.createElement("div")
        ,textDom = document.createElement("div")
        ,thumbImgDom = document.createElement("div")
        ,imgDom = document.createElement("img")

        ,footerBlock = document.createElement("div")
    ;

    rootDom.className = R.klass.msg.attachment.container;
    //Color
    attachmentBlock.style.borderColor = getColor(attachment["color"] || "");
    attachmentBlock.className = R.klass.msg.attachment.block;

    //Pretext
    pretext.className = R.klass.msg.attachment.pretext;
    if (attachment["pretext"]) {
        pretext.innerHTML = msg.formatText(attachment["pretext"]);
    } else {
        pretext.classList.add(R.klass.hidden);
    }

    //Title
    titleBlock.target = "_blank";
    if (attachment["title"]) {
        titleBlock.innerHTML = msg.formatText(attachment["title"]);
        if (attachment["title_link"]) {
            titleBlock.href = attachment["title_link"];
        }
        titleBlock.className = R.klass.msg.attachment.title;

    } else {
        titleBlock.className = R.klass.hidden + " " +R.klass.msg.attachment.title;
    }

    //Author
    authorName.target = "_blank";
    authorBlock.className = R.klass.msg.author;
    if (attachment["author_name"]) {
        authorName.innerHTML = msg.formatText(attachment["author_name"]);
        authorName.href = attachment["author_link"] || "";
        authorName.className = R.klass.msg.authorname;
        authorImg.className = R.klass.msg.authorAvatar;
        if (attachment["author_icon"]) {
            authorImg.src = attachment["author_icon"];
            authorBlock.appendChild(authorImg);
        }
        authorBlock.appendChild(authorName);
    }

    // Img (small one)
    thumbImgDom.className = R.klass.msg.attachment.thumbImg;
    if (attachment["thumb_url"]) {
        var img = document.createElement("img");
        img.src = attachment["thumb_url"];
        thumbImgDom.appendChild(img);
        attachmentBlock.classList.add(R.klass.msg.attachment.hasThumb);

        if (attachment["video_html"])
            thumbImgDom.dataset["video"] = attachment["video_html"];
    } else {
        thumbImgDom.classList.add(R.klass.hidden);
    }

    //Text
    textBlock.className = R.klass.msg.attachment.content;
    var textContent = msg.formatText(attachment["text"] || "");
    textDom.className = R.klass.msg.attachment.text;
    if (textContent && textContent != "") {
        textDom.innerHTML = textContent;
    } else {
        textDom.classList.add(R.klass.hidden);
    }

    textBlock.appendChild(thumbImgDom);
    textBlock.appendChild(textDom);

    if (attachment["geo"]) {
        var geoTileDom = makeOSMTiles(attachment["geo"]);
        if (geoTileDom)
            textBlock.appendChild(geoTileDom);
    }

    //Img (the big one)
    imgDom.className = R.klass.msg.attachment.img;
    if (attachment["image_url"])
        imgDom.src = attachment["image_url"];
    else
        imgDom.classList.add(R.klass.hidden);

    //Footer
    footerBlock.className = R.klass.msg.attachment.footer;
    if (attachment["footer"]) {
        var footerText = document.createElement("span")
        footerText.className = R.klass.msg.attachment.footerText;
        footerText.innerHTML = msg.formatText(attachment["footer"]);
        if (attachment["footer_icon"]) {
            var footerIcon = document.createElement("img")
            footerIcon.src = attachment["footer_icon"];
            footerIcon.className = R.klass.msg.attachment.footerIcon;
            footerBlock.appendChild(footerIcon);
        }
        footerBlock.appendChild(footerText);
    }

    //Ts
    if (attachment["ts"]) {
        var footerTs = document.createElement("span")
        footerTs.className = R.klass.msg.ts;
        footerTs.innerHTML = locale.formatDate(attachment["ts"]);
        footerBlock.appendChild(footerTs);
    }

    attachmentBlock.appendChild(titleBlock);
    attachmentBlock.appendChild(authorBlock);
    attachmentBlock.appendChild(textBlock);
    attachmentBlock.appendChild(imgDom);

    // Fields
    if (attachment["fields"] && attachment["fields"].length) {
        var fieldsContainer = document.createElement("ul");

        attachmentBlock.appendChild(fieldsContainer);
        fieldsContainer.className = R.klass.msg.attachment.field.container;

        attachment["fields"].forEach(function(fieldData) {
            var fieldDom = createFieldDom(msg, fieldData["title"] || "", fieldData["value"] || "", !!fieldData["short"]);
            if (fieldDom) {
                fieldsContainer.appendChild(fieldDom);
            }
        });
    }

    // Buttons
    if (attachment["actions"] && attachment["actions"].length) {
        var buttons;

        buttons = document.createElement("ul");
        buttons.className = R.klass.msg.attachment.actions +' ' +R.klass.buttonContainer;
        attachmentBlock.appendChild(buttons);

        for (var i =0, nbAttachments = attachment["actions"].length; i < nbAttachments; i++) {
            var action = attachment["actions"][i];

            if (action) {
                var button = createActionButtonDom(attachmentIndex, i, action);

                if (button) {
                    buttons.appendChild(button);
                }
            }
        }
    }

    attachmentBlock.appendChild(footerBlock);
    rootDom.appendChild(pretext);
    rootDom.appendChild(attachmentBlock);
    return rootDom;
}

/**
 * @param {UiMessage|UiNoticeMessage} msg
 * @param {string} title
 * @param {string} text
 * @param {boolean} isShort
 * @return {Element}
**/
function createFieldDom(msg, title, text, isShort) {
    var fieldDom = document.createElement("li")
        ,titleDom = document.createElement("div")
        ,textDom = document.createElement("div");

    fieldDom.className = R.klass.msg.attachment.field.item;
    if (!isShort) {
        fieldDom.classList.add(R.klass.msg.attachment.field.longField);
    }
    titleDom.className = R.klass.msg.attachment.field.title;
    titleDom.textContent = title;
    textDom.className = R.klass.msg.attachment.field.text;
    textDom.innerHTML = msg.formatText(text);
    fieldDom.appendChild(titleDom);
    fieldDom.appendChild(textDom);
    return fieldDom;
}

/**
 * @param {number} attachmentIndex
 * @param {number} actionIndex
 * @param {*} action
 * @return {Element}
**/
function createActionButtonDom(attachmentIndex, actionIndex, action) {
    var li = document.createElement("li")
        ,color = getColor(action["style"]);

    li.textContent = action["text"];
    if (color !== getColor())
        li.style.color = color;
    li.style.borderColor = color;
    li.dataset["attachmentIndex"] = attachmentIndex;
    li.dataset["actionIndex"] = actionIndex;
    li.className = R.klass.msg.attachment.actionItem +' ' +R.klass.button;
    return li;
}

/**
 * @param {Chatter} user
 * @return {Element}
**/
function makeUserIsTypingDom(user) {
    var dom = document.createElement("li")
        ,userName = document.createElement("span");
    userName.textContent = user.getName();
    dom.appendChild(createTypingDisplay());
    dom.appendChild(userName);
    return dom;
}

