// BACKGROUND VARIABLES
let b = {
    debug: [],
    bg_running: false,
    schedule_save: 0,
    windows: {},
    tabs: {},
    tt_ids: {},
    EmptyTabs: [],
    newTabUrl: browserId == "F" ? "about:newtab" : "chrome://startpage/",
    safe_mode: false
};

// BACKGROUND FUNCTIONS
function pushlog(log) {
    b.debug.push(log);
    if (b.debug.length > 100) b.debug.splice(0, 1);
    console.log(log);
    b.schedule_save++;
}

function ReplaceParents(oldTabId, newTabId) {
    for (let tabId in b.tabs) {
        if (b.tabs[tabId].parent == oldTabId) b.tabs[tabId].parent = newTabId;
    }
}

async function DiscardTab(tabId) {
    let DiscardTimeout = 0;
    let Discard = setInterval(function() {
        chrome.tabs.get(tabId, function(tab) {
            if ((tab.favIconUrl != undefined && tab.favIconUrl != "" && tab.title != undefined && tab.title != "") || tab.status == "complete" || tab.audible) {
                chrome.tabs.discard(tab.id);
                clearInterval(Discard);
            }
            if (DiscardTimeout > 300) clearInterval(Discard);
        });
        DiscardTimeout++;
    }, 2000);
}

async function DiscardWindow(windowId) {
    let DiscardTimeout = 0;
    let DiscardedTabs = 0;
    let Discard = setInterval(function() {
        chrome.windows.get(windowId, { populate: true }, function(w) {
            for (let i = 0; i < w.tabs.length; i++) {
                if (w.tabs[i].discarded == false && w.tabs[i].active == false) {
                    if ((w.tabs[i].favIconUrl != undefined && w.tabs[i].favIconUrl != "" && w.tabs[i].title != undefined && w.tabs[i].title != "") || w.tabs[i].status == "complete" || tab.audible) {
                        chrome.tabs.discard(w.tabs[i].id);
                        DiscardedTabs++;
                    }
                }
            }
            if (DiscardedTabs == w.tabs.length) clearInterval(Discard);
        });
        if (DiscardTimeout > 300) clearInterval(Discard);
        DiscardTimeout++;
    }, 5000);
}

function GetTabGroupId(tabId, windowId) {
    let groupId = "tab_list";
    if (tabId == undefined || windowId == undefined || b.windows[windowId] == undefined || b.tabs[tabId] == undefined) return groupId;
    let parent = b.tabs[tabId].parent;
    while (parent) {
        if (isNaN(parent) == false && b.tabs[parent]) {
            parent = b.tabs[parent].parent;
        } else {
            if (parent.match("tab_list|g_|f_") == null && b.tabs[parent]) {
                parent = b.tabs[parent].parent;
            } else {
                if (parent.match("f_") != null && b.windows[windowId].folders[parent]) {
                    parent = b.windows[windowId].folders[parent].parent;
                } else {
                    if (parent.match("pin_list|tab_list|g_") != null) {
                        groupId = parent;
                        parent = false;
                    } else {
                        parent = false;
                    }
                }
            }
        }
    }
    return groupId;
}

function GetTabParents(tabId, windowId) {
    let Parents = [];
    if (tabId == undefined) return Parents;
    if (b.tabs[tabId] == undefined) return Parents;
    let parent = b.tabs[tabId].parent;
    let escape = 9999;
    while (escape > 0 && (b.tabs[parent] != undefined || b.windows[windowId].folders[parent])) {
        if (b.tabs[parent]) {
            Parents.push(parent);
            parent = b.tabs[parent].parent;
        } else {
            if (b.windows[windowId].folders[parent]) {
                Parents.push(parent);
                parent = b.windows[windowId].folders[parent].parent;
            }
        }
        escape--;
    }
    return Parents;
}

function GetChildren(TTObj, parentId) { // TTObj is b.tabs or b.windows[winId].folders
    let Children = [];
    for (let Id in TTObj) {
        if (TTObj[Id].parent == parentId) Children.push(Id);
    }
    return Children;
}

