--
-- Created by IntelliJ IDEA.
-- User: RJ
-- Date: 03/02/14
-- Time: 09:28
-- To change this template use File | Settings | File Templates.
--

require "ISUI/ISCollapsableWindow"
require "ISUI/ISRichTextPanel"
require "ISUI/ISButton"
require "ISUI/ISTabPanel"

ISChat = ISCollapsableWindow:derive("ISChat");
ISChat.maxLine = 50;
ISChat.focused = false;
ISChat.allChatStreams = {}
ISChat.allChatStreams[1] = {name = "say", command = "/say ", shortCommand = "/s ", tabID = 1};
ISChat.allChatStreams[2] = {name = "yell", command = "/yell ", shortCommand = "/y ", tabID = 1};
ISChat.allChatStreams[3] = {name = "whisper", command = "/whisper ", shortCommand = "/w ", tabID = 1};
ISChat.allChatStreams[4] = {name = "faction", command = "/faction ", shortCommand = "/f ", tabID = 1};
ISChat.allChatStreams[5] = {name = "safehouse", command = "/safehouse ", shortCommand = "/sh ", tabID = 1};
ISChat.allChatStreams[6] = {name = "general", command = "/all ", tabID = 1};
ISChat.allChatStreams[7] = {name = "admin", command = "/admin ", shortCommand = "/a ", tabID = 2};
ISChat.defaultTabStream = {}
ISChat.defaultTabStream[1] = ISChat.allChatStreams[1];
ISChat.defaultTabStream[2] = ISChat.allChatStreams[7];
-- default min and max opaque values
ISChat.minControlOpaque = 0.5; -- a value
ISChat.minGeneralOpaque = 0.0; -- a value, not percentage
ISChat.maxGeneralOpaque = 1.0; -- a value, not percentage
ISChat.minTextEntryOpaque = 0.3;
ISChat.maxTextEntryOpaque = 1.0;
ISChat.minTextOpaque = 0.8;
-- elements name
ISChat.textEntryName = "chat text entry"
ISChat.tabPanelName = "chat tab panel"
ISChat.yResizeWidgetName = "chat bottom y resize widget"
ISChat.xyResizeWidgetName = "chat xy resize widget"
ISChat.closeButtonName = "chat close button"
ISChat.lockButtonName = "chat lock button"
ISChat.gearButtonName = "chat gear button"
ISChat.textPanelName = "chat text element"
ISChat.windowName = "chat window"

function ISChat:initialise()
    self:setUIName(ISChat.windowName);
    ISCollapsableWindow.initialise(self);
    self.showTimestamp = getCore():isOptionShowChatTimestamp();
    self.showTitle = getCore():isOptionShowChatTitle();
    self.minOpaque = getCore():getOptionMinChatOpaque(); -- in percentage
    self.maxOpaque = getCore():getOptionMaxChatOpaque(); -- in percentage
    self.fadeTime = getCore():getOptionChatFadeTime();
    self.chatFont = getCore():getOptionChatFontSize();
    self.opaqueOnFocus = getCore():getOptionChatOpaqueOnFocus();
    self:initFade(self.fadeTime);
    self.fade:update();
    self.backgroundColor.a = self.maxOpaque * ISChat.maxGeneralOpaque;
    self.pin = true;
    self.borderColor.a = 0.0;
end

ISChat.initChat = function()
    local instance = ISChat.instance;
    if instance.tabCnt == 1 then
        instance.chatText:setVisible(false);
        instance:removeChild(instance.chatText);
        instance.chatText = nil;
    elseif instance.tabCnt > 1 then
        instance.panel:setVisible(false);
        for i = 1, instance.tabCnt do
            instance.panel:removeView(instance.tabs[i])
        end
    end
    instance.tabCnt = 0;
    instance.tabs = {}
    instance.currentTabID = 0;
end

