// QUANTUM
function QuantumStart() {
    chrome.storage.local.get(null, function(storage) {
        chrome.windows.getAll({ windowTypes: ["normal"], populate: true }, function(w) {
            
            let windows_data = {};
            let tabs_data = {};
            
            for (let win of w) {
                Promise.resolve(browser.sessions.getWindowValue(win.id, "TTdata")).then(function(WindowData) {
                    windows_data[win.id] = WindowData;
                    for (let tab of win.tabs) {
                        Promise.resolve(browser.sessions.getTabValue(tab.id, "TTdata")).then(function(TData) {
                            if (TData != undefined) b.tt_ids[TData.ttid] = tab.id;
                            tabs_data[tab.id] = TData;
                        });
                    }
                });
            }
            
            setTimeout(function() {

                // LOAD PREFERENCES
                Preferences_GetCurrentPreferences(storage);

                // CACHED COUNTS AND STUFF
                let tabs_matched = 0;
                let tabs_count = 0;

                for (let win of w) {
                    tabs_count += win.tabs.length;
                }

                let lastWinId = w[w.length - 1].id;
                let lastTabId = w[w.length - 1].tabs[w[w.length - 1].tabs.length - 1].id;

                if (opt.debug == true) {
                    if (storage.debug_log != undefined) b.debug = storage.debug_log;
                }

                for (let win of w) {

                    // LOAD TTID FROM FIREFOX GET WINDOW VALUE
                    if (opt.skip_load == false && windows_data[win.id] != undefined) {
                        b.windows[win.id] = Object.assign({}, windows_data[win.id]);
                    } else {
                        QuantumAppendWinTTId(win.id);
                    }

                    for (let tab of win.tabs) {
                        // LOAD TTID FROM FIREFOX GET TAB VALUE
                        if (opt.skip_load == false && tabs_data[tab.id] != undefined) {
                            b.tabs[tab.id] = Object.assign({}, tabs_data[tab.id]);
                            tabs_matched++;
                        } else {
                            QuantumAppendTabTTId(tab);
                        }
                        if (tab.active) b.windows[win.id].activeTabId = tab.id;
                    }
                }
                
                // OK, DONE, NOW REPLACE OLD PARENTS IDS WITH THIS SESSION IDS
                for (let tabId in b.tabs) {
                    if (b.tt_ids[b.tabs[tabId].parent] != undefined) {
                        b.tabs[tabId].parent = b.tt_ids[b.tabs[tabId].parent]; // is tab
                    } else {
                        b.tabs[tabId].parent = b.tabs[tabId].parent; // is not tab
                    }
                }

                // OK, SAME THING FOR ACTIVE TABS IN GROUPS
                for (let winId in b.windows) {
                    for (let group in b.windows[winId].groups) {
                        if (b.tt_ids[b.windows[winId].groups[group].active_tab] != undefined) b.windows[winId].groups[group].active_tab = b.tt_ids[b.windows[winId].groups[group].active_tab];
                        if (b.tt_ids[b.windows[winId].groups[group].prev_active_tab] != undefined) b.windows[winId].groups[group].prev_active_tab = b.tt_ids[b.windows[winId].groups[group].prev_active_tab];
                    }
                }

                if (opt.skip_load == false && tabs_matched < tabs_count*0.5) {
                    b.safe_mode = true;
                    SafeModeCheck();
                    // SAFE MODE IS DISABLED AFTER 10 MINUTES
                    setTimeout(function() {
                        b.safe_mode = false;
                    }, 600000);

                    if (opt.debug) pushlog("started in safe mode");
                }
                    
                b.bg_running = true;
                QuantumAutoSaveData();
                QuantumStartListeners();
                delete DefaultToolbar;
                delete DefaultTheme;
                delete DefaultPreferences;
                delete DefaultMenu;
                chrome.runtime.sendMessage({ command: "bg_started" });
                
                if (opt.debug) pushlog("QuantumStart, Current windows count is: " + w.length + "Current tabs count is: " + tabs_count + "Matching tabs: " + tabs_matched);
                
            }, 1000);
        });
    });
}

