// ChuanhuChat core javascript

const MAX_HISTORY_LENGTH = 32;

var key_down_history = [];
var currentIndex = -1;

var gradioContainer = null;
var user_input_ta = null;
var user_input_tb = null;
var userInfoDiv = null;
var appTitleDiv = null;
var chatbotArea = null;
var chatbot = null;
var chatbotIndicator = null;
var uploaderIndicator = null;
var chatListIndicator = null;
var chatbotWrap = null;
var apSwitch = null;
var messageBotDivs = null;
var loginUserForm = null;
var logginUser = null;
var updateToast = null;
var sendBtn = null;
var cancelBtn = null;
var sliders = null;
var updateChuanhuBtn = null;
var statusDisplay = null;

var historySelector = null;
var chuanhuPopup = null;
var settingBox = null;
var trainingBox = null;
var popupWrapper = null;
var chuanhuHeader = null;
var menu = null;
var toolbox = null;
// var trainBody = null;

var isInIframe = (window.self !== window.top);
var currentTime = new Date().getTime();

let windowWidth = window.innerWidth; // 初始窗口宽度

function addInit() {
    var needInit = {chatbotIndicator, uploaderIndicator};

    chatbotIndicator = gradioApp().querySelector('#chuanhu-chatbot > div.wrap');
    uploaderIndicator = gradioApp().querySelector('#upload-index-file > div.wrap');
    chatListIndicator = gradioApp().querySelector('#history-select-dropdown > div.wrap');

    for (let elem in needInit) {
        if (needInit[elem] == null) {
            // addInited = false;
            return false;
        }
    }

    chatbotObserver.observe(chatbotIndicator, { attributes: true, childList: true, subtree: true });
    chatListObserver.observe(chatListIndicator, { attributes: true });
    setUploader();

    return true;
}

function initialize() {
    gradioObserver.observe(gradioApp(), { childList: true, subtree: true });

    loginUserForm = gradioApp().querySelector(".gradio-container > .main > .wrap > .panel > .form")
    gradioContainer = gradioApp().querySelector(".gradio-container");
    user_input_tb = gradioApp().getElementById('user-input-tb');
    userInfoDiv = gradioApp().getElementById("user-info");
    appTitleDiv = gradioApp().getElementById("app-title");
    chatbotArea = gradioApp().querySelector('#chatbot-area');
    chatbot = gradioApp().querySelector('#chuanhu-chatbot');
    chatbotWrap = gradioApp().querySelector('#chuanhu-chatbot > .wrapper > .wrap');
    apSwitch = gradioApp().querySelector('.apSwitch input[type="checkbox"]');
    updateToast = gradioApp().querySelector("#toast-update");
    sendBtn = gradioApp().getElementById("submit-btn");
    cancelBtn = gradioApp().getElementById("cancel-btn");
    sliders = gradioApp().querySelectorAll('input[type="range"]');
    updateChuanhuBtn = gradioApp().getElementById("update-chuanhu-btn");
    statusDisplay = gradioApp().querySelector('#status-display');

    historySelector = gradioApp().querySelector('#history-select-dropdown');
    chuanhuPopup = gradioApp().querySelector('#chuanhu-popup');
    settingBox = gradioApp().querySelector('#chuanhu-setting');
    trainingBox = gradioApp().querySelector('#chuanhu-training');
    popupWrapper = gradioApp().querySelector('#popup-wrapper');
    chuanhuHeader = gradioApp().querySelector('#chuanhu-header');
    menu = gradioApp().querySelector('#menu-area');
    toolbox = gradioApp().querySelector('#toolbox-area');
    // trainBody = gradioApp().querySelector('#train-body');

    // if (loginUserForm) {
    // localStorage.setItem("userLogged", true);
    // userLogged = true;
    // }

    adjustDarkMode();
    adjustSide();
    setChatList();
    setChatListHeader();
    setLoclize();
    selectHistory();
    // setChatbotHeight();
    setPopupBoxPosition();
    setSlider();
    setCheckboxes();
    checkModel();
    menuClick();
    toolboxClick();
    settingBox.classList.add('hideBox');
    trainingBox.classList.add('hideBox');

    if (!historyLoaded) loadHistoryHtml();
    if (!usernameGotten) getUserInfo();

    setUpdater();

    setChatbotScroll();
    setTimeout(showOrHideUserInfo(), 2000);

    // setHistroyPanel();
    // trainBody.classList.add('hide-body');



    return true;
}