function ISChat:createChildren()
    --window stuff
    -- Do corner x + y widget
    local rh = self:resizeWidgetHeight()
    local resizeWidget = ISResizeWidget:new(self.width-rh, self.height-rh, rh, rh, self);
    resizeWidget:initialise();
    resizeWidget.onMouseDown = ISChat.onMouseDown;
    resizeWidget.onMouseUp = ISChat.onMouseUp;
    resizeWidget:setVisible(self.resizable)
    resizeWidget:bringToTop();
    resizeWidget:setUIName(ISChat.xyResizeWidgetName);
    self:addChild(resizeWidget);
    self.resizeWidget = resizeWidget;

    -- Do bottom y widget
    local resizeWidget2 = ISResizeWidget:new(0, self.height-rh, self.width-rh, rh, self, true);
    resizeWidget2.anchorLeft = true;
    resizeWidget2.anchorRight = true;
    resizeWidget2:initialise();
    resizeWidget2.onMouseDown = ISChat.onMouseDown;
    resizeWidget2.onMouseUp = ISChat.onMouseUp;
    resizeWidget2:setVisible(self.resizable);
    resizeWidget2:setUIName(ISChat.yResizeWidgetName);
    self:addChild(resizeWidget2);
    self.resizeWidget2 = resizeWidget2;

    -- close button
    local th = self:titleBarHeight()
    self.closeButton = ISButton:new(3, 0, th, th, "", self, self.close);
    self.closeButton:initialise();
    self.closeButton.borderColor.a = 0.0;
    self.closeButton.backgroundColor.a = 0;
    self.closeButton.backgroundColorMouseOver.a = 0;
    self.closeButton:setImage(self.closeButtonTexture);
    self.closeButton:setUIName(ISChat.closeButtonName);
    self:addChild(self.closeButton);

    -- lock button
    self.lockButton = ISButton:new(self.width - 19, 0, th, th, "", self, ISChat.pin);
    self.lockButton.anchorRight = true;
    self.lockButton.anchorLeft = false;
    self.lockButton:initialise();
    self.lockButton.borderColor.a = 0.0;
    self.lockButton.backgroundColor.a = 0;
    self.lockButton.backgroundColorMouseOver.a = 0;
    if self.locked then
        self.lockButton:setImage(self.chatLockedButtonTexture);
    else
        self.lockButton:setImage(self.chatUnLockedButtonTexture);
    end
    self.lockButton:setUIName(ISChat.lockButtonName);
    self:addChild(self.lockButton);
    self.lockButton:setVisible(true);

    --gear button
    self.gearButton = ISButton:new(self.lockButton:getX() - th / 2 - th, 1, th, th, "", self, ISChat.onGearButtonClick);
    self.gearButton.anchorRight = true;
    self.gearButton.anchorLeft = false;
    self.gearButton:initialise();
    self.gearButton.borderColor.a = 0.0;
    self.gearButton.backgroundColor.a = 0;
    self.gearButton.backgroundColorMouseOver.a = 0;
    self.gearButton:setImage(getTexture("media/ui/Panel_Icon_Gear.png"));
    self.gearButton:setUIName(ISChat.gearButtonName);
    self:addChild(self.gearButton);
    self.gearButton:setVisible(true);

    --general stuff
    self.minimumHeight = 90;
    self.minimumWidth = 200;
    self:setResizable(true);
    self:setDrawFrame(true);
    self:addToUIManager();

    self.tabs = {};
    self.tabCnt = 0;
    self.btnHeight = 25;
    self.currentTabID = 0;
    self.inset = 2
    self.fontHgt = getTextManager():getFontFromEnum(UIFont.Medium):getLineHeight();

    --text entry stuff
    local inset, EdgeSize, fontHgt = self.inset, 5, self.fontHgt;

    -- EdgeSize must match UITextBox2.EdgeSize
    local height = EdgeSize * 2 + fontHgt
    self.textEntry = ISTextEntryBox:new("", inset, self:getHeight() - 8 - inset - height, self:getWidth() - inset * 2, height);
    self.textEntry.font = UIFont.Medium
    self.textEntry:initialise();
    self.textEntry:instantiate();
    self.textEntry.backgroundColor = {r=0, g=0, b=0, a=0.5};
    self.textEntry.borderColor = {r=1, g=1, b=1, a=0.0};
    self.textEntry:setHasFrame(true)
    self.textEntry:setAnchorTop(false);
    self.textEntry:setAnchorBottom(true);
    self.textEntry:setAnchorRight(true);
    self.textEntry.onCommandEntered = ISChat.onCommandEntered;
    self.textEntry.onTextChange = ISChat.onTextChange;
    self.textEntry.onPressDown = ISChat.onPressDown;
    self.textEntry.onPressUp = ISChat.onPressUp;
    self.textEntry.onOtherKey = ISChat.onOtherKey
    self.textEntry.onClick = ISChat.onMouseDown;
    self.textEntry:setUIName(ISChat.textEntryName); -- need to be right this. If it will empty or another then focus will lost on click in chat
    self.textEntry:setHasFrame(true);
    self:addChild(self.textEntry)
    ISChat.maxTextEntryOpaque = self.textEntry:getFrameAlpha();

    --tab panel stuff
    local panelHeight = self.textEntry:getY() - self:titleBarHeight() - self.inset;
    self.panel = ISTabPanel:new(0, self:titleBarHeight(), self.width - inset, panelHeight);
    self.panel:initialise();
    self.panel.borderColor = { r = 0, g = 0, b = 0, a = 0};
    self.panel.onActivateView = ISChat.onActivateView;
    self.panel.target = self;
    self.panel:setAnchorTop(true);
    self.panel:setAnchorLeft(true);
    self.panel:setAnchorRight(true);
    self.panel:setAnchorBottom(true);
    self.panel:setEqualTabWidth(false);
    self.panel:setVisible(false);
    self.panel.onRightMouseUp = ISChat.onRightMouseUp;
    self.panel.onRightMouseDown = ISChat.onRightMouseDown;
    self.panel.onMouseUp = ISChat.onMouseUp;
    self.panel.onMouseDown = ISChat.ISTabPanelOnMouseDown;
    self.panel:setUIName(ISChat.tabPanelName);
    self:addChild(self.panel);

    self:bringToTop();
    self.textEntry:bringToTop();
    self.minimumWidth = self.panel:getWidthOfAllTabs() + 2 * inset;
    self.minimumHeight = self.textEntry:getHeight() + self:titleBarHeight() + 2 * inset + self.panel.tabHeight + fontHgt * 4;
    self:unfocus();

    self.mutedUsers = {}
end

function ISChat:setDrawFrame(visible)
    self.background = visible
    self.drawFrame = visible
    if self.closeButton then
        self.closeButton:setVisible(visible)
    end
end

function ISChat:collapse()
    self.pin = false;
    self.lockButton:setVisible(true);
end

function ISChat:close()
    if not self.locked then
        ISCollapsableWindow.close(self);
    end
end

function ISChat:pin()
    self.locked = not self.locked;
    ISChat.focused = not self.locked;
    if self.locked then
        self.lockButton:setImage(self.chatLockedButtonTexture);
    else
        self.lockButton:setImage(self.chatUnLockedButtonTexture);
    end
end

