BNPChrome.controller("PanelController", function PanelController($scope, toolbar, parse, $timeout) {

    const LOCAL_STORAGE = window.localStorage;
    const MAX_BODY_SIZE = 20000;
    const HOST = "https://leviolson.com"; // "http://localhost:3000"
    const CHANGELOG = {
        "What's New": {
            "v1.0.2:": {
                "Added Settings": HOST + "/posts/bnp-changelog#added-settings",
                "Auto URL Decoding": ""
            },
            "v1.0.1:": {
                "Panel Settings": HOST + "/posts/bnp-changelog#panel-settings",
                "Bugs": "squashed"
            },
            "v1.0.0:": {
                "Improved Search": HOST + "/posts/bnp-changelog#improved-search",
                "JSON Editor BUILT IN": HOST + "/posts/bnp-changelog#json-editor-built-in",
                "Vertical Chrome Panel": HOST + "/posts/bnp-changelog#vertical-chrome-panel",
                "Download JSON": HOST + "/posts/bnp-changelog#download-json"
            }
        }
    };
    const STORAGE_MAP = {
        'autoJSONParseDepthRes': 3,
        'autoJSONParseDepthReq': 6,
        'clearOnRefresh': false,
        'scrollToNew': true,
        'andfilter': false,
        'searchTerms': [],
        'oldSearchTerms': [],
    };


    $scope.autoJSONParseDepthRes = STORAGE_MAP.autoJSONParseDepthRes;
    $scope.autoJSONParseDepthReq = STORAGE_MAP.autoJSONParseDepthReq;
    $scope.clearOnRefresh        = STORAGE_MAP.clearOnRefresh;
    $scope.scrollToNew           = STORAGE_MAP.scrollToNew;
    $scope.andFilter             = STORAGE_MAP.andfilter;
    $scope.searchTerms           = STORAGE_MAP.searchTerms;
    $scope.oldSearchTerms        = STORAGE_MAP.oldSearchTerms;

    $scope.search = "";
    $scope.uniqueId = 100000;
    $scope.activeId = null;
    $scope.requests = {};
    $scope.masterRequests = [];
    $scope.filteredRequests = [];
    $scope.showAll = true;
    $scope.limitNetworkRequests = false;
    $scope.showOriginal = false;
    $scope.currentDetailTab = "tab-response";
    $scope.filter = "";
    $scope.editor = null;

    $scope.activeCookies = [];
    $scope.activeHeaders = [];
    $scope.activePostData = [];
    $scope.activeRequest = [];
    $scope.activeResponseData = [];
    $scope.activeResponseCookies = [];
    $scope.activeResponseHeaders = [];
    $scope.activeCode = null;

    $scope.init = function () {
        $("#tabs").tabs();

        _initChrome();

        _createToolbar();

        const options = {
            mode: 'view',
            modes: ['code', 'view', 'preview'],
            onEditable: function (node) {
                if (!node.path) {
                    // In modes code and text, node is empty: no path, field, or value
                    // returning false makes the text area read-only
                    return false;
                }
                return true;
            }
        };
        const response = document.getElementById('response-jsoneditor');
        const request = document.getElementById('request-jsoneditor');
        $scope.responseJsonEditor = new JSONEditor(response, options);
        $scope.requestJsonEditor = new JSONEditor(request, options);

        $timeout(() =>  {
            $scope.responseJsonEditor.set(CHANGELOG);
            $scope.responseJsonEditor.expandAll();
        });
    };

    const _initChrome = function () {
        for (const property in STORAGE_MAP) {
            try {
                let item = LOCAL_STORAGE.getItem(`bnp-${property}`);
                if (item) {
                    let retrieved = JSON.parse(item);
                    switch (typeof STORAGE_MAP[property]) {
                    case 'boolean':
                        $scope[property] = retrieved;
                        break;
                    default:
                        $scope[property] = retrieved || STORAGE_MAP[property];
                        break;
                    }
                } else {
                    $scope[property] = STORAGE_MAP[property];
                }
            } catch (e) {
                $scope[property] = STORAGE_MAP[property];
            }
            $scope.$watch(property, function(n, o) {
                if (n !== o) {
                    _setLocalStorage();
                }
            });
            console.debug(`Retrieving ${property}`, $scope[property]);
        }

        chrome.devtools.network.onRequestFinished.addListener(function (request) {
            // do not show requests to chrome extension resources
            if (request.request.url.startsWith("chrome-extension://")) {
                return;
            }
            $scope.handleRequest(request);
        });

        chrome.devtools.network.onNavigated.addListener(function (event) {
            // display a line break in the network logs to show page reloaded
            if ($scope.clearOnRefresh) {
                $scope.clear();
                return;
            }
            $scope.masterRequests.push({
                id: $scope.uniqueId,
                separator: true,
                event: event
            });
            $scope.uniqueId++;
            $scope.cleanRequests();
        });
    };

    const _filterRequests = function () {
        if (!$scope.searchTerms || $scope.searchTerms.length === 0) {
            $scope.filteredRequests = $scope.masterRequests;
            return;
        }
        // console.log("Filtering for: ", $scope.searchTerms);

        let negTerms = [];
        let posTerms = [];
        for (let term of $scope.searchTerms) {
            term = term.toLowerCase();
            if (term && term[0] === '-') negTerms.push(term.substring(1));
            else posTerms.push(term);
        }

        $scope.filteredRequests = $scope.masterRequests.filter(function (x) {
            if (x.separator) return true;
            for (let term of negTerms) {
                // if neg
                if (x && x.searchIndex && x.searchIndex.includes(term)) return false;
            }

            if ($scope.andFilter) {
                // AND condition
                for (let term of posTerms) {
                    // if pos
                    if (x && x.searchIndex && !x.searchIndex.includes(term)) {
                        return false;
                    }
                }
                return true;
            } else {
                // OR condition
                for (let term of posTerms) {
                    // if pos
                    if (x && x.searchIndex && x.searchIndex.includes(term)) {
                        return true;
                    }
                }
                return false;
            }
        });
    };

    $scope.toggleSearchType = function() {
        $scope.andFilter = !$scope.andFilter;
        _setLocalStorage();
        _filterRequests();
    };

    $scope.customSearch = function() {
        if (!$scope.searchTerms.includes($scope.search)) {
            $scope.searchTerms.push($scope.search);
            $scope.search = "";
            _setLocalStorage();
            _filterRequests();
        }
    };

    const _setLocalStorage = function() {
        // do some sort of comparison to searchTerms and oldSearchTerms to make sure there is only one.
        // although, now that I think about it... this comparison shouldn't be necessary... /shrug
        for (const property in STORAGE_MAP) {
            LOCAL_STORAGE.setItem(`bnp-${property}`, JSON.stringify($scope[property]));
            console.debug(`Saving ${property}`, $scope[property]);
        }
    };

    $scope.addSearchTerm = function(index) {
        $scope.searchTerms.push($scope.oldSearchTerms.splice(index, 1)[0]);
        _setLocalStorage();
        _filterRequests();
    };

    $scope.removeSearchTerm = function(index) {
        $scope.oldSearchTerms.push($scope.searchTerms.splice(index, 1)[0]);
        _setLocalStorage();
        _filterRequests();
    };

    $scope.deleteSearchTerm = function(index) {
        $scope.oldSearchTerms.splice(index, 1);
        _setLocalStorage();
    };

    $scope.handleRequest = function (har_entry) {
        $scope.addRequest(har_entry, har_entry.request.method, har_entry.request.url, har_entry.response.status, null);
    };

    const _createToolbar = function () {
        toolbar.createToggleButton(
            "embed",
            "Toggle JSON Parsing (See Panel Settings)",
            false,
            function () {
                // ga('send', 'event', 'button', 'click', 'Toggle JSON Parsing');
                $scope.$apply(function () {
                    $scope.showOriginal = !$scope.showOriginal;
                    $scope.selectDetailTab($scope.currentDetailTab);
                    // $scope.displayCode();
                });
            },
            true
        );
        toolbar.createButton("download3", "Download", false, function () {
            // ga('send', 'event', 'button', 'click', 'Download');
            $scope.$apply(function () {
                const panel = $scope.currentDetailTab;
                let blob;
                if (panel === "tab-response") {
                    blob = new Blob([JSON.parse(JSON.stringify($scope.activeCode, null, 4))], { type: "application/json;charset=utf-8" });
                    saveAs(blob, "export_response.json");
                } else {
                    try {
                        blob = new Blob([JSON.stringify($scope.activePostData)], { type: "application/json;charset=utf-8" });
                        saveAs(blob, "export_request.json");
                    } catch (e) {
                        console.log(e);
                    }

                }
            });
        });
        toolbar.createButton("blocked", "Clear", false, function () {
            // ga('send', 'event', 'button', 'click', 'Clear');
            $scope.$apply(function () {
                $scope.clear();
            });
        });

        $(".toolbar").replaceWith(toolbar.render());
    };

    $scope.addRequest = function (data, request_method, request_url, response_status) {
        $scope.$apply(function () {
            const requestId = data.id || $scope.uniqueId;
            $scope.uniqueId++;

            if (data.request != null) {
                data["request_data"] = $scope.createKeypairs(data.request);
                if (data.request.cookies != null) {
                    data.cookies = $scope.createKeypairsDeep(data.request.cookies);
                }
                if (data.request.headers != null) {
                    data.headers = $scope.createKeypairsDeep(data.request.headers);
                }
                if (data.request.postData != null) {
                    data.postData = $scope.createKeypairs(data.request.postData);
                }
            }
            if (data.response != null) {
                data["response_data"] = $scope.createKeypairs(data.response);
                data.response_data.response_body = "Loading " + requestId;
                if (data.response.cookies != null) {
                    data["response_cookies"] = $scope.createKeypairsDeep(data.response.cookies);
                }
                if (data.response.headers != null) {
                    data["response_headers"] = $scope.createKeypairsDeep(data.response.headers);
                }
            }

            data["request_method"] = request_method;
            if (request_url.includes("apexremote")) {
                try {
                    let text = data && data.request && data.request.postData && data.request.postData.text ? JSON.parse(data.request.postData.text) : "";
                    data["request_apex_type"] = text.data && typeof text.data[1] === "string" ? text.data[1] : JSON.stringify(text.data);
                    data["request_apex_method"] = text.method || "";
                } catch (e) {
                    console.debug("Error", e);
                }
            }
            data.request_url     = request_url;
            data.response_status = response_status;
            data['id']           = requestId;
            let ctObj            = data.response_headers.find(x => x.name == "Content-Type");
            data.content_type    = ctObj && ctObj.value || null;

            $scope.requests[requestId] = data; // master
            data.searchIndex = JSON.stringify(data.request).toLowerCase();
            $scope.masterRequests.push(data);

            data.getContent(function (content, encoding) {
                $scope.requests[requestId].response_data.response_body = content;
            });

            $scope.cleanRequests();
        });
    };

    $scope.cleanRequests = function () {
        if ($scope.limitNetworkRequests === true) {
            if ($scope.masterRequests.length >= 500) $scope.masterRequests.shift();
            const keys = Object.keys($scope.requests).reverse().slice(500);
            keys.forEach(function (key) {
                if ($scope.requests[key]) {
                    delete $scope.requests[key];
                }
            });
        }
        _filterRequests();
    };

    $scope.clear = function () {
        $scope.requests = {};
        $scope.activeId = null;
        $scope.masterRequests = [];
        $scope.filteredRequests = [];

        $scope.activeCookies = [];
        $scope.activeHeaders = [];
        $scope.activePostData = [];
        $scope.activeRequest = [];
        $scope.activeResponseData = [];
        $scope.activeResponseDataPreview = "";
        $scope.activeResponseCookies = [];
        $scope.activeResponseHeaders = [];
        $scope.activeCode = null;
    };

    $scope.setActive = function (requestId) {
        if (!$scope.requests[requestId]) {
            return;
        }
        $scope.activeId = requestId;

        $scope.activeCookies = $scope.requests[requestId].cookies;
        $scope.activeHeaders = $scope.requests[requestId].headers;
        $scope.activePostData = $scope.requests[requestId].postData;
        $scope.activeRequest = $scope.requests[requestId].request_data;
        $scope.activeResponseData = $scope.requests[requestId].response_data;
        $scope.activeResponseDataPreview = $scope.requests[requestId].response_data.response_body;
        $scope.activeResponseCookies = $scope.requests[requestId].response_cookies;
        $scope.activeResponseHeaders = $scope.requests[requestId].response_headers;
        $scope.activeCode = $scope.requests[requestId].response_data.response_body;
    };

    $scope.getClass = function (requestId, separator) {
        if (separator) return "separator";
        if (requestId === $scope.activeId) {
            return "selected";
        } else {
            return "";
        }
    };
    $scope.titleIfSeparator = function(separator) {
        if (separator)
            return "Page reloaded here";
        return "";
    };

    $scope.createKeypairs = function (data) {
        let keypairs = [];
        if (!(data instanceof Object)) {
            return keypairs;
        }

        $.each(data, function (key, value) {
            if (!(value instanceof Object)) {
                keypairs.push({
                    name: key,
                    value: value
                });
            }
        });

        return keypairs;
    };

    $scope.createKeypairsDeep = function (data) {
        let keypairs = [];

        if (!(data instanceof Object)) {
            return keypairs;
        }

        $.each(data, function (key, value) {
            keypairs.push({
                name: value.name,
                value: value.value
            });
        });

        return keypairs;
    };

    $scope.$watch("activeCode", function (newVal, oldVal) {
        if (newVal === null) {
            $scope.responseJsonEditor.set(null);
            $scope.requestJsonEditor.set(null);
        }
        $scope.displayCode("responseJsonEditor", $scope.activeCode, $scope.autoJSONParseDepthRes);
        $scope.displayCode("requestJsonEditor", $scope.activePostData, $scope.autoJSONParseDepthReq);
    });

    $scope.selectDetailTab = function (tabId, external) {
        $scope.currentDetailTab = tabId;
        if (external) {
            $("#tabs a[href='#" + tabId + "']").trigger("click");
        }
        if (tabId === "tab-response") {
            $scope.displayCode("responseJsonEditor", $scope.activeCode, $scope.autoJSONParseDepthRes);
        }
        if (tabId === "tab-request") {
            $scope.displayCode("requestJsonEditor", $scope.activePostData, $scope.autoJSONParseDepthReq);
        }
    };

    $scope.displayCode = function (elementId, input, depth) {
        if (input) {
            let content;
            if ($scope.showOriginal) {
                content = parse(input, 0, 1);
            } else {
                content = parse(input, 0, depth);
            }

            if (typeof input === 'object' || Array.isArray(input)) {
                // JSON
                $scope[elementId].setMode("view");
                $scope[elementId].set(content);
            } else {
                // Something else
                try {
                    let json = JSON.parse(input);
                    $scope[elementId].setMode("view");
                    $scope[elementId].set(content);
                } catch (e) {
                    $scope[elementId].setMode("code");
                    $scope[elementId].set(content);
                }
            }
            let bodySize;
            if (elementId === "responseJsonEditor") {
                bodySize = $scope.activeResponseData.find(x => x.name === "bodySize");
                if (bodySize && bodySize.value < MAX_BODY_SIZE) { // an arbitrary number that I picked so there is HUGE lag
                    if ($scope[elementId].getMode() === 'tree' || $scope[elementId].getMode() === 'view')
                        $scope[elementId].expandAll();
                }
            } else if (elementId === "requestJsonEditor") {
                bodySize = $scope.activeRequest.find(x => x.name === "bodySize");
                if (bodySize && bodySize.value < MAX_BODY_SIZE) {
                    if ($scope[elementId].getMode() === 'tree' || $scope[elementId].getMode() === 'view')
                        $scope[elementId].expandAll();
                }
            }
        } else {
            $scope[elementId].set(null);
            $scope[elementId].expandAll();
        }
    };
});