function gradioApp() {
    const elems = document.getElementsByTagName('gradio-app');
    const elem = elems.length == 0 ? document : elems[0];

    if (elem !== document) {
        elem.getElementById = function(id) {
            return document.getElementById(id);
        };
    }
    return elem.shadowRoot ? elem.shadowRoot : elem;
}

function showConfirmationDialog(a, file, c) {
    if (file != "") {
        var result = confirm(i18n(deleteConfirm_i18n_pref) + file + i18n(deleteConfirm_i18n_suff));
        if (result) {
            return [a, file, c];
        }
    }
    return [a, "CANCELED", c];
}

function selectHistory() {
    user_input_ta = user_input_tb.querySelector("textarea");
    if (user_input_ta) {
        disableSendBtn();
        // 在 textarea 上监听 keydown 事件
        user_input_ta.addEventListener("keydown", function (event) {
            var value = user_input_ta.value.trim();
            // 判断按下的是否为方向键
            if (event.code === 'ArrowUp' || event.code === 'ArrowDown') {
                // 如果按下的是方向键,且输入框中有内容,且历史记录中没有该内容,则不执行操作
                if (value && key_down_history.indexOf(value) === -1)
                    return;
                // 对于需要响应的动作,阻止默认行为。
                event.preventDefault();
                var length = key_down_history.length;
                if (length === 0) {
                    currentIndex = -1; // 如果历史记录为空,直接将当前选中的记录重置
                    return;
                }
                if (currentIndex === -1) {
                    currentIndex = length;
                }
                if (event.code === 'ArrowUp' && currentIndex > 0) {
                    currentIndex--;
                    user_input_ta.value = key_down_history[currentIndex];
                } else if (event.code === 'ArrowDown' && currentIndex < length - 1) {
                    currentIndex++;
                    user_input_ta.value = key_down_history[currentIndex];
                }
                user_input_ta.selectionStart = user_input_ta.value.length;
                user_input_ta.selectionEnd = user_input_ta.value.length;
                const input_event = new InputEvent("input", { bubbles: true, cancelable: true });
                user_input_ta.dispatchEvent(input_event);
            } else if (event.code === "Enter") {
                if (value) {
                    currentIndex = -1;
                    if (key_down_history.indexOf(value) === -1) {
                        key_down_history.push(value);
                        if (key_down_history.length > MAX_HISTORY_LENGTH) {
                            key_down_history.shift();
                        }
                    }
                }
            }
        });
    }
}

function disableSendBtn() {
    sendBtn.disabled = user_input_ta.value.trim() === '';
    user_input_ta.addEventListener('input', () => {
        sendBtn.disabled = user_input_ta.value.trim() === '';
    });
}

function checkModel() {
    const model = gradioApp().querySelector('#model-select-dropdown input');
    var modelValue = model.value;
    checkGPT();
    checkXMChat();
    function checkGPT() {
        modelValue = model.value;
        if (modelValue.toLowerCase().includes('gpt')) {
            gradioApp().querySelector('#header-btn-groups').classList.add('is-gpt');
        } else {
            gradioApp().querySelector('#header-btn-groups').classList.remove('is-gpt');
        }
        // console.log('gpt model checked')
    }
    function checkXMChat() {
        modelValue = model.value;
        if (modelValue.includes('xmchat')) {
            chatbotArea.classList.add('is-xmchat');
        } else {
            chatbotArea.classList.remove('is-xmchat');
        }
    }

    model.addEventListener('blur', ()=>{
        setTimeout(()=>{
            checkGPT();
            checkXMChat();
        }, 100);
    });
}