// save every second if there is anything to save obviously
async function QuantumAutoSaveData() {
    setInterval(function() {
        if (b.schedule_save > 1) b.schedule_save = 1;
        if (b.safe_mode == false && b.bg_running && b.schedule_save > 0 && Object.keys(b.tabs).length > 1) {
            chrome.windows.getAll({ windowTypes: ['normal'], populate: true }, function(w) {
                for (let win of w) {
                    if (b.windows[win.id] != undefined) {
                        if (b.windows[win.id].ttid != undefined && b.windows[win.id].group_bar != undefined && b.windows[win.id].search_filter != undefined && b.windows[win.id].active_shelf != undefined && b.windows[win.id].active_group != undefined && b.windows[win.id].groups != undefined && b.windows[win.id].folders != undefined) {
                            let windowData = Object.assign({}, b.windows[win.id]);
                            for (let groupId in b.windows[win.id].groups) {
                                if (b.tabs[b.windows[win.id].groups[groupId].active_tab]) windowData.groups[groupId].active_tab = b.tabs[b.windows[win.id].groups[groupId].active_tab].ttid;
                                if (b.tabs[b.windows[win.id].groups[groupId].prev_active_tab]) windowData.groups[groupId].prev_active_tab = b.tabs[b.windows[win.id].groups[groupId].prev_active_tab].ttid;
                            }
                            browser.sessions.setWindowValue(win.id, "TTdata", windowData);
                        }
                    } else {
                        QuantumAppendWinTTId(win.id);
                    }
                    
                    for (let tab of win.tabs) {
                        if (b.tabs[tab.id] != undefined) {
                            if (b.tabs[tab.id].ttid != undefined && b.tabs[tab.id].parent != undefined && b.tabs[tab.id].index != undefined && b.tabs[tab.id].expand != undefined) {
                                browser.sessions.setTabValue(tab.id, "TTdata", { ttid: b.tabs[tab.id].ttid, parent: (b.tabs[b.tabs[tab.id].parent] ? b.tabs[b.tabs[tab.id].parent].ttid : b.tabs[tab.id].parent), index: b.tabs[tab.id].index, expand: b.tabs[tab.id].expand });   
                            } else {
                                QuantumAppendTabTTId(tab);
                            }
                        }
                    }
                }
                b.schedule_save--;
            });
        }
        if (opt.debug == true) chrome.storage.local.set({ debug_log: b.debug });
    }, 1000);
}

function QuantumGenerateNewWindowID() {
    let newID = "";
    while (newID == "") {
        newID = "w_" + GenerateRandomID();
        for (let wId in b.windows) {
            if (wId == newID) newID = "";
        }
    }
    return newID;
}

function QuantumGenerateNewTabID() {
    let newID = "";
    while (newID == "") {
        newID = "t_" + GenerateRandomID();
        for (let tId in b.tabs) {
            if (tId == newID) newID = "";
        }
    }
    return newID;
}

function QuantumAppendTabTTId(tab) {
    let NewTTTabId = QuantumGenerateNewTabID();
    if (b.tabs[tab.id] != undefined) {
        b.tabs[tab.id].ttid = NewTTTabId;
    } else {
        b.tabs[tab.id] = { ttid: NewTTTabId, parent: (b.windows[tab.windowId] ? b.windows[tab.windowId].active_group : "tab_list"), index: tab.index, expand: "" };
    }
    b.tt_ids[NewTTTabId] = tab.id;
    return NewTTTabId;
}