function ISChat:onGearButtonClick()
    local context = ISContextMenu.get(0, self:getAbsoluteX() + self:getWidth() / 2, self:getAbsoluteY() + self.gearButton:getY())

    local timestampOptionName = getText("UI_chat_context_enable_timestamp");
    if self.showTimestamp then
        timestampOptionName = getText("UI_chat_context_disable_timestamp");
    end
    context:addOption(timestampOptionName, ISChat.instance, ISChat.onToggleTimestampPrefix)

    local tagOptionName = getText("UI_chat_context_enable_tags");
    if self.showTitle then
        tagOptionName = getText("UI_chat_context_disable_tags");
    end
    context:addOption(tagOptionName, ISChat.instance, ISChat.onToggleTagPrefix)

    local fontSizeOption = context:addOption(getText("UI_chat_context_font_submenu_name"), ISChat.instance);
    local fontSubMenu = context:getNew(context);
    context:addSubMenu(fontSizeOption, fontSubMenu);
    fontSubMenu:addOption(getText("UI_chat_context_font_small"), ISChat.instance, ISChat.onFontSizeChange, "small");
    fontSubMenu:addOption(getText("UI_chat_context_font_medium"), ISChat.instance, ISChat.onFontSizeChange, "medium");
    fontSubMenu:addOption(getText("UI_chat_context_font_large"), ISChat.instance, ISChat.onFontSizeChange, "large");
    if self.chatFont == "small" then
        fontSubMenu:setOptionChecked(fontSubMenu.options[1], true)
    elseif self.chatFont == "medium" then
        fontSubMenu:setOptionChecked(fontSubMenu.options[2], true)
    elseif self.chatFont == "large" then
        fontSubMenu:setOptionChecked(fontSubMenu.options[3], true)
    end

    local minOpaqueOption = context:addOption(getText("UI_chat_context_opaque_min"), ISChat.instance);
    local minOpaqueSubMenu = context:getNew(context);
    context:addSubMenu(minOpaqueOption, minOpaqueSubMenu);
    local opaques = {0, 0.25, 0.5, 0.75, 1};
    for i = 1, #opaques do
        if logTo01(opaques[i]) <= self.maxOpaque then
            local option = minOpaqueSubMenu:addOption((opaques[i] * 100) .. "%", ISChat.instance, ISChat.onMinOpaqueChange, opaques[i])
            local current = math.floor(self.minOpaque * 1000)
            local value = math.floor(logTo01(opaques[i]) * 1000)
            if current == value then
                minOpaqueSubMenu:setOptionChecked(option, true)
            end
        end
    end

    local maxOpaqueOption = context:addOption(getText("UI_chat_context_opaque_max"), ISChat.instance);
    local maxOpaqueSubMenu = context:getNew(context);
    context:addSubMenu(maxOpaqueOption, maxOpaqueSubMenu);
    for i = 1, #opaques do
        if logTo01(opaques[i]) >= self.minOpaque then
            local option = maxOpaqueSubMenu:addOption((opaques[i] * 100) .. "%", ISChat.instance, ISChat.onMaxOpaqueChange, opaques[i])
            local current = math.floor(self.maxOpaque * 1000)
            local value = math.floor(logTo01(opaques[i]) * 1000)
            if current == value then
                maxOpaqueSubMenu:setOptionChecked(option, true)
            end
        end
    end

    local fadeTimeOption = context:addOption(getText("UI_chat_context_opaque_fade_time_submenu_name"), ISChat.instance);
    local fadeTimeSubMenu = context:getNew(context);
    context:addSubMenu(fadeTimeOption, fadeTimeSubMenu);
    local availFadeTime = {0, 1, 2, 3, 5, 10};
    local option = fadeTimeSubMenu:addOption(getText("UI_chat_context_disable"), ISChat.instance, ISChat.onFadeTimeChange, 0)
    if 0 == self.fadeTime then
        fadeTimeSubMenu:setOptionChecked(option, true)
    end
    for i = 2, #availFadeTime  do
        local time = availFadeTime[i];
        option = fadeTimeSubMenu:addOption(time .. " s", ISChat.instance, ISChat.onFadeTimeChange, time)
        if time == self.fadeTime then
            fadeTimeSubMenu:setOptionChecked(option, true)
        end
    end

    local opaqueOnFocusOption = context:addOption(getText("UI_chat_context_opaque_on_focus"), ISChat.instance);
    local opaqueOnFocusSubMenu = context:getNew(context);
    context:addSubMenu(opaqueOnFocusOption, opaqueOnFocusSubMenu);
    opaqueOnFocusSubMenu:addOption(getText("UI_chat_context_disable"), ISChat.instance, ISChat.onFocusOpaqueChange, false)
    opaqueOnFocusSubMenu:addOption(getText("UI_chat_context_enable"), ISChat.instance, ISChat.onFocusOpaqueChange, true)
    opaqueOnFocusSubMenu:setOptionChecked(opaqueOnFocusSubMenu.options[self.opaqueOnFocus and 2 or 1], true)
end

function ISChat:createTab()
    local chatY = self:titleBarHeight() + self.btnHeight + 2 * self.inset;
    local chatHeight = self.textEntry:getY() - chatY;
    local chatText = ISRichTextPanel:new(0, chatY, self:getWidth(), chatHeight);
    chatText.maxLines = 500
    chatText:initialise();
    chatText.background = false;
    chatText:setAnchorBottom(true);
    chatText:setAnchorRight(true);
    chatText:setAnchorTop(true);
    chatText:setAnchorLeft(true);
    chatText.log = {};
    chatText.logIndex = 0;
    chatText.marginTop = 2
    chatText.marginBottom = 0
    chatText.onRightMouseUp = nil;
    chatText.render = ISChat.render_chatText
    chatText.autosetheight = false;
    chatText:addScrollBars();
    chatText.vscroll:setVisible(false);
    chatText.vscroll.background = false;
    chatText:ignoreHeightChange()
    chatText:setVisible(false);
    chatText.chatTextLines = {};
    chatText.chatMessages = {};
    chatText.onRightMouseUp = ISChat.onRightMouseUp;
    chatText.onRightMouseDown = ISChat.onRightMouseDown;
    chatText.onMouseUp = ISChat.onMouseUp;
    chatText.onMouseDown = ISChat.onMouseDown;
    return chatText;