function toggleDarkMode(isEnabled) {
    if (isEnabled) {
        document.body.classList.add("dark");
        document.querySelector('meta[name="theme-color"]').setAttribute('content', '#171717');
        document.body.style.setProperty("background-color", "var(--neutral-950)", "important");
    } else {
        document.body.classList.remove("dark");
        document.querySelector('meta[name="theme-color"]').setAttribute('content', '#ffffff');
        document.body.style.backgroundColor = "";
    }
}
function adjustDarkMode() {
    const darkModeQuery = window.matchMedia("(prefers-color-scheme: dark)");
    apSwitch.checked = darkModeQuery.matches;
    toggleDarkMode(darkModeQuery.matches);
    darkModeQuery.addEventListener("change", (e) => {
        apSwitch.checked = e.matches;
        toggleDarkMode(e.matches);
    });
    apSwitch.addEventListener("change", (e) => {
        toggleDarkMode(e.target.checked);
    });
}
function btnToggleDarkMode() {
    apSwitch.checked = !apSwitch.checked;
    toggleDarkMode(apSwitch.checked);
}

function setScrollShadow() {
    const toolboxScroll = toolbox.querySelector('#toolbox-area > .gradio-box > .gradio-tabs > div.tab-nav');
    const toolboxTabs = toolboxScroll.querySelectorAll('button');
    let toolboxScrollWidth = 0;
    toolboxTabs.forEach((tab) => {
        toolboxScrollWidth += tab.offsetWidth; // 获取按钮宽度并累加
    });
    function adjustScrollShadow() {
        if (toolboxScroll.scrollLeft > 0) {
            toolboxScroll.classList.add('scroll-shadow-left');
        } else {
            toolboxScroll.classList.remove('scroll-shadow-left');
        }

        if (toolboxScroll.scrollLeft + toolboxScroll.clientWidth < toolboxScrollWidth) {
            toolboxScroll.classList.add('scroll-shadow-right');
        } else {
            toolboxScroll.classList.remove('scroll-shadow-right');
        }
    }
    toolboxScroll.addEventListener('scroll', () => {
        adjustScrollShadow();
    });
    // no, I failed to make shadow appear on the top layer...
}

function setPopupBoxPosition() {
    const screenWidth = window.innerWidth;
    const screenHeight = window.innerHeight;
    popupWrapper.style.height = `${screenHeight}px`;
    popupWrapper.style.width = `${screenWidth}px`;
    // const popupBoxWidth = 680;
    // const popupBoxHeight = 400;
    // chuanhuPopup.style.left = `${(screenWidth - popupBoxWidth) / 2}px`;
    // chuanhuPopup.style.top = `${(screenHeight - popupBoxHeight) / 2}px`;
}

function updateVH() {
    const vh = window.innerHeight * 0.01;
    document.documentElement.style.setProperty('--vh', `${vh}px`);
}

function setChatbotHeight() {
    return;
    const screenWidth = window.innerWidth;
    const statusDisplay = document.querySelector('#status-display');
    const statusDisplayHeight = statusDisplay ? statusDisplay.offsetHeight : 0;
    const vh = window.innerHeight * 0.01;
    document.documentElement.style.setProperty('--vh', `${vh}px`);
    if (isInIframe) {
        chatbot.style.height = `700px`;
        chatbotWrap.style.maxHeight = `calc(700px - var(--line-sm) * 1rem - 2 * var(--block-label-margin))`
    } else {
        if (screenWidth <= 320) {
            chatbot.style.height = `calc(var(--vh, 1vh) * 100 - ${statusDisplayHeight + 150}px)`;
            chatbotWrap.style.maxHeight = `calc(var(--vh, 1vh) * 100 - ${statusDisplayHeight + 150}px - var(--line-sm) * 1rem - 2 * var(--block-label-margin))`;
        } else if (screenWidth <= 499) {
            chatbot.style.height = `calc(var(--vh, 1vh) * 100 - ${statusDisplayHeight + 100}px)`;
            chatbotWrap.style.maxHeight = `calc(var(--vh, 1vh) * 100 - ${statusDisplayHeight + 100}px - var(--line-sm) * 1rem - 2 * var(--block-label-margin))`;
        } else {
            chatbot.style.height = `calc(var(--vh, 1vh) * 100 - ${statusDisplayHeight + 160}px)`;
            chatbotWrap.style.maxHeight = `calc(var(--vh, 1vh) * 100 - ${statusDisplayHeight + 160}px - var(--line-sm) * 1rem - 2 * var(--block-label-margin))`;
        }
    }
}
function setChatbotScroll() {
    var scrollHeight = chatbotWrap.scrollHeight;
    chatbotWrap.scrollTo(0,scrollHeight)
}