function QuantumAppendWinTTId(windowId) {
    let NewTTWindowId = QuantumGenerateNewWindowID();
    if (b.windows[windowId] != undefined) {
        b.windows[windowId].ttid = NewTTWindowId;
    } else {
        b.windows[windowId] = { activeTabId: 0, ttid: NewTTWindowId, group_bar: opt.groups_toolbar_default, search_filter: "url", active_shelf: "", active_group: "tab_list", groups: { tab_list: { id: "tab_list", index: 0, active_tab: 0, prev_active_tab: 0, name: labels.ungrouped_group, font: "" } }, folders: {} };
    }
}


function QuantumStartListeners() {
    browser.browserAction.onClicked.addListener(function() {
        browser.sidebarAction.setPanel({ panel: (browser.extension.getURL("/sidebar.html")) });
        browser.sidebarAction.open();
    });
    chrome.tabs.onCreated.addListener(function(tab) {
        if (b.windows[tab.windowId] == undefined) {
            QuantumAppendWinTTId(tab.windowId);
        }
        let prevActiveTabId = b.windows[tab.windowId].activeTabId;
        let t = Promise.resolve(browser.sessions.getTabValue(tab.id, "TTdata")).then(function(TabData) {
            if (TabData != undefined) {
                b.tabs[tab.id] = Object.assign({}, TabData);
                let originalParent = b.tt_ids[TabData.parent] ? b.tt_ids[TabData.parent] : TabData.parent;
                let AfterId = undefined;
                let append = undefined;
                if (originalParent) {
                    let originalParentChildren = GetChildren(b.tabs, originalParent);
                    if (TabData.index > 0 && TabData.index < originalParentChildren.length) {
                        for (let i = TabData.index + 1; i < originalParentChildren.length; i++) { // shift next siblings indexes
                            b.tabs[originalParentChildren[i]].index += 1;
                        }
                        AfterId = originalParentChildren[TabData.index];
                    }
                    if (TabData.index == 0) {
                        append = false;
                    }
                    if (TabData.index > originalParentChildren.length) {
                        append = true;
                    }
                }
                chrome.runtime.sendMessage({ command: "tab_created", windowId: tab.windowId, tabId: tab.id, tab: tab, ParentId: originalParent, InsertAfterId: AfterId, Append: append });
            } else {
                QuantumAppendTabTTId(tab);
                chrome.tabs.get(tab.id, function(NewTab) { // get tab again as reported tab's url is empty! Also for some reason firefox sends tab with "active == false" even if tab is active (THIS IS POSSIBLY A NEW BUG IN FF 60!)
                    if (NewTab) {
                        OnMessageTabCreated(NewTab, prevActiveTabId);
                    }
                });
            }
        });
    });
    chrome.tabs.onAttached.addListener(function(tabId, attachInfo) {
        let oldId = tabId;
        chrome.tabs.get(oldId, function(tab) {
            ReplaceParents(oldId, tab.id);
            chrome.runtime.sendMessage({ command: "tab_attached", windowId: attachInfo.newWindowId, tab: tab, tabId: tab.id, ParentId: b.tabs[tab.id].parent });
            b.schedule_save++;
        });
    });

    chrome.tabs.onDetached.addListener(function(tabId, detachInfo) {
        chrome.runtime.sendMessage({ command: "tab_detached", windowId: detachInfo.oldWindowId, tabId: tabId });
    });

    chrome.tabs.onRemoved.addListener(function(tabId, removeInfo) {
        // if (b.windows[removeInfo.windowId].activeTabId == tabId) {
        // chrome.runtime.sendMessage({command: "switch_active_tab", windowId: removeInfo.windowId, tabId: tabId});
        // }
        let SiblingTabs = GetChildren(b.tabs, b.tabs[tabId].parent);
        let SiblingFolders = GetChildren(b.windows[removeInfo.windowId].folders, b.tabs[tabId].parent);
        UnshiftChildrenIndexes(SiblingTabs, b.tabs[tabId].index, SiblingFolders, removeInfo.windowId);
        if (b.EmptyTabs.indexOf(tabId) != -1) {
            b.EmptyTabs.splice(b.EmptyTabs.indexOf(tabId), 1);
        }
        setTimeout(function() {
            chrome.runtime.sendMessage({ command: "tab_removed", windowId: removeInfo.windowId, tabId: tabId });
        }, 5);
        delete b.tabs[tabId];
        b.schedule_save++;
    });
    chrome.tabs.onUpdated.addListener(function(tabId, changeInfo, tab) {
        if (changeInfo.pinned == true) {
            if (b.tabs[tabId]) {
                b.tabs[tabId].parent = "pin_list";
                b.schedule_save++;
            }
        }
        if (changeInfo.pinned == false) {
            if (b.tabs[tabId]) {
                b.tabs[tabId].parent = "tab_list";
                b.schedule_save++;
            }
        }
        if (changeInfo.url != undefined) { // if set to append when url changes and matches pre-set group
            if (tab.pinned == false) {
                if (opt.move_tabs_on_url_change == "always" || ((opt.move_tabs_on_url_change == "from_empty" || opt.move_tabs_on_url_change == "from_empty_b") && b.EmptyTabs.indexOf(tabId) != -1)) {
                    AppendTabToGroupOnRegexMatch(tabId, tab.windowId, changeInfo.url);
                }
            }
            if (changeInfo.url != b.newTabUrl && b.EmptyTabs.indexOf(tabId) != -1) {
                b.EmptyTabs.splice(b.EmptyTabs.indexOf(tabId), 1);
            }
        }
        if (changeInfo.title != undefined && !tab.active) {
            chrome.runtime.sendMessage({ command: "tab_attention", windowId: tab.windowId, tabId: tabId });
        }
        chrome.runtime.sendMessage({ command: "tab_updated", windowId: tab.windowId, tab: tab, tabId: tabId, changeInfo: changeInfo });
    });

    chrome.tabs.onReplaced.addListener(function(addedTabId, removedTabId) {
        chrome.tabs.get(addedTabId, function(tab) {
            if (addedTabId == removedTabId) {
                chrome.runtime.sendMessage({ command: "tab_updated", windowId: tab.windowId, tab: tab, tabId: tab.id, changeInfo: { status: tab.status, url: tab.url, title: tab.title, audible: tab.audible, mutedInfo: tab.mutedInfo } });
            } else {
                if (b.tabs[removedTabId]) {
                    b.tabs[addedTabId] = b.tabs[removedTabId];
                }
                ReplaceParents(tabId, tab.id);
                chrome.runtime.sendMessage({ command: "tab_removed", windowId: tab.windowId, tabId: removedTabId });
                chrome.runtime.sendMessage({ command: "tab_attached", windowId: tab.windowId, tab: tab, tabId: addedTabId, ParentId: b.tabs[addedTabId].parent });
                // delete ttid[b.tabs[removedTabId].ttid];
                // delete b.tabs[removedTabId];
            }
            setTimeout(function() {
                QuantumAppendTabTTId(tab);
                b.schedule_save++;
            }, 100);

        });
    });
    chrome.tabs.onActivated.addListener(function(activeInfo) {
        if (b.windows[activeInfo.windowId]) {
            b.windows[activeInfo.windowId].activeTabId = activeInfo.tabId;
        }
        chrome.runtime.sendMessage({ command: "tab_activated", windowId: activeInfo.windowId, tabId: activeInfo.tabId });
        b.schedule_save++;
    });
    chrome.windows.onCreated.addListener(function(window) {
        let win = Promise.resolve(browser.sessions.getWindowValue(window.id, "TTdata")).then(function(WindowData) {
            if (WindowData != undefined) {
                b.windows[window.id] = Object.assign({}, WindowData);
            } else {
                QuantumAppendWinTTId(window.id);
            }
            b.schedule_save++;
        });
    });
    chrome.windows.onRemoved.addListener(function(windowId) {
        delete b.windows[windowId];
        b.schedule_save++;
    });
}