// Copyright (C) 2021 Leigh Morresi (dgtlmoon@gmail.com)
// All rights reserved.
// yes - this is really a hack, if you are a front-ender and want to help, please get in touch!

let runInClearMode = false;

$(document).ready(() => {
    let currentSelections = [];
    let currentSelection = null;
    let appendToList = false;
    let c, xctx, ctx;
    let xScale = 1, yScale = 1;
    let selectorImage, selectorImageRect, selectorData;


    // Global jQuery selectors with "Elem" appended
    const $selectorCanvasElem = $('#selector-canvas');
    const $includeFiltersElem = $("#include_filters");
    const $selectorBackgroundElem = $("img#selector-background");
    const $selectorCurrentXpathElem = $("#selector-current-xpath span");
    const $fetchingUpdateNoticeElem = $('.fetching-update-notice');
    const $selectorWrapperElem = $("#selector-wrapper");

    // Color constants
    const FILL_STYLE_HIGHLIGHT = 'rgba(205,0,0,0.35)';
    const FILL_STYLE_GREYED_OUT = 'rgba(205,205,205,0.95)';
    const STROKE_STYLE_HIGHLIGHT = 'rgba(255,0,0, 0.9)';
    const FILL_STYLE_REDLINE = 'rgba(255,0,0, 0.1)';
    const STROKE_STYLE_REDLINE = 'rgba(225,0,0,0.9)';

    $('#visualselector-tab').click(() => {
        $selectorBackgroundElem.off('load');
        currentSelections = [];
        bootstrapVisualSelector();
    });

    function clearReset() {
        ctx.clearRect(0, 0, c.width, c.height);

        if ($includeFiltersElem.val().length) {
            alert("Existing filters under the 'Filters & Triggers' tab were cleared.");
        }
        $includeFiltersElem.val('');

        currentSelections = [];

        // Means we ignore the xpaths from the scraper marked as sel.highlight_as_custom_filter (it matched a previous selector)
        runInClearMode = true;

        highlightCurrentSelected();
    }

    function splitToList(v) {
        return v.split('\n').map(line => line.trim()).filter(line => line.length > 0);
    }

    function sortScrapedElementsBySize() {
        // Sort the currentSelections array by area (width * height) in descending order
        selectorData['size_pos'].sort((a, b) => {
            const areaA = a.width * a.height;
            const areaB = b.width * b.height;
            return areaB - areaA;
        });
    }

    $(document).on('keydown keyup', (event) => {
        if (event.code === 'ShiftLeft' || event.code === 'ShiftRight') {
            appendToList = event.type === 'keydown';
        }

        if (event.type === 'keydown') {
            if ($selectorBackgroundElem.is(":visible") && event.key === "Escape") {
                clearReset();
            }
        }
    });

    $('#clear-selector').on('click', () => {
        clearReset();
    });
    // So if they start switching between visualSelector and manual filters, stop it from rendering old filters
    $('li.tab a').on('click', () => {
        runInClearMode = true;
    });

    if (!window.location.hash || window.location.hash !== '#visualselector') {
        $selectorBackgroundElem.attr('src', '');
        return;
    }

    bootstrapVisualSelector();

    function bootstrapVisualSelector() {
        $selectorBackgroundElem
            .on("error", () => {
                $fetchingUpdateNoticeElem.html("<strong>Ooops!</strong> The VisualSelector tool needs at least one fetched page, please unpause the watch and/or wait for the watch to complete fetching and then reload this page.")
                    .css('color', '#bb0000');
                $('#selector-current-xpath, #clear-selector').hide();
            })
            .on('load', () => {
                console.log("Loaded background...");
                c = document.getElementById("selector-canvas");
                xctx = c.getContext("2d");
                ctx = c.getContext("2d");
                fetchData();
                $selectorCanvasElem.off("mousemove mousedown");
            })
            .attr("src", screenshot_url);

        let s = `${$selectorBackgroundElem.attr('src')}?${new Date().getTime()}`;
        $selectorBackgroundElem.attr('src', s);
    }

    function alertIfFilterNotFound() {
        let existingFilters = splitToList($includeFiltersElem.val());
        let sizePosXpaths = selectorData['size_pos'].map(sel => sel.xpath);

        for (let filter of existingFilters) {
            if (!sizePosXpaths.includes(filter)) {
                alert(`One or more of your existing filters was not found and will be removed when a new filter is selected.`);
                break;
            }
        }
    }

    function fetchData() {
        $fetchingUpdateNoticeElem.html("Fetching element data..");

        $.ajax({
            url: watch_visual_selector_data_url,
            context: document.body
        }).done((data) => {
            $fetchingUpdateNoticeElem.html("Rendering..");
            selectorData = data;
            sortScrapedElementsBySize();
            console.log(`Reported browser width from backend: ${data['browser_width']}`);

            // Little sanity check for the user, alert them if something missing
            alertIfFilterNotFound();

            setScale();
            reflowSelector();
            $fetchingUpdateNoticeElem.fadeOut();
        });
    }

    function updateFiltersText() {
        // Assuming currentSelections is already defined and contains the selections
        let uniqueSelections = new Set(currentSelections.map(sel => (sel[0] === '/' ? `xpath:${sel.xpath}` : sel.xpath)));

        if (currentSelections.length > 0) {
            // Convert the Set back to an array and join with newline characters
            let textboxFilterText = Array.from(uniqueSelections).join("\n");
            $includeFiltersElem.val(textboxFilterText);
        }
    }

    function setScale() {
        $selectorWrapperElem.show();
        selectorImage = $selectorBackgroundElem[0];
        selectorImageRect = selectorImage.getBoundingClientRect();

        $selectorCanvasElem.attr({
            'height': selectorImageRect.height,
            'width': selectorImageRect.width
        });
        $selectorWrapperElem.attr('width', selectorImageRect.width);
        $('#visual-selector-heading').css('max-width', selectorImageRect.width + "px")

        xScale = selectorImageRect.width / selectorImage.naturalWidth;
        yScale = selectorImageRect.height / selectorImage.naturalHeight;

        ctx.strokeStyle = STROKE_STYLE_HIGHLIGHT;
        ctx.fillStyle = FILL_STYLE_REDLINE;
        ctx.lineWidth = 3;
        console.log("Scaling set  x: " + xScale + " by y:" + yScale);
        $("#selector-current-xpath").css('max-width', selectorImageRect.width);
    }

    function reflowSelector() {
        $(window).resize(() => {
            setScale();
            highlightCurrentSelected();
        });

        setScale();

        console.log(selectorData['size_pos'].length + " selectors found");

        let existingFilters = splitToList($includeFiltersElem.val());

        selectorData['size_pos'].forEach(sel => {
            if ((!runInClearMode && sel.highlight_as_custom_filter) || existingFilters.includes(sel.xpath)) {
                console.log("highlighting " + c);
                currentSelections.push(sel);
            }
        });


        highlightCurrentSelected();
        updateFiltersText();

        $selectorCanvasElem.bind('mousemove', handleMouseMove.debounce(5));
        $selectorCanvasElem.bind('mousedown', handleMouseDown.debounce(5));
        $selectorCanvasElem.bind('mouseleave', highlightCurrentSelected.debounce(5));

        function handleMouseMove(e) {
            if (!e.offsetX && !e.offsetY) {
                const targetOffset = $(e.target).offset();
                e.offsetX = e.pageX - targetOffset.left;
                e.offsetY = e.pageY - targetOffset.top;
            }

            ctx.fillStyle = FILL_STYLE_HIGHLIGHT;

            selectorData['size_pos'].forEach(sel => {
                if (e.offsetY > sel.top * yScale && e.offsetY < sel.top * yScale + sel.height * yScale &&
                    e.offsetX > sel.left * yScale && e.offsetX < sel.left * yScale + sel.width * yScale) {
                    setCurrentSelectedText(sel.xpath);
                    drawHighlight(sel);
                    currentSelections.push(sel);
                    currentSelection = sel;
                    highlightCurrentSelected();
                    currentSelections.pop();
                }
            })
        }


        function setCurrentSelectedText(s) {
            $selectorCurrentXpathElem[0].innerHTML = s;
        }

        function drawHighlight(sel) {
            ctx.strokeRect(sel.left * xScale, sel.top * yScale, sel.width * xScale, sel.height * yScale);
            ctx.fillRect(sel.left * xScale, sel.top * yScale, sel.width * xScale, sel.height * yScale);
        }

        function handleMouseDown() {
            // If we are in 'appendToList' mode, grow the list, if not, just 1
            currentSelections = appendToList ? [...currentSelections, currentSelection] : [currentSelection];
            highlightCurrentSelected();
            updateFiltersText();
        }

    }

    function highlightCurrentSelected() {
        xctx.fillStyle = FILL_STYLE_GREYED_OUT;
        xctx.strokeStyle = STROKE_STYLE_REDLINE;
        xctx.lineWidth = 3;
        xctx.clearRect(0, 0, c.width, c.height);

        currentSelections.forEach(sel => {
            //xctx.clearRect(sel.left * xScale, sel.top * yScale, sel.width * xScale, sel.height * yScale);
            xctx.strokeRect(sel.left * xScale, sel.top * yScale, sel.width * xScale, sel.height * yScale);
        });
    }
});