function clearChatbot(a, b) {
    clearHistoryHtml();
    // clearMessageRows();
    return [a, b]
}

function chatbotContentChanged(attempt = 1, force = false) {
    for (var i = 0; i < attempt; i++) {
        setTimeout(() => {
            // clearMessageRows();
            saveHistoryHtml();
            disableSendBtn();

            gradioApp().querySelectorAll('#chuanhu-chatbot .message-wrap .message.bot').forEach(addChuanhuButton);

            if (chatbotIndicator.classList.contains('hide')) { // generation finished
                setLatestMessage();
                setChatList();
            }

            if (!chatbotIndicator.classList.contains('translucent')) { // message deleted
                var checkLatestAdded = setInterval(() => {
                    var latestMessageNow = gradioApp().querySelector('#chuanhu-chatbot > .wrapper > .wrap > .message-wrap .message.bot.latest');
                    if (latestMessageNow && latestMessageNow.querySelector('.message-btn-row')) {
                        clearInterval(checkLatestAdded);
                    } else {
                        setLatestMessage();
                    }
                }, 200);
            }


        }, i === 0 ? 0 : 200);
    }
    // 理论上是不需要多次尝试执行的,可惜gradio的bug导致message可能没有渲染完毕,所以尝试500ms后再次执行
}

var chatbotObserver = new MutationObserver(() => {
    chatbotContentChanged(1);
    if (chatbotIndicator.classList.contains('hide')) {
        // setLatestMessage();
        chatbotContentChanged(2);
    }
    if (!chatbotIndicator.classList.contains('translucent')) {
        chatbotContentChanged(2);
    }

});

var chatListObserver = new MutationObserver(() => {
    setChatList();
});

// 监视页面内部 DOM 变动
var gradioObserver = new MutationObserver(function (mutations) {
    for (var i = 0; i < mutations.length; i++) {
        if (mutations[i].addedNodes.length) {
            if (addInit()) {
                gradioObserver.disconnect();
                return;
            }
        }
    }
});

// 监视页面变化
window.addEventListener("DOMContentLoaded", function () {
    // const ga = document.getElementsByTagName("gradio-app");
    updateVH();
    windowWidth = window.innerWidth;
    gradioApp().addEventListener("render", initialize);
    isInIframe = (window.self !== window.top);
    historyLoaded = false;
});
window.addEventListener('resize', ()=>{
    // setChatbotHeight();
    updateVH();
    windowWidth = window.innerWidth;
    setPopupBoxPosition();
    adjustSide();
});
window.addEventListener('orientationchange', (event) => {
    updateVH();
    windowWidth = window.innerWidth;
    setPopupBoxPosition();
    adjustSide();
});
window.addEventListener('scroll', ()=>{setPopupBoxPosition();});
window.matchMedia("(prefers-color-scheme: dark)").addEventListener("change", adjustDarkMode);

// console suprise
var styleTitle1 = `
font-size: 16px;
font-family: ui-monospace, monospace;
color: #06AE56;
`
var styleDesc1 = `
font-size: 12px;
font-family: ui-monospace, monospace;
`
function makeML(str) {
    let l = new String(str)
    l = l.substring(l.indexOf("/*") + 3, l.lastIndexOf("*/"))
    return l
}
let ChuanhuInfo = function () {
    /*
   ________                      __             ________          __
  / ____/ /_  __  ______ _____  / /_  __  __   / ____/ /_  ____ _/ /_
 / /   / __ \/ / / / __ `/ __ \/ __ \/ / / /  / /   / __ \/ __ `/ __/
/ /___/ / / / /_/ / /_/ / / / / / / / /_/ /  / /___/ / / / /_/ / /_
\____/_/ /_/\__,_/\__,_/_/ /_/_/ /_/\__,_/   \____/_/ /_/\__,_/\__/

   川虎Chat (Chuanhu Chat) - GUI for ChatGPT API and many LLMs
 */
}
let description = `
© 2023 Chuanhu, MZhao, Keldos
GitHub repository: [https://github.com/GaiZhenbiao/ChuanhuChatGPT]\n
Enjoy our project!\n
`
console.log(`%c${makeML(ChuanhuInfo)}`,styleTitle1);
console.log(`%c${description}`, styleDesc1);