function ShiftChildrenIndexes(TabsIdsArray, OpenerIndex, folderIdsArray, windowId) {
    for (let tabId of TabsIdsArray) { // shift indexes of siblings tabs
        if (b.tabs[tabId].index > OpenerIndex) b.tabs[tabId].index += 1;
    }
    for (let folderId of folderIdsArray) { // shift indexes of siblings folders
        if (b.windows[windowId].folders[folderId].index > OpenerIndex) b.windows[windowId].folders[folderId].index += 1;
    }
}

function UnshiftChildrenIndexes(TabsIdsArray, ClosedIndex, folderIdsArray, windowId) {
    for (let tabId of TabsIdsArray) { // shift indexes of siblings tabs
        if (b.tabs[tabId].index > ClosedIndex) b.tabs[tabId].index -= 1;
    }
    for (let folderId of folderIdsArray) { // shift indexes of siblings folders
        if (b.windows[windowId].folders[folderId].index > ClosedIndex) b.windows[windowId].folders[folderId].index -= 1;
    }
}

function AppendTabToGroupOnRegexMatch(tabId, windowId, url) {
    let TabGroupId = GetTabGroupId(tabId, windowId);
    for (let i = 0; i < opt.tab_group_regexes.length; i++) {
        let regexPair = opt.tab_group_regexes[i];
        if (url.match(regexPair[0])) {
            let groupId = FindGroupIdByName(regexPair[1], b.windows[windowId].groups);
            let groupName = regexPair[1];
            if (groupId === null) { // no group
                let newGroupID = "";
                while (newGroupID == "") {
                    newGroupID = "g_" + GenerateRandomID();
                    for (let wId in b.windows) {
                        for (let gId in b.windows[wId].groups) {
                            if (gId == newGroupID) newGroupID = "";
                        }
                    }
                }
                groupId = newGroupID;
                b.windows[windowId].groups[groupId] = { id: groupId, index: 999, active_tab: 0, prev_active_tab: 0, name: groupName, font: "" };
                chrome.runtime.sendMessage({ command: "append_group", groupId: groupId, group_name: groupName, font_color: "", windowId: windowId });
            }
            if (TabGroupId != groupId && groupId != null) {
                b.tabs[tabId].parent = groupId;
                setTimeout(function() {
                    chrome.runtime.sendMessage({ command: "append_tab_to_group", tabId: tabId, groupId: groupId, windowId: windowId });
                }, 100);
            }
            break;
        }
    }
    return b.tabs[tabId].parent;
}

function FindGroupIdByName(name, groups) {
    for (let groupId in groups) {
        if (!groups.hasOwnProperty(groupId)) continue;
        if (groups[groupId].name === name) return groupId;
    }
    return null;
}


