/******************************************************************************
 * This file fetches the quick shop raw data from the massive link minecraft
 * shops endpoint and then parses that out into objects which are then
 * displayed in a table on the page.
 * 
 * @author Brian Hair
 * * @author Kim Hair
 *****************************************************************************/


// Global defines
const DEBUG_OUTPUT = false;

/*
 * credit to Alec Lomax: https://lowmess.com/blog/fetch-with-timeout
 */
const fetchWithTimeout = (uri, options = {}, time = 5000) => {
    // Lets set up our `AbortController`, and create a request options object
    // that includes the controller's `signal` to pass to `fetch`.
    const controller = new AbortController()
    const config = { ...options, signal: controller.signal }

    // Set a timeout limit for the request using `setTimeout`. If the body
    // of this timeout is reached before the request is completed, it will
    // be cancelled.
    const timeout = setTimeout(() => {
        controller.abort()
    }, time)

    return fetch(uri, config)
        .then((response) => {
            // Because _any_ response is considered a success to `fetch`, we
            // need to manually check that the response is in the 200 range.
            // This is typically how I handle that.
            if (!response.ok) {
                throw new Error(`${response.status}: ${response.statusText}`)
            }

            return response
        })
        .catch((error) => {
            // When we abort our `fetch`, the controller conveniently throws
            // a named error, allowing us to handle them separately from
            // other errors.
            if (error.name === 'AbortError') {
                throw new Error('Response timed out')
            }

            throw new Error(error.message)
        })
}

/*
 * Get the meta value out of an EO.
 * attribute is which object in the _meta, type is the sub attribute
 */
function getEOMetaValue(eo, attribute, type) {
    if (eo._meta.hasOwnProperty(attribute)) {
        if (eo._meta[attribute].hasOwnProperty(type)) {
            return eo._meta[attribute][type].value;
        }
    }
    return "undefined";
}

/*
 * Get the display name label of a value if it exists, otherwise return the
 * internal name.
 */
function getEOLabel(eo, attribute) {
    return getEOMetaValue(eo, attribute, 'label');
}

/*
 * Get the direct EO value.
 */
function getEOValue(eo, attribute) {
    if (eo.hasOwnProperty(attribute)) {
        return eo[attribute];
    }
    return "undefined";
}


/*
 * Get the raw list of shops
 */
async function getShops() {
    let url = '/minecraft/neweden/link/v1/shops/read';
    try {
        const response = await fetchWithTimeout(url, {}, 5000);
        return await response.text();
    } catch (error) {
        console.log(error);
    }
}

/*
 * Parse and render the shops to the screen.
 */
async function renderShops() {
    // Get the list of shops from the web endpoint.
    const shops = await getShops();

    // Split the giant string of text into an array entry per line from the RAW
    // shop data.
    const shop_lines = shops.split(/\r\n|\n/);

    // Iterate through each line in our array of lines of JSON.
    $.each(shop_lines, function(index, value) {
        if (!value.trim()) {
            return;
        }

        if (DEBUG_OUTPUT) {
            console.log("RAW SHOP: " + value);
        }
        

        // This magic line parses the plain string into a JSON object.
        const shop = JSON.parse(value);

        // Start building up the HTML table row <tr> for current shop.
        let htmlSegment = `
            <tr id="shop-${index}" class="shop">
            <td class="colItem"><img src="assets/minecraft/textures/${getEOValue(shop, "item")}.png" alt="${getEOValue(shop, "item")}" style="width:16px;height:16px;"> ${getEOLabel(shop, "item")}</td>
            <td class="colCurrencyPrefix">${getEOMetaValue(shop, "price", "currencyPrefix")}</td>
            <td class="colPrice">${getEOLabel(shop, "price")}</td>
            <td class="colType">${getEOValue(shop, "type")}</td>`;

        // If there are enchantments on the item, add them in.
        // Otherwise insert an empty cell.
        if (shop.hasOwnProperty('enchants')) {
            htmlSegment += `<td class="colEnchants">`;
            $.each(shop.enchants, function(index, enchant) {
                if (index > 0) {
                    htmlSegment += `<br>`;
                }
                htmlSegment += `
                    ${getEOLabel(enchant, "name")} ${getEOValue(enchant, "level")}`;
            });
            htmlSegment += `</td>`;
        } else {
            htmlSegment += `<td></td>`;
        }

        // Add the rest of the columns for the row.
        htmlSegment += `
            <td class="colOwner">${getEOLabel(shop, "owner")}</td>
            <td class="colCoordinates"><a href="/minecraft/neweden/dynmap/?worldname=world&mapname=flat&zoom=6&x=${getEOValue(shop, "x")}&y=${getEOValue(shop, "y")}&z=${getEOValue(shop, "z")}&desc=${encodeURIComponent(getEOLabel(shop, "item"))}" target="_blank">${getEOValue(shop, "x")} / ${getEOValue(shop, "y")} / ${getEOValue(shop, "z")}</a></td>
            <td class="colWorld">${getEOValue(shop, "world")}</td>`;

        // Lastly, append the <tr></tr> into the table body
        $('#quickShopsTableBody').append(htmlSegment);
    });

    // Pull in fancy table formatting: https://datatables.net/
    $('#quickShops').DataTable( {
        "autoWidth": false,
        "columns": [
            {"width":"auto"},
            {"width":"5px"},
            {"width":"52px"},
            {"width":"auto"},
            {"width":"auto"},
            {"width":"auto"},
            {"width":"auto"},
            {"width":"auto"}
        ],
        buttons: [
            { extend: 'print', exportOptions:
                { columns: ':visible' }
            },
            { extend: 'csv', exportOptions:
                 { columns: ':visible' }
            },
            { extend: 'pdf', exportOptions:
                  { columns: ':visible' }
            },
            {
                extend: 'colvis',
                postfixButtons: [ 'colvisRestore' ],
                columns: ':not(.noVis)'
            }
        ],
        columnDefs: [
            { orderable: false, targets: [ 1, 4, 6 ]},
            { className: "dt-body-center", targets: [0,1,3,4,5,6,7]},
            { className: "noVis", targets: [0,1,2]}
        ],
        colReorder: {
            enable: true,
            fixedColumnsLeft: 3,
            realtime: false
        },
        dom: '<"butt-wrapper"Blf>rt<"buttum"ip>',
        fixedHeader: {
            header: true
        },
        "lengthMenu": [ [50, 100, -1], [50, 100, "ALL"] ],
        "orderClasses": false,
        rowGroup: true,
        rowId: 'id',
        "scrollCollapse": true,
        searchDelay: 350
    } );

    // Finally show the table and footer now that the dynamic content is loaded.
    $('#quickShops').removeAttr('hidden');
    $('footer').removeAttr('hidden');

    // Why is this here? Well, because otherwise the fixed header doesn't be
    // stuck to the top like we expect in the datatable. This forces its hand.
    $(window).trigger('resize');
}

// Execute the shop insertion
renderShops();