end

function ISChat:initFade(durationInS)
    self.fade:init(durationInS * 1000, true);
end

function ISChat:calcAlpha(defaultMin, defaultMax, fraction)
    -- default min/max value is not percentage! It's just [0,1] alpha value.
    -- The self.maxOpaque is current percentage of min/max alpha. self.maxOpaque assigned by player in context chat menu
    local newMinAlpha = defaultMin + self.minOpaque * (defaultMax - defaultMin);
    local newMaxAlpha = defaultMin + self.maxOpaque * (defaultMax - defaultMin); -- same as previous
    return newMinAlpha + fraction * (newMaxAlpha - newMinAlpha);
end

function ISChat:makeFade(fraction)
    local min, max = ISChat.minGeneralOpaque, ISChat.maxGeneralOpaque;
    local alpha = self:calcAlpha(min, max, fraction);
    self.backgroundColor.a = alpha;

    min = ISChat.minTextOpaque;
    alpha = self:calcAlpha(min, max, fraction);
    self.chatText:setContentTransparency(alpha)
    if self.tabCnt > 1 then
        self.panel:setTextTransparency(alpha);
    end

    min = ISChat.minControlOpaque;
    alpha = self:calcAlpha(min, max, fraction);
    self.widgetTextureColor.a = alpha;
    self.closeButton.textureColor.a = alpha;
    self.lockButton.textureColor.a = alpha;
    self.gearButton.textureColor.a = alpha;
    if self.tabCnt > 1 then
        self.panel:setTabsTransparency(alpha);
    end

    max = ISChat.maxTextEntryOpaque;
    min = ISChat.minTextEntryOpaque;
    alpha = self:calcAlpha(min, max, fraction);
    self.textEntry:setFrameAlpha(alpha);
    self.textEntry.backgroundColor.a = alpha;
end

function ISChat:prerender()
    ISChat.instance = self
    self.gearButton.onclick = self.onGearButtonClick

    self:setDrawFrame(true)

    if not ISChat.focused then
        self.fade:update();
    end
    self:makeFade(self.fade:fraction());

    local a = self.backgroundColor.a;
    local r, g, b = self.backgroundColor.r, self.backgroundColor.g, self.backgroundColor.b;
    self:drawRect(0, 0, self:getWidth(), self:getHeight(), a, r, g, b);
    local th = self:titleBarHeight()
    local titlebarAlpha = self:calcAlpha(ISChat.minControlOpaque, ISChat.maxGeneralOpaque, self.fade:fraction());
    self:drawTextureScaled(self.titlebarbkg, 2, 1, self:getWidth() - 4, th - 2, titlebarAlpha, 1, 1, 1);
    if self.servermsg then
        local x = getCore():getScreenWidth() / 2 - self:getX()
        local y = getCore():getScreenHeight() / 4 - self:getY();
        self:drawTextCentre(self.servermsg, x, y, 1, 0.1, 0.1, 1, UIFont.Title);
        self.servermsgTimer = self.servermsgTimer - UIManager.getMillisSinceLastRender();
        if self.servermsgTimer < 0 then
            self.servermsg = nil;
            self.servermsgTimer = 0;
        end
    end
end

function ISChat:render()
    ISCollapsableWindow.render(self);
end

function ISChat:render_chatText()
    self:setStencilRect(0, 0, self.width, self.height)
    ISRichTextPanel.render(self)
    self:clearStencilRect()
end

function ISChat:onContextClear()
    self.chatText.chatTextLines = {}
    self.chatText.text = ""
    self.chatText:paginate()
end

function ISChat:logChatCommand(command)
    self.chatText.logIndex = 0;
    if command and command ~= "" then
        local newLog = {};
        table.insert(newLog, command);
        for i,v in ipairs(self.chatText.log) do
            table.insert(newLog, v);
            if i > 20 then
                break;
            end
        end
        self.chatText.log = newLog;
    end
end