// LISTENERS
function StartBackgroundListeners() {
    chrome.runtime.onMessage.addListener(function(message, sender, sendResponse) {
        if (message.command == "is_bg_ready") {
            sendResponse(b.bg_running);
            return;
        }
        if (message.command == "reload") {
            window.location.reload();
            return;
        }
        if (message.command == "reload_options") {
            opt = Object.assign({}, message.opt);
            return;
        }
        if (message.command == "get_windows") {
            sendResponse(b.windows);
            return;
        }
        if (message.command == "get_folders") {
            if (b.windows[message.windowId]) {
                sendResponse(b.windows[message.windowId].folders);
            }
            return;
        }
        if (message.command == "save_folders") {
            if (b.windows[message.windowId]) {
                b.windows[message.windowId].folders = Object.assign({}, message.folders);
                b.schedule_save++;
            }
            return;
        }
        if (message.command == "get_groups") {
            if (b.windows[message.windowId]) {
                sendResponse(b.windows[message.windowId].groups);
            }
            return;
        }
        if (message.command == "save_groups") {
            if (b.windows[message.windowId]) {
                b.windows[message.windowId].groups = Object.assign({}, message.groups);
                b.schedule_save++;
            }
            return;
        }
        if (message.command == "set_active_group") {
            if (b.windows[message.windowId]) {
                b.windows[message.windowId].active_group = message.active_group;
                b.schedule_save++;
            }
            return;
        }
        if (message.command == "get_active_group") {
            if (b.windows[message.windowId]) {
                sendResponse(b.windows[message.windowId].active_group);
            }
            return;
        }
        if (message.command == "set_search_filter") {
            if (b.windows[message.windowId]) {
                b.windows[message.windowId].search_filter = message.search_filter;
                b.schedule_save++;
            }
            return;
        }
        if (message.command == "get_search_filter") {
            if (b.windows[message.windowId]) {
                sendResponse(b.windows[message.windowId].search_filter);
            }
            return;
        }
        if (message.command == "set_active_shelf") {
            if (b.windows[message.windowId]) {
                b.windows[message.windowId].active_shelf = message.active_shelf;
                b.schedule_save++;
            }
            return;
        }
        if (message.command == "get_active_shelf") {
            if (b.windows[message.windowId]) {
                sendResponse(b.windows[message.windowId].active_shelf);
            }
            return;
        }
        if (message.command == "set_group_bar") {
            if (b.windows[message.windowId]) {
                b.windows[message.windowId].group_bar = message.group_bar;
                b.schedule_save++;
            }
            return;
        }
        if (message.command == "get_group_bar") {
            if (b.windows[message.windowId]) {
                sendResponse(b.windows[message.windowId].group_bar);
            }
            return;
        }
        if (message.command == "get_browser_tabs") {
            sendResponse(b.tabs);
            return;
        }
        if (message.command == "update_tab") {
            if (b.tabs[message.tabId]) {
                if (message.tab.index) {
                    b.tabs[message.tabId].index = message.tab.index;
                }
                if (message.tab.expand) {
                    b.tabs[message.tabId].expand = message.tab.expand;
                }
                if (message.tab.parent) {
                    b.tabs[message.tabId].parent = message.tab.parent;
                }
                b.schedule_save++;
            }
            return;
        }
        if (message.command == "update_all_tabs") {
            for (let pin of message.pins) {
                if (b.tabs[pin.id]) {
                    b.tabs[pin.id].parent = "pin_list";
                    b.tabs[pin.id].expand = "";
                    b.tabs[pin.id].index = pin.index;
                }
            }
            for (let tab of message.tabs) {
                if (b.tabs[tab.id]) {
                    b.tabs[tab.id].parent = tab.parent;
                    b.tabs[tab.id].expand = tab.expand;
                    b.tabs[tab.id].index = tab.index;
                }
            }
            b.schedule_save++;
            return;
        }
        if (message.command == "discard_tab") {
            DiscardTab(message.tabId);
            return;
        }
        if (message.command == "discard_window") {
            DiscardWindow(message.windowId);
            return;
        }
        if (message.command == "remove_tab_from_empty_tabs") {
            setTimeout(function() {
                if (b.EmptyTabs.indexOf(message.tabId) != -1) {
                    b.EmptyTabs.splice(b.EmptyTabs.indexOf(message.tabId), 1);
                }
            }, 100);
            return;
        }
        if (message.command == "debug") {
            pushlog(message.log);
            return;
        }
    });
    
    
    // WebChromeClient.onConsoleMessage(ConsoleMessage consoleMessage)
}

// NEW TAB
function OnMessageTabCreated(NewTab, activeTabId) {
    let ParentId;
    let AfterId;
    let append;

    if (b.windows[NewTab.windowId] && NewTab.active) {
        b.windows[NewTab.windowId].groups[b.windows[NewTab.windowId].active_group].active_tab = NewTab.id;
    }

    if (NewTab.url == b.newTabUrl) {
        b.EmptyTabs.push(NewTab.id);
    }

    if (NewTab.pinned) {
        let PinTabs = GetChildren(b.tabs, "pin_list");
        b.tabs[NewTab.id].parent = "pin_list";
        if (opt.append_pinned_tab == "after") {
            if (NewTab.openerTabId && b.tabs[NewTab.openerTabId]) { // has opener tab case
                ShiftChildrenIndexes(PinTabs, b.tabs[NewTab.openerTabId].index, [], NewTab.windowId);
                b.tabs[NewTab.id].index = NewTab.index;
                AfterId = NewTab.openerTabId;
            } else {
                if (b.tabs[activeTabId]) { // after active case
                    ShiftChildrenIndexes(PinTabs, b.tabs[activeTabId].index, [], NewTab.windowId);
                    AfterId = activeTabId;
                }
            }
        }
        if (opt.append_pinned_tab == "first") { // as first
            ShiftChildrenIndexes(PinTabs, -1, [], NewTab.windowId);
            b.tabs[NewTab.id].index = 0;
            append = false;
        }
        if (opt.append_pinned_tab == "last") { // as last
            b.tabs[NewTab.id].index = PinTabs.length;
            append = true;
        }
    } else {

        if (opt.append_orphan_tab == "as_child" && opt.orphaned_tabs_to_ungrouped == false) {
            NewTab.openerTabId = activeTabId;
        }
        if (NewTab.openerTabId) { // child case
            let OpenerSiblingTabs = GetChildren(b.tabs, b.tabs[NewTab.openerTabId].parent);
            let OpenerSiblingFolders = GetChildren(b.windows[NewTab.windowId].folders, b.tabs[NewTab.openerTabId].parent);
            if (opt.append_child_tab == "after") { // place tabs flat without automatic tree
                b.tabs[NewTab.id].parent = b.tabs[NewTab.openerTabId].parent;
                ShiftChildrenIndexes(OpenerSiblingTabs, b.tabs[NewTab.openerTabId].index, OpenerSiblingFolders, NewTab.windowId);
                b.tabs[NewTab.id].index = b.tabs[NewTab.openerTabId].index + 1;
                AfterId = NewTab.openerTabId;
            } else {
                if (opt.max_tree_depth == 0) { // place tabs flat if limit is set to 0
                    b.tabs[NewTab.id].parent = b.tabs[NewTab.openerTabId].parent;
                    if (opt.append_child_tab_after_limit == "after") { // max tree depth, place tab after parent
                        ShiftChildrenIndexes(OpenerSiblingTabs, b.tabs[NewTab.openerTabId].index, OpenerSiblingFolders, NewTab.windowId);
                        b.tabs[NewTab.id].index = b.tabs[NewTab.openerTabId].index + 1;
                        AfterId = NewTab.openerTabId;
                    }

                    if (opt.append_child_tab_after_limit == "top" && opt.append_child_tab != "after") { // max tree depth, place tab on top (above parent)
                        ShiftChildrenIndexes(OpenerSiblingTabs, -1, OpenerSiblingFolders, NewTab.windowId);
                        b.tabs[NewTab.id].index = 0;
                        ParentId = b.tabs[NewTab.id].parent;
                        append = false;
                    }
                    if (opt.append_child_tab_after_limit == "bottom" && opt.append_child_tab != "after") { // max tree depth, place tab on bottom (below parent)
                        b.tabs[NewTab.id].index = OpenerSiblingTabs.length + OpenerSiblingFolders.length;
                        ParentId = b.tabs[NewTab.id].parent;
                        append = true;
                    }

                } else {

                    let Parents = GetTabParents(NewTab.openerTabId, NewTab.windowId);
                    let OpenerChildren = GetChildren(b.tabs, NewTab.openerTabId);
                    if (opt.max_tree_depth < 0 || (opt.max_tree_depth > 0 && Parents.length < opt.max_tree_depth)) { // append to tree on top and bottom
                        b.tabs[NewTab.id].parent = NewTab.openerTabId;
                        if (opt.append_child_tab == "top") { // place child tab at the top (reverse hierarchy)
                            ShiftChildrenIndexes(OpenerSiblingTabs, -1, OpenerSiblingFolders, NewTab.windowId);
                            b.tabs[NewTab.id].index = 0;
                            ParentId = b.tabs[NewTab.id].parent;
                        }

                        if (opt.append_child_tab == "bottom") { // place child tab at the bottom
                            b.tabs[NewTab.id].index = OpenerSiblingTabs.length + OpenerSiblingFolders.length;
                            ParentId = b.tabs[NewTab.id].parent;
                            append = true;
                        }

                    } else {

                        if (opt.max_tree_depth > 0 && Parents.length >= opt.max_tree_depth) { // if reached depth limit of the tree
                            b.tabs[NewTab.id].parent = b.tabs[NewTab.openerTabId].parent;
                            if (opt.append_child_tab_after_limit == "after") { // tab will append after opener
                                ShiftChildrenIndexes(OpenerSiblingTabs, b.tabs[NewTab.openerTabId].index, OpenerSiblingFolders, NewTab.windowId);
                                b.tabs[NewTab.id].index = b.tabs[NewTab.openerTabId].index + 1;
                                AfterId = NewTab.openerTabId;
                            }
                            if (opt.append_child_tab_after_limit == "top") { // tab will append on top
                                ShiftChildrenIndexes(OpenerSiblingTabs, -1, OpenerSiblingFolders, NewTab.windowId);
                                b.tabs[NewTab.id].index = 0;
                                ParentId = b.tabs[NewTab.id].parent;
                            }
                            if (opt.append_child_tab_after_limit == "bottom") { // tab will append on bottom
                                b.tabs[NewTab.id].index = OpenerSiblingTabs.length + OpenerSiblingFolders.length;
                                ParentId = b.tabs[NewTab.id].parent;
                                append = true;
                            }
                        }
                    }
                }
            }

        } else { // ORPHAN TAB

            if (opt.orphaned_tabs_to_ungrouped == true) { // if set to append orphan tabs to ungrouped group
                let TabListTabs = GetChildren(b.tabs, "tab_list");
                let TabListFolders = GetChildren(b.windows[NewTab.windowId].folders, "tab_list");
                b.tabs[NewTab.id].index = TabListTabs.length + TabListFolders.length;
                ParentId = "tab_list";
                append = true;
            } else {


                if (opt.append_orphan_tab == "after_active" || opt.append_orphan_tab == "active_parent_top" || opt.append_orphan_tab == "active_parent_bottom") {

                    if (b.windows[NewTab.windowId] && b.windows[NewTab.windowId].activeTabId) {
                        if (b.tabs[activeTabId]) {
                            let ActiveTabSiblings = GetChildren(b.tabs, b.tabs[activeTabId].parent);
                            let ActiveTabSiblingFolders = GetChildren(b.windows[NewTab.windowId].folders, b.tabs[activeTabId].parent);
                            b.tabs[NewTab.id].parent = b.tabs[activeTabId].parent;
                            if (opt.append_orphan_tab == "after_active") {
                                ShiftChildrenIndexes(ActiveTabSiblings, b.tabs[activeTabId].index, ActiveTabSiblingFolders, NewTab.windowId);
                                b.tabs[NewTab.id].index = b.tabs[activeTabId].index + 1;
                                AfterId = activeTabId;
                            }
                            if (opt.append_orphan_tab == "active_parent_top") {
                                ShiftChildrenIndexes(ActiveTabSiblings, -1, ActiveTabSiblingFolders, NewTab.windowId);
                                b.tabs[NewTab.id].index = 0;
                                ParentId = b.tabs[NewTab.id].parent;
                            }
                            if (opt.append_orphan_tab == "active_parent_bottom") {
                                b.tabs[NewTab.id].index = ActiveTabSiblings.length + ActiveTabSiblingFolders.length;
                                ParentId = b.tabs[NewTab.id].parent;
                                append = true;
                            }

                        } else { // FAIL, no active tab!
                            let ActiveGroupTabs = GetChildren(b.tabs, b.windows[NewTab.windowId].active_group);
                            let ActiveGroupFolders = GetChildren(b.windows[NewTab.windowId].folders, b.windows[NewTab.windowId].active_group);
                            b.tabs[NewTab.id].parent = b.windows[NewTab.windowId].active_group;
                            b.tabs[NewTab.id].index = ActiveGroupTabs.length + ActiveGroupFolders.length;
                            ParentId = b.windows[NewTab.windowId].active_group;
                        }
                    } else {
                        b.tabs[NewTab.id].parent = "tab_list";
                        b.tabs[NewTab.id].index = NewTab.index;
                        ParentId = "tab_list";
                    }
                }

                if (opt.append_orphan_tab == "top") {
                    let ActiveGroupTabs = GetChildren(b.tabs, b.windows[NewTab.windowId].active_group);
                    let ActiveGroupFolders = GetChildren(b.windows[NewTab.windowId].folders, b.tabs[activeTabId].parent);
                    b.tabs[NewTab.id].parent = b.windows[NewTab.windowId].active_group;
                    ShiftChildrenIndexes(ActiveGroupTabs, -1, ActiveGroupFolders, NewTab.windowId);
                    b.tabs[NewTab.id].index = 0;
                    ParentId = b.windows[NewTab.windowId].active_group;
                }

                if (opt.append_orphan_tab == "bottom") {
                    let ActiveGroupTabs = GetChildren(b.tabs, b.windows[NewTab.windowId].active_group);
                    let ActiveGroupFolders = b.tabs[activeTabId] ? GetChildren(b.windows[NewTab.windowId].folders, b.tabs[activeTabId].parent) : [];
                    b.tabs[NewTab.id].parent = b.windows[NewTab.windowId].active_group;
                    b.tabs[NewTab.id].index = ActiveGroupTabs.length + ActiveGroupFolders.length;
                    ParentId = b.windows[NewTab.windowId].active_group;
                    append = true;
                }
            }
        }

        if (opt.move_tabs_on_url_change === "all_new" && NewTab.pinned == false) {
            setTimeout(function() {
                chrome.tabs.get(NewTab.id, function(CheckTabsUrl) {
                    AppendTabToGroupOnRegexMatch(CheckTabsUrl.id, CheckTabsUrl.windowId, CheckTabsUrl.url);
                });
            }, 100);
        }
    }
    setTimeout(function() {
        b.schedule_save++;
    }, 500);

    chrome.runtime.sendMessage({ command: "tab_created", windowId: NewTab.windowId, tabId: NewTab.id, tab: NewTab, ParentId: ParentId, InsertAfterId: AfterId, Append: append });
}

function SafeModeCheck() {
    setInterval(function() {
        if (b.safe_mode) {
            if (browserId == "F") {
                chrome.windows.getAll({ windowTypes: ["normal"], populate: true }, function(w) {
                    for (win of w) {
                        Promise.resolve(browser.sessions.getWindowValue(win.id, "TTdata")).then(function(WindowData) {
                            if (WindowData != undefined) {
                                chrome.runtime.sendMessage({command: "reload_sidebar"});
                                window.location.reload();
                            }
                        });
                    }
                });
            }
            if (browserId == "O") {
                chrome.runtime.sendMessage({command: "reload_sidebar"});
                window.location.reload();
            }
            if (browserId == "V") {
                chrome.runtime.sendMessage({command: "reload_sidebar"});
                window.location.reload();
            }
        }
    }, 2000);
}


// START BACKGROUND SCRIPT
document.addEventListener("DOMContentLoaded", function() {
    StartBackgroundListeners();
    
    if (browserId == "F") {
        QuantumStart();
    }
    if (browserId == "O") {
        OperaStart();
    }
    if (browserId == "V") {
        VivaldiStart();
    }
    
});