function ISChat:onCommandEntered()
    local command = ISChat.instance.textEntry:getText();
    local chat = ISChat.instance;

    ISChat.instance:unfocus();
    if not command or command == "" then
        return;
    end

    local commandProcessed = false;
    local chatCommand;
    local chatStreamName;
    for _, stream in ipairs(ISChat.allChatStreams) do
        chatCommand = nil;
        if luautils.stringStarts(command, stream.command) then
            chatCommand = stream.command;
        elseif stream.shortCommand and luautils.stringStarts(command, stream.shortCommand) then
            chatCommand = stream.shortCommand;
        end
        if chatCommand then
            if chat.currentTabID ~= stream.tabID then
                showWrongChatTabMessage(chat.currentTabID - 1, stream.tabID - 1, chatCommand); -- from one-based to zero-based
                commandProcessed = true;
                break;
            end
            chat.chatText.lastChatCommand = chatCommand;
            local originalCommand = command;
            command = string.sub(command, #chatCommand);
            if command ~= "" and command ~= " " then
                chat:logChatCommand(originalCommand);
            end
            chatStreamName = stream.name;
            break;
        end
    end
    if not chatCommand then
        if luautils.stringStarts(command, "/") then
            SendCommandToServer(command);
            chat:logChatCommand(command);
            commandProcessed = true;
        else
            local defaultStream = ISChat.defaultTabStream[chat.currentTabID];
            chatStreamName = defaultStream.name;
            chat:logChatCommand(command);
        end
    end
    if not commandProcessed then
        if chatStreamName == "yell" then
            processShoutMessage(command);
        elseif chatStreamName == "whisper" then
            local username = proceedPM(command);
            chat.chatText.lastChatCommand = chat.chatText.lastChatCommand .. username .. " ";
        elseif chatStreamName == "faction" then
            proceedFactionMessage(command);
        elseif chatStreamName == "safehouse" then
            processSafehouseMessage(command);
        elseif chatStreamName == "admin" then
            processAdminChatMessage(command);
        elseif chatStreamName == "say" then
            processSayMessage(command);
        elseif chatStreamName == "general" then
            processGeneralMessage(command);
        end

    end
    doKeyPress(false);
    ISChat.instance.timerTextEntry = 20;
end

function ISChat:onOtherKey(key)
    if key == Keyboard.KEY_ESCAPE then
        ISChat.instance:unfocus();
    end
end

ISChat.onSwitchStream = function()
    if ISChat.focused then
        local t = ISChat.instance.textEntry;
        local internalText = t:getInternalText();
        local data = luautils.split(internalText, " ")
        local onlineUsers = getOnlinePlayers()
        for i=0, onlineUsers:size()-1 do
            local username = onlineUsers:get(i):getUsername()
            if #data > 1 and string.match(string.lower(username), string.lower(data[#data])) then
                local txt = ""
                for i = 1, #data-1 do
                    txt = txt .. data[i] .. " "
                end
                txt = txt .. username
                ISChat.instance.textEntry:setText(txt);
                return
            end
        end

        local curTxtPanel = ISChat.instance.chatText
        local chatStreams = curTxtPanel.chatStreams;
        for i = 0, #chatStreams do
            curTxtPanel.streamID = curTxtPanel.streamID % #chatStreams + 1;
            if checkPlayerCanUseChat(chatStreams[curTxtPanel.streamID].command) then
                break;
            end
        end
        ISChat.instance.textEntry:setText(chatStreams[curTxtPanel.streamID].command);
    end
end

ISChat.onTextChange = function()
    local t = ISChat.instance.textEntry;
    local internalText = t:getInternalText();
    if ISChat.instance.chatText.lastChatCommand ~= nil then
        for _, chat in ipairs(ISChat.instance.chatText.chatStreams) do
            local prefix;
            if chat.command and luautils.stringStarts(internalText, chat.command) then
                prefix = chat.command;
            elseif chat.shortCommand and luautils.stringStarts(internalText, chat.shortCommand) then
                prefix = chat.shortCommand;
            end
            if prefix then
                if string.sub(t:getText(), prefix:len() + 1, t:getText():len()):len() <= 5
                        and luautils.stringStarts(internalText, "/")
                        and luautils.stringEnds(internalText, "/") then
                    t:setText("/");
                    return;
                end
            end
        end
        if t:getText():len() <= 5 and luautils.stringEnds(internalText, "/") then
            t:setText("/");
            return;
        end
    end
end

function ISChat:focus()
    self:setVisible(true);
    ISChat.focused = true;
    self.textEntry:setEditable(true);
    self.textEntry:focus();
    self.textEntry:ignoreFirstInput();
    self.textEntry:setText(self.chatText.lastChatCommand);
    self.fade:reset();
    self.fade:update(); --reset fraction to start value
end

function ISChat:unfocus()
    self.textEntry:unfocus();
    self.textEntry:setText("");
    if ISChat.focused then
        self.fade:reset(); -- to begin fade. unfocus called when element was unfocused also.
    end
    ISChat.focused = false;
    self.textEntry:setEditable(false);
end

function ISChat:updateChatPrefixSettings()
    updateChatSettings(self.chatFont, self.showTimestamp, self.showTitle);
    for tabNumber, chatText in ipairs(self.tabs) do
        chatText.text = "";
        local newText = "";
        chatText.chatTextLines = {};
        for i,msg in ipairs(chatText.chatMessages) do
            local line = msg:getTextWithPrefix() .. " <LINE> ";
            table.insert(chatText.chatTextLines, line);
            if i == #chatText.chatMessages then
                line = string.gsub(line, " <LINE> $", "")
            end
            newText = newText .. line;
        end
        chatText.text = newText;
        chatText:paginate();
    end
end

ISChat.onToggleTimestampPrefix = function()
    local chat = ISChat.instance;
    chat.showTimestamp = not chat.showTimestamp;
    if chat.showTimestamp then
        print("timestamp enabled");
    else
        print("timestamp disabled");
    end
    chat:updateChatPrefixSettings();
end

ISChat.onToggleTagPrefix = function()
    local chat = ISChat.instance;
    chat.showTitle = not chat.showTitle;
    if chat.showTitle then
        print("tags enabled");
    else
        print("tags disabled");
    end
    chat:updateChatPrefixSettings();
end

ISChat.onFontSizeChange = function(target, value)
    if target.chatFont == value then
        return;
    end
    target.chatFont = value;
    target:updateChatPrefixSettings();
    print("Font size switched to " .. value);
end

ISChat.onMinOpaqueChange = function(target, value)
    target.minOpaque = logTo01(value);
    target.backgroundColor.a = target.maxOpaque;
    getCore():setOptionMinChatOpaque(target.minOpaque);
end

ISChat.onMaxOpaqueChange = function(target, value)
    target.maxOpaque = logTo01(value);
    target.backgroundColor.a = target.maxOpaque;
    getCore():setOptionMaxChatOpaque(target.maxOpaque);
end

ISChat.onFadeTimeChange = function(target, value)
    target.fadeTime = value;
    target:initFade(target.fadeTime);
    getCore():setOptionChatFadeTime(value);
end

ISChat.onFocusOpaqueChange = function(target, value)
    target.opaqueOnFocus = value;
    getCore():setOptionChatOpaqueOnFocus(value);
end

ISChat.addLineInChat = function(message, tabID)
    local line = message:getTextWithPrefix();
    if message:isServerAlert() then
        ISChat.instance.servermsg = "";
        if message:isShowAuthor() then
            ISChat.instance.servermsg = message:getAuthor() .. ": ";
        end
        ISChat.instance.servermsg = ISChat.instance.servermsg .. message:getText();
        ISChat.instance.servermsgTimer = 5000;
        --return;
    end
    if user and ISChat.instance.mutedUsers[user] then return end
    if not ISChat.instance.chatText then
        ISChat.instance.chatText = ISChat.instance.defaultTab;
        ISChat.instance:onActivateView();
    end
    local chatText;
    for i,tab in ipairs(ISChat.instance.tabs) do
        if tab and tab.tabID == tabID then
            chatText = tab;
            break;
        end
    end
    if chatText.tabTitle ~= ISChat.instance.chatText.tabTitle then
        local alreadyExist = false;
        for i,blinkedTab in ipairs(ISChat.instance.panel.blinkTabs) do
            if blinkedTab == chatText.tabTitle then
                alreadyExist = true;
                break;
            end
        end
        if alreadyExist == false then
            table.insert(ISChat.instance.panel.blinkTabs, chatText.tabTitle);
        end
    end
    local vscroll = chatText.vscroll
    local scrolledToBottom = (chatText:getScrollHeight() <= chatText:getHeight()) or (vscroll and vscroll.pos == 1)
    if #chatText.chatTextLines > ISChat.maxLine then
        local newLines = {};
        for i,v in ipairs(chatText.chatTextLines) do
            if i ~= 1 then
                table.insert(newLines, v);
            end
        end
        table.insert(newLines, line .. " <LINE> ");
        chatText.chatTextLines = newLines;
    else
        table.insert(chatText.chatTextLines, line .. " <LINE> ");
    end
    chatText.text = "";
    local newText = "";
    for i,v in ipairs(chatText.chatTextLines) do
        if i == #chatText.chatTextLines then
            v = string.gsub(v, " <LINE> $", "")
        end
        newText = newText .. v;
    end
    chatText.text = newText;
    table.insert(chatText.chatMessages, message);
    chatText:paginate();
    if scrolledToBottom then
        chatText:setYScroll(-10000);
    end
end

ISChat.onToggleChatBox = function(key)
    if ISChat.instance==nil then return; end
    if key == getCore():getKey("Toggle chat") or key == getCore():getKey("Alt toggle chat") then
        ISChat.instance:focus();
    end
    local chat = ISChat.instance;
    if key == getCore():getKey("Switch chat stream") then
        chat.currentTabID = chat.currentTabID % chat.tabCnt + 1;
        chat.panel:activateView(chat.tabs[chat.currentTabID].tabTitle);
        ISChat.instance:onActivateView();
    end
end

ISChat.onKeyKeepPressed = function(key)
    if ISChat.instance==nil then return; end
end

function ISChat:calcTabSize()
    if ISChat.instance.tabCnt == 0 then
        return {width = self.width, height = self.textEntry:getY() - self.panel:getY()};
    elseif ISChat.instance.tabCnt > 0 then
        return {width = self.panel.width, height = self.panel.height - self.panel.tabHeight - 1};
    end
end

function ISChat:calcTabPos()
    if ISChat.instance.tabCnt == 0 then
        return {x = 0, y = self:titleBarHeight() + self.inset};
    else
        return {x = 0, y = self:titleBarHeight() + self.btnHeight + 2 * self.inset};
    end
end

ISChat.onTabAdded = function(tabTitle, tabID)
    local chat = ISChat.instance;
    local newTab = chat:createTab();
    newTab.parent = chat;
    newTab.tabTitle = tabTitle;
    newTab.tabID = tabID;
    newTab.streamID = 1;
    newTab.chatStreams = {}
    for _, stream in ipairs(ISChat.allChatStreams) do
        if stream.tabID == tabID + 1 then --tabID is zero-based index but stream.tabID is one-based index.
            table.insert(newTab.chatStreams, stream)
        end
    end
    newTab.lastChatCommand = newTab.chatStreams[newTab.streamID].command;
    newTab:setUIName("chat text panel with title '" .. tabTitle .. "'");
    local pos = chat:calcTabPos();
    local size = chat:calcTabSize();
    newTab:setY(pos.y);
    newTab:setHeight(size.height);
    newTab:setWidth(size.width);
    if chat.tabCnt == 0 then
        chat:addChild(newTab);
        chat.chatText = newTab;
        chat.chatText:setVisible(true);
    end
    if chat.tabCnt == 1 then
        chat.panel:setVisible(true);
        chat.chatText:setY(pos.y);
        chat.chatText:setHeight(size.height);
        chat.chatText:setWidth(size.width);
        chat:removeChild(chat.chatText);
        chat.panel:addView(chat.chatText.tabTitle, chat.chatText);
    end
    if chat.tabCnt >= 1 then
        chat.panel:addView(tabTitle, newTab);
        chat.minimumWidth = chat.panel:getWidthOfAllTabs() + 2 * chat.inset;
    end
    table.insert(chat.tabs, newTab);
    chat.tabCnt = chat.tabCnt + 1;
end

ISChat.onTabRemoved = function(tabTitle, tabID)
    local foundedTab;
    for i,tab in ipairs(ISChat.instance.tabs) do
        if tabID == tab.tabID then
            foundedTab = tab;
            table.remove(ISChat.instance.tabs, i);
            break;
        end
    end
    if ISChat.instance.tabCnt > 1 then
        for i,blinkedTab in ipairs(ISChat.instance.panel.blinkTabs) do
            if tabTitle == blinkedTab then
                table.remove(ISChat.instance.panel.blinkTabs, i);
                break;
            end
        end
        ISChat.instance.panel:removeView(foundedTab);
        ISChat.instance.minimumWidth = ISChat.instance.panel:getWidthOfAllTabs() + 2 * ISChat.instance.inset;

    end
    ISChat.instance.tabCnt = ISChat.instance.tabCnt - 1;
    if ISChat.instance.tabCnt == 1 then
        local lastTab = ISChat.instance.tabs[1];
        ISChat.instance.panel:setVisible(false);
        ISChat.instance.panel:removeView(lastTab);
        ISChat.instance.chatText = lastTab;
        ISChat.instance:addChild(ISChat.instance.chatText);
        ISChat.instance.chatText:setVisible(true);
    end
    ISChat.instance:onActivateView();
end

ISChat.onSetDefaultTab = function(defaultTabTitle)
    for i,v in ipairs(ISChat.instance.tabs) do
        if v.tabTitle == defaultTabTitle then
            ISChat.instance.defaultTab = v;
            ISChat.instance.currentTabID = i;
            ISChat.instance.chatText = v;
            ISChat.instance:onActivateView();
            return;
        end
    end
    ISChat.instance.defaultTab = nil;
end

function ISChat:onActivateView()
    if self.tabCnt > 1 then
        self.chatText = self.panel.activeView.view;
    end
    for i,blinkedTab in ipairs(self.panel.blinkTabs) do
        if self.chatText.tabTitle and self.chatText.tabTitle == blinkedTab then
            table.remove(self.panel.blinkTabs, i);
            break;
        end
    end
    for i,tab in ipairs(self.tabs) do
        if tab.tabTitle == self.chatText.tabTitle then
            self.currentTabID = i;
            break;
        end
    end
    focusOnTab(self.chatText.tabID)
end

function ISChat:new (x, y, width, height)
    local o = {}
    --o.data = {}
    o = ISCollapsableWindow:new(x, y, width, height);
    setmetatable(o, self)
    self.__index = self
    o.x = x;
    o.y = y;
    o.borderColor = {r=1, g=1, b=1, a=0.7};
    o.backgroundColor = {r=0, g=0, b=0, a=1};
    o.width = width;
    o.height = height;
    o.anchorLeft = true;
    o.anchorRight = false;
    o.anchorTop = true;
    o.anchorBottom = false;
    o.onRightMouseUp = nil;
    o.prevBtnTxt = getTexture("media/ui/sGuidePrevBtn.png");
    o.nextBtnTxt = getTexture("media/ui/sGuideNextBtn.png");
    o.closeBtnTxt = getTexture("media/ui/sGuideCloseBtn.png");
    o.chatLockedButtonTexture = getTexture("media/ui/lock.png");
    o.chatUnLockedButtonTexture = getTexture("media/ui/lockOpen.png");
    o.background = true;
    o.timerTextEntry = 0;
    o.servermsg = "";
    o.servermsgTimer = 0;
    o.showTimestamp = true;
    o.showTitle = true;
    o.chatFont = "medium";
    o.fadeTime = 0;
    o.minOpaque = 1; -- in percentage
    o.maxOpaque = 1; -- in percentage
    o.opaqueOnFocus = true;
    o.backgroundColor.a = 1.0 * o.maxOpaque;
    o.fade = UITransition.new()
    o.fade:setIgnoreUpdateTime(true);
    ISChat.instance = o;
    Events.OnTick.Add(ISChat.ontick);
    return o
end

function ISChat:onPressDown()
    local chat = ISChat.instance;
    local chatText = chat.chatText;
    chatText.logIndex = chatText.logIndex - 1;
    if chatText.logIndex < 0 then
        chatText.logIndex = 0;
    end
    if chatText.log and chatText.log[chatText.logIndex] then
        --        print(ISChat.instance.log[ISChat.instance.logIndex]);
        chat.textEntry:setText(chatText.log[chatText.logIndex]);
    else
        chat.textEntry:setText("");
    end
end

function ISChat:onPressUp()
    local chat = ISChat.instance;
    local chatText = chat.chatText;
    chatText.logIndex = chatText.logIndex + 1;
    if chatText.logIndex > #chatText.log then
        chatText.logIndex = #chatText.log;
    end
    if chatText.log and chatText.log[chatText.logIndex] then
        --        print(ISChat.instance.log[ISChat.instance.logIndex]);
        chat.textEntry:setText(chatText.log[chatText.logIndex]);
    end
end

function ISChat:isCursorOnTitlebar(x, y)
   return y <= self:titleBarHeight();
end

function ISChat.onMouseUp(target, x, y)
    -- check if player clicked on titlebar
    if target:getUIName() == ISChat.windowName and ISChat.instance.moving then
        ISCollapsableWindow.onMouseUp(ISChat.instance, x, y);
        return true;
    end

    -- checks if player clicked on text panel
    if target:getUIName() == ISChat.textPanelName then
        -- if window focused we should ignore other handlers. if we returns true then another handlers will be ignored
        -- we ignore clicks on text
        return ISChat.focused;
    end

    -- checks if player clicked on tab panel
    if target:getUIName() == ISChat.tabPanelName then
        ISTabPanel.onMouseUp(ISChat.instance.panel, x, y)
        return ISChat.focused;
    end

    -- checks if player clicked on text entry panel
    if target:getUIName() == ISChat.textEntryName then
        return ISChat.focused;
    end

    -- checks if player clicked on bottom y resize widget
    if not ISChat.instance.locked and target:getUIName() == ISChat.yResizeWidgetName then
        ISResizeWidget.onMouseUp(ISChat.instance.resizeWidget2, x, y);
        return true
    end

    -- checks if player clicked on xy resize widget
    if not ISChat.instance.locked and target:getUIName() == ISChat.xyResizeWidgetName then
        ISResizeWidget.onMouseUp(ISChat.instance.resizeWidget, x, y);
        return true
    end

    return ISChat.focused;
end

function ISChat.onMouseDown(target, x, y)
    -- check if player clicked on titlebar
    if target:getUIName() == ISChat.windowName and not ISChat.instance.locked and ISChat.instance:isCursorOnTitlebar(x, y) then
        ISCollapsableWindow.onMouseDown(ISChat.instance, x, y);
        return true;
    end

    -- checks if player clicked on text panel
    if target:getUIName() == ISChat.textPanelName then
        -- if window focused we should ignore other handlers. if we returns true then another handlers will be ignored
        -- we ignore clicks on text
        return ISChat.focused;
    end

    -- checks if player clicked on tab panel
    if target:getUIName() == ISChat.tabPanelName then
        ISChat.ISTabPanelOnMouseDown(ISChat.instance.panel, x, y)
        return ISChat.focused;
    end

    -- checks if player clicked on text entry panel
    if target:getUIName() == ISChat.textEntryName then
        return ISChat.focused;
    end

    -- checks if player clicked on bottom y resize widget
    if not ISChat.instance.locked and target:getUIName() == ISChat.yResizeWidgetName then
        ISResizeWidget.onMouseDown(ISChat.instance.resizeWidget2, x, y);
        return true
    end

    -- checks if player clicked on xy resize widget
    if not ISChat.instance.locked and target:getUIName() == ISChat.xyResizeWidgetName then
        ISResizeWidget.onMouseDown(ISChat.instance.resizeWidget, x, y);
        return true
    end

    return ISChat.focused;
end

ISChat.ISTabPanelOnMouseDown = function(target, x, y)
    if target:getMouseY() >= 0 and target:getMouseY() < target.tabHeight then
        if target:getScrollButtonAtX(x) == "left" then
            target:onMouseWheel(-1)
            return true
        end
        if target:getScrollButtonAtX(x) == "right" then
            target:onMouseWheel(1)
            return true
        end
        local tabIndex = target:getTabIndexAtX(target:getMouseX())
        if tabIndex >= 1 and tabIndex <= #target.viewList and ISTabPanel.xMouse == -1 and ISTabPanel.yMouse == -1 then -- if we clicked on a tab, the first time we set up the x,y of the mouse, so next time we can see if the player moved the mouse (moved the tab)
            ISTabPanel.xMouse = target:getMouseX();
            ISTabPanel.yMouse = target:getMouseY();
            target.draggingTab = tabIndex - 1;
            local clickedTab = target.viewList[target.draggingTab + 1];
            target:activateView(clickedTab.name)
            return true
        end
    end
    return false
end

function ISChat:onRightMouseUp(x, y)
    if ISChat.focused then
        return true;
    end
    return false;
end

function ISChat:onRightMouseDown(x, y)
    if ISChat.focused then
        return true;
    end
    return false;
end

function ISChat:mute(username)
    if self.mutedUsers[username] then
        self.mutedUsers[username] = nil
    else
        self.mutedUsers[username] = true
    end
end

function ISChat:isMuted(username)
    return self.mutedUsers[username] ~= nil
end

-- RJ : Do this because of some delay your last key entered in chat can pop a "KeyPressed"
ISChat.ontick = function()
    if ISChat.instance and ISChat.instance.timerTextEntry > 0 then
        ISChat.instance.timerTextEntry = ISChat.instance.timerTextEntry - 1;
        if ISChat.instance.timerTextEntry == 0 then
            doKeyPress(true);
        end
    end
end

ISChat.unfocusEvent = function()
    if ISChat.instance == nil then
        return;
    end

    ISChat.instance:unfocus();
end

function ISChat:RestoreLayout(name, layout)
    ISLayoutManager.DefaultRestoreWindow(self, layout)
    if layout.locked == 'false' then
        self.locked = false;
        self.lockButton:setImage(self.chatUnLockedButtonTexture);
    else
        self.locked = true;
        self.lockButton:setImage(self.chatLockedButtonTexture);
    end
    self:recalcSize();
end

function ISChat:SaveLayout(name, layout)
    ISLayoutManager.DefaultSaveWindow(self, layout)
    if self.locked then layout.locked = 'true' else layout.locked = 'false' end
end

ISChat.createChat = function()
    if not isClient() then
        return;
    end
    ISChat.chat = ISChat:new(15, getCore():getScreenHeight() - 400, 500, 200);
    ISChat.chat:initialise();
    ISChat.chat:addToUIManager();
    ISChat.chat:setVisible(true);
    ISChat.chat:bringToTop()
    ISLayoutManager.RegisterWindow('chat', ISChat, ISChat.chat)

    ISChat.instance:setVisible(true);

    Events.OnAddMessage.Add(ISChat.addLineInChat);
    Events.OnMouseDown.Add(ISChat.unfocusEvent);
    Events.OnKeyPressed.Add(ISChat.onToggleChatBox);
    Events.OnKeyKeepPressed.Add(ISChat.onKeyKeepPressed);
    Events.OnTabAdded.Add(ISChat.onTabAdded);
    Events.OnSetDefaultTab.Add(ISChat.onSetDefaultTab);
    Events.OnTabRemoved.Add(ISChat.onTabRemoved);
    Events.SwitchChatStream.Add(ISChat.onSwitchStream)
end

function logTo01(value)
    if value < 0.0 or value > 1.0 then
        error("only [0,1] accepted!");
    end
    if value > 0.0 then
        return math.log10(value * 100) - 1;
    end
    return 0.0;
end

Events.OnGameStart.Add(ISChat.createChat);
Events.OnChatWindowInit.Add(ISChat.initChat)
