Subversion Repositories cheapmusic

Rev

Rev 158 | Blame | Compare with Previous | Last modification | View Log | RSS feed

<?php
use Fuse\Fuse;
include_once ('php/clsLibGTIN.php');
include_once ('php/exchangeRates.php');
include_once ('php/countryCodes.php');
include_once ('php/constants.php');
include_once ('php/ebay.php');
include_once ('php/discogs.php');
include_once ('php/linkshare.php');
include_once ('php/cjaffiliate.php');
include_once ('php/walmart.php');
include_once ('php/itunes.php');
include_once ('php/amazon.php');
include_once ('php/amazon_scrape.php');
include_once ('php/impact.php');
include_once ('php/sessions_db.php');
include_once ('php/media.php');
include_once ('php/class.html.php');
include_once ('php/htmlTools.php');

error_reporting(E_ALL);

// search
function performSearch($noDiscogs = false) {
    $currentMd5SearchTerm = md5SearchTerm($noDiscogs);
    if (isset($_SESSION['md5LastSearch']) && $currentMd5SearchTerm == $_SESSION['md5LastSearch']) {
        return;
    }
    $_SESSION['md5LastSearch'] = $currentMd5SearchTerm;

    updatePbFile(true, "Start");

    getGeoLocation();

    if ($noDiscogs === false) {
        findDiscogsMaster();
    }
    updatePbFile(false, "Discogs");

    $_SESSION["resultArr"] = [];
    expireSearchCache();
    $_SESSION["resultArr"] = searchAll();

    verifyResultArr();

    $_SESSION["resultArr"] = applySearchFilter($_SESSION["resultArr"]);

    //echo "<pre>";print_r($_SESSION["resultArr"]);echo "</pre>";
    $_SESSION["lowestPrice"]["Used"] = findLowestCondition("Used");
    $_SESSION["lowestPrice"]["New"] = findLowestCondition("New");
    $_SESSION["lowestPrice"]["CD"] = findLowestMediaType("CD");
    $_SESSION["lowestPrice"]["Record"] = findLowestMediaType("Record");
    $_SESSION["lowestPrice"]["Digital"] = findLowestMediaType("Digital");
    $_SESSION["lowestPrice"]["Book"] = findLowestMediaType("Book");
    $_SESSION["lowestPrice"]["All"] = 0.00;
    if (array_sum($_SESSION["lowestPrice"]) > 0) {
        $_SESSION["lowestPrice"]["All"] = minNotNull($_SESSION["lowestPrice"]);
    }

    $aiVal = saveSearchResult($noDiscogs);

    updatePbFile(true, "End:$aiVal");
}

// search for items on all sites
function searchAll($batchFlag = false) {
    $searchKey = $_SESSION["searchTerm"];
    $arr = [];
    if ($_SESSION["filterCondition"]["New"]) {
        get_vendor($arr, 'get_ebay', $searchKey, constant("NEW"));
    }
    if (!$batchFlag) { updatePbFile(false, "eBay New"); }
    if ($_SESSION["filterCondition"]["New"]) {
        get_vendor($arr, 'get_linkshare', $searchKey, constant("NEW"));
    }
    if (!$batchFlag) { updatePbFile(false, "Linkshare"); }
    if ($_SESSION["filterCondition"]["New"]) {
        get_vendor($arr, 'get_cjaffiliate', $searchKey, constant("NEW"));
    }
    if (!$batchFlag) { updatePbFile(false, "CJ Affiliate"); }
    if ($_SESSION["filterCondition"]["New"]) {
        get_vendor($arr, 'get_walmart', $searchKey, constant("NEW"));
    }
    if (!$batchFlag) { updatePbFile(false, "Walmart"); }
    if ($_SESSION["filterCondition"]["New"]) {
        get_vendor($arr, 'get_itunes', $searchKey, constant("NEW"));
    }
    if (!$batchFlag) { updatePbFile(false, "iTunes"); }

    $cntArr = count($arr);
    get_vendor($arr, 'get_amazon', $searchKey, constant("NEW"));
    if (!$batchFlag) { updatePbFile(false, "Amazon API"); }
    if ($cntArr == count($arr)) {
        get_vendor($arr, 'get_amazon_scrape', $searchKey, constant("NEW"));
    }
    if (!$batchFlag) { updatePbFile(false, "Amazon Scrape"); }

    get_vendor($arr, 'get_impact', $searchKey, constant("NEW"));
    if (!$batchFlag) { updatePbFile(false, "Impact"); }

    if ($_SESSION["filterCondition"]["Used"]) {
        get_vendor($arr, 'get_ebay', $searchKey, constant("USED"));
    }
    if (!$batchFlag) { updatePbFile(false, "eBay Used"); }

//echo "<pre>";print_r($arr);echo "</pre";

    $arr = applyExchangeRates($arr);
    usort($arr, 'compare_price');

    return $arr;
}

// Search and merge
function get_vendor(&$arr, $func, $searchKey, $condition) {
    $arrTemp = $func($searchKey, $condition);
    foreach($arrTemp as $value) {
        $arr[] = $value;
    }
}

// delete results from array that do not match the search filters
function applySearchFilter($arr) {
    unset($_SESSION['AdditionalFilterCounters']);
    unset($_SESSION['AdditionalFilters']);
    foreach ($arr as $key => $row) {
        if (!$_SESSION["filterMediaType"][$row["MediaType"]] || !$_SESSION["filterCondition"][$row["Condition"]]) {
            unset($arr[$key]);
        } else {
            if (isset($_SESSION['AdditionalFilterCounters']['Condition']['All'])) {
                $_SESSION['AdditionalFilterCounters']['Condition']['All']++;
            } else {
                $_SESSION['AdditionalFilterCounters']['Condition']['All'] = 1;
            }

            if (isset($_SESSION['AdditionalFilterCounters']['Merchant'][$row['Merchant']])) {
                $_SESSION['AdditionalFilterCounters']['Merchant'][$row['Merchant']]++;
            } else {
                $_SESSION['AdditionalFilterCounters']['Merchant'][$row['Merchant']] = 1;
                $_SESSION['AdditionalFilters']['Merchant'][$row['Merchant']] = true;
            }

            if (!empty($row['SellerName'])) {
                if (isset($_SESSION['AdditionalFilterCounters']['Seller'][$row['SellerName']])) {
                    $_SESSION['AdditionalFilterCounters']['Seller'][$row['SellerName']]++;
                } else {
                    $_SESSION['AdditionalFilterCounters']['Seller'][$row['SellerName']] = 1;
                    $_SESSION['AdditionalFilters']['Seller'][$row['SellerName']] = true;
                }
            }

            if (isset($_SESSION['AdditionalFilterCounters']['Condition'][$row['Condition']])) {
                $_SESSION['AdditionalFilterCounters']['Condition'][$row['Condition']]++;
            } else {
                $_SESSION['AdditionalFilterCounters']['Condition'][$row['Condition']] = 1;
                $_SESSION['AdditionalFilters']['Condition'][$row['Condition']] = true;
            }

            if (isset($_SESSION['AdditionalFilterCounters']['MediaType'][$row['MediaType']])) {
                $_SESSION['AdditionalFilterCounters']['MediaType'][$row['MediaType']]++;
            } else {
                $_SESSION['AdditionalFilterCounters']['MediaType'][$row['MediaType']] = 1;
                $_SESSION['AdditionalFilters']['MediaType'][$row['MediaType']] = true;
            }

            if (isset($_SESSION['AdditionalFilterCounters']['DetailCondition'][$row['DetailCondition']])) {
                $_SESSION['AdditionalFilterCounters']['DetailCondition'][$row['DetailCondition']]++;
            } else {
                $_SESSION['AdditionalFilterCounters']['DetailCondition'][$row['DetailCondition']] = 1;
                $_SESSION['AdditionalFilters']['DetailCondition'][$row['DetailCondition']] = true;
            }

            if (isset($_SESSION['AdditionalFilterCounters']['ShippingFrom'][$row['Country']])) {
                $_SESSION['AdditionalFilterCounters']['ShippingFrom'][$row['Country']]++;
            } else {
                $_SESSION['AdditionalFilterCounters']['ShippingFrom'][$row['Country']] = 1;
                $_SESSION['AdditionalFilters']['ShippingFrom'][$row['Country']] = true;
            }
        }
    }

    if (isset($_SESSION['AdditionalFilters']['Merchant'])) {
        ksort($_SESSION['AdditionalFilters']['Merchant'], SORT_NATURAL | SORT_FLAG_CASE);
    }

    if (isset($_SESSION['AdditionalFilters']['Seller'])) {
        ksort($_SESSION['AdditionalFilters']['Seller'], SORT_NATURAL | SORT_FLAG_CASE);
    }

    if (isset($_SESSION['AdditionalFilters']['Condition'])) {
        ksort($_SESSION['AdditionalFilters']['Condition'], SORT_NATURAL | SORT_FLAG_CASE);
    }

    if (isset($_SESSION['AdditionalFilters']['DetailCondition'])) {
        ksort($_SESSION['AdditionalFilters']['DetailCondition'], SORT_NATURAL | SORT_FLAG_CASE);
    }

    if (isset($_SESSION['AdditionalFilters']['ShippingFrom'])) {
        ksort($_SESSION['AdditionalFilters']['ShippingFrom'], SORT_NATURAL | SORT_FLAG_CASE);
    }

    if (isset($_SESSION['AdditionalFilters']['MediaType'])) {
        ksort($_SESSION['AdditionalFilters']['MediaType'], SORT_NATURAL | SORT_FLAG_CASE);
    }

    return $arr;
}

// filter view result table $_SESSION["resultArr"] for detailed filter selection
function detailFilterResults($selArr) {
    if (!empty($selArr['filterCondition'])) {
        foreach($_SESSION['AdditionalFilters']['Condition'] as $key => $value) {
            $_SESSION['AdditionalFilters']['Condition'][$key] = false;
        }
        if (!is_array($selArr['filterCondition'])) { $selArr['filterCondition'] = [ $selArr['filterCondition'] ];}
        foreach($selArr['filterCondition'] as $value) {
            $_SESSION['AdditionalFilters']['Condition'][$value] = true;
        }
    } else {
        $selArr['filterCondition'] = [];
        if (!empty($_SESSION['AdditionalFilters']['Condition'])) {
            foreach($_SESSION['AdditionalFilters']['Condition'] as $key => $value) {
                if ($value) {
                    $selArr['filterCondition'][] = $key;
                }
            }
        }
    }

    if (!empty($selArr['filterMediaType'])) {
        foreach($_SESSION['AdditionalFilters']['MediaType'] as $key => $value) {
            $_SESSION['AdditionalFilters']['MediaType'][$key] = false;
        }
        if (!is_array($selArr['filterMediaType'])) { $selArr['filterMediaType'] = [ $selArr['filterMediaType'] ];}
        foreach($selArr['filterMediaType'] as $value) {
            $_SESSION['AdditionalFilters']['MediaType'][$value] = true;
        }
    } else {
        $selArr['filterMediaType'] = [];
        if (!empty($_SESSION['AdditionalFilters']['MediaType'])) {
            foreach($_SESSION['AdditionalFilters']['MediaType'] as $key => $value) {
                if ($value) {
                    $selArr['filterMediaType'][] = $key;
                }
            }
        }
    }

    if (!empty($selArr['filterShipFrom'])) {
        foreach($_SESSION['AdditionalFilters']['ShippingFrom'] as $key => $value) {
            $_SESSION['AdditionalFilters']['ShippingFrom'][$key] = false;
        }
        if (!is_array($selArr['filterShipFrom'])) { $selArr['filterShipFrom'] = [ $selArr['filterShipFrom'] ];}
        foreach($selArr['filterShipFrom'] as $value) {
            $_SESSION['AdditionalFilters']['ShippingFrom'][$value] = true;
        }
    } else {
        $selArr['filterShipFrom'] = [];
        if (!empty($_SESSION['AdditionalFilters']['ShippingFrom'])) {
            foreach($_SESSION['AdditionalFilters']['ShippingFrom'] as $key => $value) {
                if ($value) {
                    $selArr['filterShipFrom'][] = $key;
                }
            }
        }
    }

    if (!empty($selArr['filterMerchant'])) {
        foreach($_SESSION['AdditionalFilters']['Merchant'] as $key => $value) {
            $_SESSION['AdditionalFilters']['Merchant'][$key] = false;
        }
        if (!is_array($selArr['filterMerchant'])) { $selArr['filterMerchant'] = [ $selArr['filterMerchant'] ];}
        foreach($selArr['filterMerchant'] as $value) {
            $_SESSION['AdditionalFilters']['Merchant'][$value] = true;
        }
    } else {
        $selArr['filterMerchant'] = [];
        if (!empty($_SESSION['AdditionalFilters']['Merchant'])) {
            foreach($_SESSION['AdditionalFilters']['Merchant'] as $key => $value) {
                if ($value) {
                    $selArr['filterMerchant'][] = $key;
                }
            }
        }
    }

    foreach ($_SESSION["resultArr"] as & $row) {
        $row["Show"] = true;

        if (!in_array($row["Condition"], $selArr['filterCondition']) ||
            !in_array($row["MediaType"], $selArr['filterMediaType']) ||
            !in_array($row["Merchant"], $selArr['filterMerchant']) ||
            !in_array($row["Country"], $selArr['filterShipFrom'])) {
            $row["Show"] = false;
        }
    }
}

function resetDetailFilter() {
    if (isset($_SESSION['AdditionalFilters']['Merchant'])) {
        foreach($_SESSION['AdditionalFilters']['Merchant'] as $key => $field) {
            $_SESSION['AdditionalFilters']['Merchant'][$key] = true;
        }
    }

    if (isset($_SESSION['AdditionalFilters']['Seller'])) {
        foreach($_SESSION['AdditionalFilters']['Seller'] as $key => $field) {
            $_SESSION['AdditionalFilters']['Seller'][$key] = true;
        }
    }

    if (isset($_SESSION['AdditionalFilters']['Condition'])) {
        foreach($_SESSION['AdditionalFilters']['Condition'] as $key => $field) {
            $_SESSION['AdditionalFilters']['Condition'][$key] = true;
        }
    }

    if (isset($_SESSION['AdditionalFilters']['DetailCondition'])) {
        foreach($_SESSION['AdditionalFilters']['DetailCondition'] as $key => $field) {
            $_SESSION['AdditionalFilters']['DetailCondition'][$key] = true;
        }
    }

    if (isset($_SESSION['AdditionalFilters']['ShippingFrom'])) {
        foreach($_SESSION['AdditionalFilters']['ShippingFrom'] as $key => $field) {
            $_SESSION['AdditionalFilters']['ShippingFrom'][$key] = true;
        }
    }

    if (isset($_SESSION['AdditionalFilters']['MediaType'])) {
        foreach($_SESSION['AdditionalFilters']['MediaType'] as $key => $field) {
            $_SESSION['AdditionalFilters']['MediaType'][$key] = true;
        }
    }

    foreach ($_SESSION["resultArr"] as & $row) {
        $row["Show"] = true;
    }
}

// print result table or card deck
function printResult() {
    if ($_SESSION["currentLayout"] == 'TableView') {
        return buildTable($_SESSION["resultArr"]);
    }
    else /* CardView */ {
        return buildCardDeck($_SESSION["resultArr"]);
    }
}

// build HTML table from array
function buildTable($arr, $cnt = "") {
    global $buyItNowTooltip;

    $xh = new Html;
    $xh->init($_SESSION["htmlIndent"]);

    if (count($arr) > 0) {
        $xh->add_attribute("class", "table");
        $xh->tag('div');
        $xh->add_attribute("class", "table table-striped small w-100 DataTable");
        $xh->add_attribute("data-paging", "false");
        $xh->add_attribute("data-searching", "false");
        $xh->add_attribute("data-state-save", "false");
        $xh->add_attribute("data-info", "false");
        $xh->add_attribute("data-ordering", "false");
        $xh->add_attribute("data-responsive", "true");
        $xh->add_attribute("id", "storeOfferTable" . $cnt);
        $xh->tag('table');
        $xh->add_attribute("class", "thead-dark table-header-sticky");
        $xh->tag('thead');
        $xh->tag('tr');

        $xh->add_attribute("data-name", "image");
        $xh->add_attribute("data-width", "20%");
        $xh->add_attribute("data-priority", "1");
        $xh->tag('th', "Image");
        $xh->add_attribute("data-name", "title");
        $xh->add_attribute("data-priority", "1");
        $xh->add_attribute("class", "text-left");
        $xh->tag('th', "Title / Merchant");
        $xh->add_attribute("data-name", "condition");
        $xh->add_attribute("data-class-name", "text-center");
        $xh->add_attribute("data-priority", "1");
        $xh->tag('th', "Condition");
        $xh->add_attribute("data-name", "price");
        $xh->add_attribute("data-priority", "2");
        $xh->tag('th', "Price");
        $xh->add_attribute("data-name", "shipping");
        $xh->add_attribute("data-priority", "2");
        $xh->tag('th', "S/H");
        $xh->add_attribute("data-name", "total");
        $xh->add_attribute("data-priority", "1");
        $xh->tag('th', "Total");
        $xh->add_attribute("data-name", "link");
        $xh->add_attribute("data-class-name", "text-center");
        $xh->add_attribute("data-priority", "3");
        $xh->tag('th', "");
        $xh->add_attribute("data-name", "details");
        $xh->add_attribute("data-priority", "9");
        // bugbug $xh->add_attribute("data-class-name", "never"); // none
        // bugbug $xh->tag('th', "Additional Details");

        $xh->close(); // tr
        $xh->close(); // thead

        $xh->tag('tbody');

        foreach ($arr as $row) {
            if (!$row["Show"]) {
                continue;
            }

            $title = $row["Title"];
            if (mb_strlen($row["Title"], 'UTF-8') > MAXTITLELENGTH) {
                $title = mb_substr($row["Title"], 0, MAXTITLELENGTH, 'UTF-8') . '...';
            }
            $title = htmlentities($title);

            $xh->add_attribute("class", "border");
            $xh->add_attribute("data-url", $row["URL"]);
            $xh->add_attribute("data-merchant", $row["Merchant"]);
            $xh->tag('tr');

            // Image
            $xh->tag('td');
            $xh->add_attribute("href", htmlentities($row["URL"]));
            $xh->add_attribute("target", "_blank");
            $xh->add_attribute("rel", "sponsored noreferrer noopener");
            $xh->add_attribute("data-toggle", "tooltip");
            $xh->add_attribute("title", $buyItNowTooltip);
            $xh->tag('a');
            $xh->add_attribute("class", "affiliate-link-table img-fluid result-table-image lazyload");
            $xh->add_attribute("src", PIXEL);
            $xh->add_attribute("data-src", htmlentities($row["Image"]));
            $xh->add_attribute("alt", "Item Image");
            $xh->single_tag('img');
            $xh->close(); // a
            $xh->close(); // td

            // Title / Merchant
            $xh->tag('td');
            $xh->add_attribute("class", "font-weight-bold");
            $xh->tag('span');
            $xh->add_attribute("class", "affiliate-link-table");
            $xh->add_attribute("href", htmlentities($row["URL"]));
            $xh->add_attribute("target", "_blank");
            $xh->add_attribute("rel", "sponsored noreferrer noopener");
            $xh->add_attribute("data-toggle", "tooltip");
            $xh->add_attribute("title",$buyItNowTooltip);
            $xh->tag('a', $title);
            $xh->close(); // span
            $xh->brnl();
            $xh->brnl();
            $xh->add_attribute("class", "font-weight-bold");
            $xh->tag('span', htmlentities($row["Merchant"]));
            if ($row["FeedbackScore"] != - 1) {
                $xh->brnl();
                $xh->tag('span', htmlentities($row["SellerName"]) . " (" . number_format($row["FeedbackScore"], 0, "", ", ") . " / " . $row["FeedbackPercent"] . "%)");
            }
            else if (!empty($row["SellerName"])) {
                $xh->brnl();
                $xh->tag('span', htmlentities($row["SellerName"]));
            }
            if (!empty($row["TimeLeft"])) {
                $xh->brnl();
                $xh->tag('span', $row["TimeLeft"]);
            }
            $xh->close(); // td

            // Condition
            $xh->tag('td');
            $xh->add_attribute("class", "font-weight-bold");
            $xh->tag('span', $row["DetailCondition"]);
            $xh->brnl();
            $xh->brnl();
            $xh->add_attribute("class",getMediaIconClass($row["MediaType"], "media-icon"));
            $xh->add_attribute("title",getMediaIconText($row["MediaType"]));
            $xh->add_attribute("data-toggle", "tooltip");
            $xh->add_attribute("data-placement", "right");
            $xh->add_attribute("data-delay", "200");
            $xh->tag('i', getMediaIconAlias($row["MediaType"]));

            $xh->add_attribute("class", "d-sm-none font-weight-bolder bg-info px-1");
            $xh->tag('span', print_monetary($row["ConvertedTotalPrice"], $_SESSION["buyer"]["Currency"]));

            $xh->close(); // td

            // Price
            $str = print_monetary($row["Price"], $row["Currency"]);
            if ($row["Currency"] != $_SESSION["buyer"]["Currency"]) {
                $str .= "<br/>&asymp; " . print_monetary($row["ConvertedPrice"], $_SESSION["buyer"]["Currency"]);
            }
            if ($row["BestOffer"] == "true") {
                $str .= "<br>Best Offer Accepted";
            }
            $xh->tag('td', $str);

            // Shipping and Handling Cost
            $str = "";
            if ($row["ShippingCost"] == 0.00) {
                $str .= "Free Shipping";
            }
            else {
                $str .= print_monetary($row["ShippingCost"], $row["ShippingCurrency"]);
                if ($row["ShippingEstimated"]) {
                    $str .= "*";
                }
            }
            if ($row["ShippingCost"] > 0.00 && $row["ShippingCurrency"] != $_SESSION["buyer"]["Currency"]) {
                $str .= "<br/>&asymp; " . print_monetary($row["ConvertedShippingCost"], $_SESSION["buyer"]["Currency"]);
            }
            if ($row["HandlingTime"] > 0) {
                $str .= "<br>Handling Time " . $row["HandlingTime"] . " day" . ($row["HandlingTime"] > 1 ? "s" : "");
            }
            if ($row["ShippingCost"] > 0.00 && $row["FreeShippingCap"] > 0) {
                $str .= "<br>Free Shipping over " . print_monetary($row["FreeShippingCap"], $_SESSION["buyer"]["Currency"]);
            }
            $str .= "<br/>";
            $xh->tag('td');
            $xh->tag('span', $str);
            $xh->add_attribute("class", "img-fluid lazyload");
            $xh->add_attribute("title", "Ships from " . getCountry($row["Country"]));
            $xh->add_attribute("data-toggle", "tooltip");
            $xh->add_attribute("data-placement", "right");
            $xh->add_attribute("data-delay", "200");
            $xh->add_attribute("src", PIXEL);
            $xh->add_attribute("data-src", timeStampUrl("images/flags/" . $row["Country"] . ".png"));
            $xh->add_attribute("alt", getCountry($row["Country"]) . " Flag");
            $xh->single_tag('img');
            $xh->close(); // td

            // Total Price
            $xh->add_attribute("class", "font-weight-bolder");
            $xh->tag('td', print_monetary($row["ConvertedTotalPrice"], $_SESSION["buyer"]["Currency"]));

            // Link
            if ($row["Merchant"] == "iTunes") {
                if ($row["MediaType"] == "Digital") {
                    $linkImage = timeStampUrl("images/US-UK_Apple_Music_Badge_RGB.svg");
                }
                else {
                    $linkImage = timeStampUrl("images/US_UK_Apple_Books_Badge_Get_RGB_071818.svg");
                }
                $class = "btn";
                $altText = "iTunes Badge";
            } else if (strpos($row["Merchant"], "eBay") !== false) {
                $class = "btn";
                $altText = "eBay Store";
                $linkImage = timeStampUrl("images/ebay-right-now.gif");
            } else if (strpos($row["Merchant"], "Amazon") !== false) {
                $class = "btn";
                $altText = "Amazon Store";
                $linkImage = timeStampUrl("images/amazon-buy3.gif");
            } else {
                $class = "btn btn-success";
                $altText = "";
                $linkImage = "";
            }
            $xh->tag('td');
            $xh->add_attribute("class", $class);
            $xh->add_attribute("title", $buyItNowTooltip);
            $xh->add_attribute("aria-label", "Go to store");
            $xh->add_attribute("data-toggle", "tooltip");
            $xh->add_attribute("role", "button");
            $xh->add_attribute("href", htmlentities($row["URL"]));
            $xh->add_attribute("target", "_blank");
            $xh->add_attribute("rel", "sponsored noreferrer noopener");
            $xh->tag('a');
            if (empty($linkImage)) {
                $xh->add_attribute("class", "affiliate-link-table material-icons md-36");
                $xh->tag('i', "store");
            } else {
                $xh->add_attribute("class", "affiliate-link-table lazyload");
                $xh->add_attribute("src", PIXEL);
                $xh->add_attribute("data-src", $linkImage);
                $xh->add_attribute("alt", $altText);
                $xh->single_tag('img');
            }
            $xh->close(); // a
            $xh->close(); // td

            // bugbug $xh->tag('td', $row["Details"]);

            $xh->close(); // tr
        }

        $xh->close(); // tbody

        $xh->tag('tfoot');
        $xh->add_attribute("class", "border");
        $xh->tag('tr');
        $xh->add_attribute("class", "font-italic");
        $xh->add_attribute("colspan", "7");
        $xh->tag('td', "Prices retrieved on " . gmdate("Y-m-d H:i") . " UTC<br>Daily exchange rates update");
        $xh->close(); // tr
        $xh->close(); // tfoot
        $xh->close(); // table

        $xh->close(); // div

        $html = $xh->flush();
        //error_log(print_r($html, 1));
    }
    else {
        $html = printNoResultsWarning();
    }

    return ($html);
}

// build HTML card deck from array
function buildCardDeck($arr) {
    global $buyItNowTooltip;

    $xh = new Html;
    $xh->init($_SESSION["htmlIndent"]);

    if (count($arr) > 0) {
        $xh->add_attribute("class", "card-deck small");
        $xh->add_attribute("id", "storeOfferCards");
        $xh->tag('div');

        foreach ($arr as $row) {
            if (!$row["Show"]) {
                continue;
            }

            $href = "href=\"" . htmlentities($row["URL"]) . "\" target=\"_blank\" rel=\"sponsored noreferrer noopener\"";
            $title = $row["Title"];
            if (mb_strlen($row["Title"], 'UTF-8') > MAXTITLELENGTH) {
                $title = mb_substr($row["Title"], 0, MAXTITLELENGTH, 'UTF-8') . '...';
            }
            $title = htmlentities($title);

            $xh->add_attribute("class", "card m-2 shadow mx-auto result-card");
            $xh->add_attribute("data-url", $row["URL"]);
            $xh->add_attribute("data-merchant", $row["Merchant"]);
            $xh->tag('div');

            // Image
              $xh->add_attribute("class", "p-0 m-0 text-center");
              $xh->add_attribute("href", htmlentities($row["URL"]));
              $xh->add_attribute("target", "_blank");
              $xh->add_attribute("rel", "sponsored noreferrer noopener");
              $xh->add_attribute("data-toggle", "tooltip");
              $xh->add_attribute("title", $buyItNowTooltip);
              $xh->tag('a');
                $xh->add_attribute("class", "affiliate-link-card p-0 m-0 img-fluid result-card-image lazyload");
                $xh->add_attribute("src", PIXEL);
                $xh->add_attribute("data-src", htmlentities($row["Image"]));
                $xh->add_attribute("alt", $title . " Item Image");
                $xh->single_tag('img');
              $xh->close(); // a

              $xh->add_attribute("class", "card-body d-flex flex-column");
              $xh->tag('div');

            // Title / Merchant
              $xh->add_attribute("class", "card-title font-weight-bold");
              $xh->tag('p');
                $xh->add_attribute("class", "affiliate-link-card");
                $xh->add_attribute("href", htmlentities($row["URL"]));
                $xh->add_attribute("target", "_blank");
                $xh->add_attribute("rel", "sponsored noreferrer noopener");
                $xh->add_attribute("data-toggle", "tooltip");
                $xh->add_attribute("title", $buyItNowTooltip);
                $xh->tag('a', $title);
              $xh->close(); // p
              $xh->add_attribute("class", "card-text mt-auto");
              $xh->tag('div');
                $xh->add_attribute("class", "font-weight-bold");
                $xh->tag('span', htmlentities($row["Merchant"]));
                $xh->brnl();

            // Condition / MediaType
                $xh->tag('span', $row["DetailCondition"]);
                $xh->add_attribute("class", getMediaIconClass($row["MediaType"], "media-icon float-right"));
                $xh->add_attribute("title", getMediaIconText($row["MediaType"]));
                $xh->add_attribute("data-toggle", "tooltip");
                $xh->add_attribute("data-placement", "right");
                $xh->add_attribute("data-delay", "200");
                $xh->tag('i', getMediaIconAlias($row["MediaType"]));
                $xh->brnl();

            // Total Price
                $xh->add_attribute("class", "font-weight-bolder bg-info px-1");
                $xh->tag('span', print_monetary($row["ConvertedTotalPrice"], $_SESSION["buyer"]["Currency"]));
            $xh->close(); // div

            $xh->close(); // div

            // Link / Ships from Flag
            $xh->add_attribute("class", "card-footer");
            $xh->tag('div');
            $xh->add_attribute("class", "row");
            $xh->tag('div');
            $xh->add_attribute("class", "col-9");
            $xh->tag('div');
            if ($row["Merchant"] == "iTunes") {
                if ($row["MediaType"] == "Digital") {
                    $linkImage = timeStampUrl("images/US-UK_Apple_Music_Badge_RGB.svg");
                }
                else {
                    $linkImage = timeStampUrl("images/US_UK_Apple_Books_Badge_Get_RGB_071818.svg");
                }
                $class = "btn p-0 m-0";
                $altText = "iTunes Badge";
            } else if (strpos($row["Merchant"], "eBay") !== false) {
                $class = "btn p-0 m-0";
                $altText = "eBay Store";
                $linkImage = timeStampUrl("images/ebay-right-now.gif");
            } else if (strpos($row["Merchant"], "Amazon") !== false) {
                $class = "btn p-0 m-0";
                $altText = "Amazon Store";
                $linkImage = timeStampUrl("images/amazon-buy3.gif");
            } else {
                $class = "btn btn-success m-0";
                $altText = "";
                $linkImage = "";
            }

            $xh->add_attribute("class", "affiliate-link-card " . $class);
            $xh->add_attribute("title", $buyItNowTooltip);
            $xh->add_attribute("aria-label", "Go to store");
            $xh->add_attribute("data-toggle", "tooltip");
            $xh->add_attribute("role", "button");
            $xh->add_attribute("href",htmlentities($row["URL"]));
            $xh->add_attribute("target", "_blank");
            $xh->add_attribute("rel", "sponsored noreferrer noopener");
            $xh->tag('a');
            if (empty($linkImage)) {
                $xh->add_attribute("class", "affiliate-link-card material-icons md-36");
                $xh->tag('i', "store");
            } else {
                $xh->add_attribute("class", $class . " affiliate-link-card img-fluid p-0 m-0 lazyload");
                $xh->add_attribute("src", PIXEL);
                $xh->add_attribute("data-src", $linkImage);
                $xh->add_attribute("alt",$altText);
                $xh->single_tag('img');
            }
            $xh->close(); // a
            $xh->close(); // div
            $xh->add_attribute("class", "col-3");
            $xh->tag('div');
              $xh->add_attribute("class", "float-right lazyload");
              $xh->add_attribute("title", "Ships from " . getCountry($row["Country"]));
              $xh->add_attribute("data-toggle", "tooltip");
              $xh->add_attribute("data-placement", "right");
              $xh->add_attribute("data-delay", "200");
              $xh->add_attribute("src", PIXEL);
              $xh->add_attribute("data-src", timeStampUrl("images/flags/" . $row["Country"] . ".png"));
              $xh->add_attribute("alt", getCountry($row["Country"]) . " Flag");
              $xh->single_tag('img');
            $xh->close(); // div
            $xh->close(); // div
            $xh->close(); // div

            $xh->close(); // div
        }

        $xh->close(); // div

        $xh->add_attribute("class", "py-2 text-right");
        $xh->tag('div');
        $xh->add_attribute("class", "py-2 font-italic");
        $xh->tag('p', "Prices retrieved on " . gmdate("Y-m-d H:i") . " UTC<br>Daily exchange rates update");
        $xh->close(); // div

        $html = $xh->flush();
    }
    else {
        $html = printNoResultsWarning();
    }

    //error_log(print_r($html, 1));

    return $html;
}

function printResultHeader() {
    $xh = new Html;
    $xh->init($_SESSION["htmlIndent"]);
    $xh->add_attribute("class", "navbar bg-dark mx-n3 mt-2 pb-0");
    $xh->tag('nav');
    $str = "";
    if ($_SESSION["lowestPrice"]["New"] > 0) {
        $str .= 'New from ' . print_monetary($_SESSION["lowestPrice"]["New"], $_SESSION["buyer"]["Currency"]);
    }
    if ($_SESSION["lowestPrice"]["New"] > 0 && $_SESSION["lowestPrice"]["Used"] > 0) {
        $str .= '<br>';
    }
    if ($_SESSION["lowestPrice"]["Used"] > 0) {
        $str .= 'Used from ' . print_monetary($_SESSION["lowestPrice"]["Used"], $_SESSION["buyer"]["Currency"]);
    }
    $xh->add_attribute("class", "d-none d-md-block mr-5");
    $xh->tag('span', $str);
    $xh->add_attribute("class", "nav nav-tabs");
    $xh->tag('ul');
    $xh->add_attribute("class", "nav-item border-0 mb-n1");
    $xh->tag('li');
    $xh->add_attribute("id", "detailTab");
    $xh->add_attribute("class", "nav-link active bg-white");
    $xh->add_attribute("href", "#detailFilter");
    $xh->tag('a');
    $xh->tag('span', "Filter");
    $xh->add_attribute("id", "detailTabArrow");
    $xh->tag('span');
    $xh->add_attribute("class", "material-icons material-text");
    $xh->tag('i', "expand_more");
    $xh->close(); // span
    $xh->close(); // a
    $xh->close(); // li
    $xh->close(); // ul

    $xh->add_attribute("class", "navbar-text float-right ml-3");
    $xh->tag('span', "Showing " . count(array_filter($_SESSION["resultArr"], function ($entry) { return ($entry['Show'] === true); })) . ' of ' . count($_SESSION["resultArr"]));

    $xh->add_attribute("class", "ml-auto");
    $xh->tag('span');
    if ($_SESSION["currentLayout"] == 'CardView') {
        $xh->add_attribute("id", "resultViewToggle");
        $xh->add_attribute("name", "submitBtn");
        $xh->add_attribute("value", "TableView");
        $xh->add_attribute("type", "submit");
        $xh->add_attribute("class", "btn btn-sm btn-rounded filterButton btn-info p-0 m-0 active");
        $xh->add_attribute("data-toggle", "tooltip");
        $xh->add_attribute("title", "Table View");
        $xh->add_attribute("aria-label", "Switch to Table View");
        $xh->tag('button');
        $xh->add_attribute("class", "material-icons md-36");
        $xh->tag('i', "view_list");
        $xh->close(); // button
    } else {
        $xh->add_attribute("id", "resultViewToggle");
        $xh->add_attribute("name", "submitBtn");
        $xh->add_attribute("value", "CardView");
        $xh->add_attribute("type", "submit");
        $xh->add_attribute("class", "btn btn-sm btn-rounded filterButton btn-info p-0 m-0 active");
        $xh->add_attribute("data-toggle", "tooltip");
        $xh->add_attribute("title", "Card View");
        $xh->add_attribute("aria-label", "Switch To Card View");
        $xh->tag('button');
        $xh->add_attribute("class", "material-icons md-36");
        $xh->tag('i', "view_module");
        $xh->close(); // button
    }
    $xh->close(); // span

    $xh->close(); // nav
    $xh->add_attribute("class", "tab-content mb-3");
    $xh->tag('div');
    $xh->add_attribute("id", "detailFilter");
    $xh->add_attribute("class", "container tab-pane border");
    $xh->tag('div');
    $xh->brnl();
    $xh->insert_code(detailResultHeader());
    $xh->close(); // div
    $xh->close(); // div

    $html = $xh->flush();
    //error_log(print_r($html, 1));

    return $html;
}

// print summary/header on top of listing table
function detailResultHeader() {
    $xh = new Html;
    $xh->init($_SESSION["htmlIndent"]);

    $xh->add_attribute("id", "detailFilterForm");
    $xh->tag('form');
    $xh->insert_code(inputSessionTab());
    $xh->insert_code(inputNonce());
    $xh->add_attribute("class", "card-group");
    $xh->tag('div');

    // Condition
    if (isset($_SESSION['AdditionalFilterCounters']['Condition'])) {
        $xh->add_attribute("class", "card m-2");
        $xh->tag('div');
        $xh->add_attribute("class", "card-header font-weight-bold");
        $xh->tag('div', "Condition");
        $xh->add_attribute("class", "card-body");
        $xh->tag('div');

        $cnt = count($_SESSION['AdditionalFilterCounters']['Condition']);
        foreach($_SESSION['AdditionalFilters']['Condition'] as $key => $value) {
            $xh->add_attribute("class", "form-check");
            $xh->tag('div');
            $xh->add_attribute("class", "form-check-label");
            $xh->tag('label');
            $xh->add_attribute("name", "filterCondition[]");
            $xh->add_attribute("type", "checkbox");
            $xh->add_attribute("value", $key);
            $xh->add_attribute("class", "form-check-input");
            if ($value) $xh->add_attribute("checked", "");
            if ($cnt < 1) $xh->add_attribute("disabled", "");
            $xh->single_tag('input');
            $xh->tag('span', $key);
            $xh->add_attribute("class", "badge badge-pill badge-dark ml-2");
            $xh->tag('span', $_SESSION['AdditionalFilterCounters']['Condition'][$key]);
            $xh->close(); // label
            $xh->close(); // div
        }

        $xh->close(); // div
        $xh->close(); // div
    }

    // Media Type
    if (isset($_SESSION['AdditionalFilterCounters']['MediaType'])) {
        $xh->add_attribute("class", "card m-2");
        $xh->tag('div');
        $xh->add_attribute("class", "card-header font-weight-bold");
        $xh->tag('div', "Media Type");
        $xh->add_attribute("class", "card-body");
        $xh->tag('div');

        $cnt = count($_SESSION['AdditionalFilterCounters']['MediaType']);
        foreach($_SESSION['AdditionalFilters']['MediaType'] as $key => $value) {
            $xh->add_attribute("class", "form-check");
            $xh->tag('div');
            $xh->add_attribute("class", "form-check-label");
            $xh->tag('label');
            $xh->add_attribute("name", "filterMediaType[]");
            $xh->add_attribute("type", "checkbox");
            $xh->add_attribute("value", $key);
            $xh->add_attribute("class", "form-check-input");
            if ($value) $xh->add_attribute("checked", "");
            if ($cnt < 1) $xh->add_attribute("disabled", "");
            $xh->single_tag('input');
            $xh->add_attribute("class",getMediaIconClass($key, "material-text"));
            $xh->tag('i', getMediaIconAlias($key));
            $xh->tag('span', getMediaIconText($key));
            $xh->add_attribute("class", "badge badge-pill badge-dark ml-2");
            $xh->tag('span', $_SESSION['AdditionalFilterCounters']['MediaType'][$key]);
            $xh->close(); // label
            $xh->close(); // div
        }

        $xh->close(); // div
        $xh->close(); // div
    }

    // Merchant
    if (isset($_SESSION['AdditionalFilterCounters']['Merchant'])) {
        $xh->add_attribute("class", "card m-2");
        $xh->tag('div');
        $xh->add_attribute("class", "card-header font-weight-bold");
        $xh->tag('div', "Merchant");
        $xh->add_attribute("class", "card-body");
        $xh->tag('div');

        $cnt = count($_SESSION['AdditionalFilterCounters']['Merchant']);
        foreach($_SESSION['AdditionalFilters']['Merchant'] as $key => $value) {
            $xh->add_attribute("class", "form-check");
            $xh->tag('div');
            $xh->add_attribute("class", "form-check-label");
            $xh->tag('label');
            $xh->add_attribute("name", "filterMerchant[]");
            $xh->add_attribute("type", "checkbox");
            $xh->add_attribute("value", $key);
            $xh->add_attribute("class", "form-check-input");
            if ($value) $xh->add_attribute("checked", "");
            if ($cnt < 1) $xh->add_attribute("disabled", "");
            $xh->single_tag('input');
            $xh->tag('span', $key);
            $xh->add_attribute("class", "badge badge-pill badge-dark ml-2");
            $xh->tag('span', $_SESSION['AdditionalFilterCounters']['Merchant'][$key]);
            $xh->close(); // label
            $xh->close(); // div
        }

        $xh->close(); // div
        $xh->close(); // div
    }

    // Shipping From
    if (isset($_SESSION['AdditionalFilterCounters']['ShippingFrom'])) {
        $xh->add_attribute("class", "card m-2");
        $xh->tag('div');
        $xh->add_attribute("class", "card-header font-weight-bold");
        $xh->tag('div', "Shipping From");
        $xh->add_attribute("class", "card-body");
        $xh->tag('div');

        $cnt = count($_SESSION['AdditionalFilterCounters']['ShippingFrom']);
        foreach($_SESSION['AdditionalFilters']['ShippingFrom'] as $key => $value) {
            $xh->add_attribute("class", "form-check");
            $xh->tag('div');
            $xh->add_attribute("class", "form-check-label");
            $xh->tag('label');

            $xh->add_attribute("name", "filterShipFrom[]");
            $xh->add_attribute("type", "checkbox");
            $xh->add_attribute("value", $key);
            $xh->add_attribute("class", "form-check-input");
            if ($value) $xh->add_attribute("checked", "");
            if ($cnt < 1) $xh->add_attribute("disabled", "");
            $xh->single_tag('input');

            $xh->add_attribute("class", "img-fluid lazyload");
            $xh->add_attribute("title", "Ships from " . getCountry($key));
            $xh->add_attribute("data-toggle", "tooltip");
            $xh->add_attribute("data-delay", "200");
            $xh->add_attribute("src", PIXEL);
            $xh->add_attribute("data-src", timeStampUrl("images/flags/" . $key . ".png"));
            $xh->add_attribute("alt", getCountry($key) . " Flag");
            $xh->single_tag('img');

            $xh->add_attribute("class", "badge badge-pill badge-dark ml-2");
            $xh->tag('span', $_SESSION['AdditionalFilterCounters']['ShippingFrom'][$key]);

            $xh->close(); // label
            $xh->close(); // div
        }

        $xh->close(); // div
        $xh->close(); // div
    }

    $xh->close(); // div
    $xh->add_attribute("class", "p-2");
    $xh->tag('div');
    $xh->add_attribute("type", "submit");
    $xh->add_attribute("id", "detailTabSubmit");
    $xh->add_attribute("class", "btn btn-success detailFilterButton");
    $xh->add_attribute("name", "submitBtn");
    $xh->add_attribute("value", "Apply");
    $xh->tag('button', "Apply");
    $xh->add_attribute("type", "submit");
    $xh->add_attribute("id", "detailTabReset");
    $xh->add_attribute("class", "btn btn-danger detailFilterButton");
    $xh->add_attribute("name", "submitBtn");
    $xh->add_attribute("value", "Reset");
    $xh->tag('button', "Reset");
    $xh->close(); // div
    $xh->close(); // form

    $html = $xh->flush();
    //error_log(print_r($html, 1));

    return $html;
}

// compare price for sort low to high
function compare_price($a, $b) {
    return strnatcmp($a['ConvertedTotalPrice'], $b['ConvertedTotalPrice']);
}

// print monetary values with correct symbol and thousands/decimal delimiters
function print_monetary($num, $curr) {
    if ($curr == "USD") {
        return ("$" . number_format($num, 2, '.', ','));
    }
    else if ($curr == "CAD") {
        return ("C $" . number_format($num, 2, '.', ','));
    }
    else if ($curr == "EUR") {
        return (number_format($num, 2, ',', '.') . "&euro;");
    }
    else if ($curr == "GBP") {
        return ("&pound;" . number_format($num, 2, '.', ','));
    }
    else if ($curr == "AUD") {
        return ("AU $" . number_format($num, 2, '.', ','));
    }

    return ($curr . " " . number_format($num, 2, '.', ','));
}

// find lowest used / new prices
function findLowestCondition($condition) {
    foreach ($_SESSION["resultArr"] as $row) {
        if (!$row["Show"]) {
            continue;
        }

        if ($condition == $row["Condition"]) {
            return ($row["ConvertedTotalPrice"]);
        }
    }

    return (0);
}

// find lowest cd, record, digital and book prices
function findLowestMediaType($mediaType) {
    foreach ($_SESSION["resultArr"] as $row) {
        if (!$row["Show"]) {
            continue;
        }

        if ($mediaType == $row["MediaType"]) {
            return ($row["ConvertedTotalPrice"]);
        }
    }

    return (0);
}

// find lowest non-zero double value in array
function minNotNull(Array $values) {
    return min(array_diff(array_map('doubleval', $values) , array(
        0
    )));
}

// apply exchange rates
function applyExchangeRates($arr) {
    foreach ($arr as & $value) {
        $value["ConvertedPrice"] = $value["Price"];
        $value["ConvertedShippingCost"] = $value["ShippingCost"];

        if ($_SESSION["buyer"]["Currency"] != $value["Currency"]) {
            $value["ConvertedPrice"] = number_format($value["Price"] / getExchangeRate($_SESSION["buyer"]["Currency"], $value["Currency"]) , 2, '.', '');
        }

        if ($_SESSION["buyer"]["Currency"] != $value["ShippingCurrency"]) {
            $value["ConvertedShippingCost"] = number_format($value["ShippingCost"] / getExchangeRate($_SESSION["buyer"]["Currency"], $value["ShippingCurrency"]) , 2, '.', '');
        }

        $value["ConvertedTotalPrice"] = number_format($value["ConvertedPrice"] + $value["ConvertedShippingCost"], 2, '.', '');
    }

    return ($arr);
}

// sanitize user input
function sanitizeInput($str) {
    $str = trim(preg_replace('/[\t\n\r\s]+/', ' ', $str));
    $str = stripslashes($str);
    $str = htmlspecialchars($str);
    return $str;
}

// convert certain utf-8 characters to ascii
function cleanString($str) {
    $utf8 = array(
        '/[áàâãªä]/u' => 'a',
        '/[ÁÀÂÃÄ]/u' => 'A',
        '/[ÍÌÎÏ]/u' => 'I',
        '/[íìîï]/u' => 'i',
        '/[éèêë]/u' => 'e',
        '/[ÉÈÊË]/u' => 'E',
        '/[óòôõºö]/u' => 'o',
        '/[ÓÒÔÕÖ]/u' => 'O',
        '/[úùûü]/u' => 'u',
        '/[ÚÙÛÜ]/u' => 'U',
        '/ç/' => 'c',
        '/Ç/' => 'C',
        '/ñ/' => 'n',
        '/Ñ/' => 'N',
        '/–/' => '-', // UTF-8 hyphen to "normal" hyphen
        '/[’‘‹›‚]/u' => ' ', // Literally a single quote
        '/[“”«»„]/u' => ' ', // Double quote
        '/ /' => ' ', // nonbreaking space (equiv. to 0x160)

    );

    return preg_replace(array_keys($utf8) , array_values($utf8) , $str);
}

// Clean the search string
function searchFriendlyString($str) {
    $str = strip_tags($str);
    $str = stripslashes($str);
    $str = cleanString($str);
    $str = str_replace(array(
        "[",
        "]",
        "<",
        ">",
        "(",
        ")",
        " - ",
        " & ",
        " / "
    ) , " ", $str);
    $str = trim(preg_replace('/[\t\n\r\s]+/', ' ', $str)); // delete extra whitespaces
    return ucwords($str);
}

// get a SESSION value, return empty string if not set
function getSV($var) {
    if (!isset($_SESSION[$var])) {
        return ('');
    }

    return ($_SESSION[$var]);
}

// initialize a SESSION value if not set
function initSV($var, $value) {
    if (!isset($_SESSION[$var])) {
        $_SESSION[$var] = $value;
    }
}

// initialize sessions variables
function initSessionVariables($systemConf) {
    initSV("resultArr", []);
    initSV("barcode", array(
        "Type" => "",
        "Value" => ""
    ));
    initSV("buyer", array(
        "Country" => "United States",
        "Currency" => "USD",
        "Zip" => ""
    ));
    initSV("filterCondition", array(
        "New" => true,
        "Used" => true
    ));
    initSV("filterMediaType", array(
        "CD" => true,
        "Record" => true,
        "Digital" => true,
        "Book" => true
    ));
    initSV("currentLayout", "TableView");
    initSV("lowestPrice", array(
        "Used" => 0.00,
        "New" => 0.00,
        "CD" => 0.00,
        "Record" => 0.00,
        "Digital" => 0.00,
        "Book" => "0.00",
        "All" => 0.00
    ));
    $_SESSION["htmlIndent"] = (!empty($systemConf["htmlIndent"]) ? intval($systemConf["htmlIndent"]) : 0);
    $_SESSION["gtmId"] = (empty($systemConf["gtmId"]) ? "" : $systemConf["gtmId"]);
    $_SESSION["nonce"] = NonceUtil::generate($systemConf["nonce_secret"], $systemConf["nonce_lifetime"]);
    initSV("mode", SIMPLE_SEARCH);
}

function md5SearchTerm($flag) {
    $data = array();
    $data['cond'] = $_SESSION['filterCondition'];
    $data['type'] = $_SESSION['filterMediaType'];
    $data['buyer'] = $_SESSION['buyer'];
    $data['flag'] = $flag;
    $data['term'] = array(
        $_SESSION['searchTerm']
    );

    return (md5(json_encode($data)));
}

// check POST value, return true if set and false if not
function checkPV($var) {
    if (isset($_POST[$var])) {
        return (true);
    }

    return (false);
}

// get POST or GET value, return empty if not set
function getPGV($var) {
    if (isset($_POST[$var])) {
        return ($_POST[$var]);
    }
    else if (isset($_GET[$var])) {
        return ($_GET[$var]);
    }

    return ("");
}

function saveSearchResult($noDiscogs) {
    $conn = MySessionHandler::getDBSessionId();

    $access = mysqli_real_escape_string($conn, time());
    // BUGBUG
    //  country
    //  currency
    $zip = mysqli_real_escape_string($conn, $_SESSION['buyer']['Zip']);
    $condNew = $_SESSION['filterCondition']['New'] ? 'Y' : 'N';
    $condUsed = $_SESSION['filterCondition']['Used'] ? 'Y' : 'N';
    $mediaCD = $_SESSION['filterMediaType']['CD'] ? 'Y' : 'N';
    $mediaRecord = $_SESSION['filterMediaType']['Record'] ? 'Y' : 'N';
    $mediaDigital = $_SESSION['filterMediaType']['Digital'] ? 'Y' : 'N';
    $mediaBook = $_SESSION['filterMediaType']['Book'] ? 'Y' : 'N';
    $searchTerm = mysqli_real_escape_string($conn, $_SESSION['searchTerm']);

    $arr = [];
    $advSearchTerm = null;
    if (!empty($_SESSION["advSearch"]["Title"])) {
        $arr[] = "t" . AS_FIELD . $_SESSION["advSearch"]["Title"];
    }
    if (!empty($_SESSION["advSearch"]["Artist"])) {
        $arr[] = "a" . AS_FIELD . $_SESSION["advSearch"]["Artist"];
    }
    if (!empty($_SESSION["advSearch"]["Track"])) {
        $arr[] = "tr" . AS_FIELD . $_SESSION["advSearch"]["Track"];
    }
    if (!empty($_SESSION["advSearch"]["Barcode"])) {
        $arr[] = "b" . AS_FIELD . $_SESSION["advSearch"]["Barcode"];
    }
    if (!empty($_SESSION["advSearch"]["Catno"])) {
        $arr[] = "c" . AS_FIELD . $_SESSION["advSearch"]["Catno"];
    }
    if (!empty($_SESSION["advSearch"]["Label"])) {
        $arr[] = "l" . AS_FIELD . $_SESSION["advSearch"]["Label"];
    }
    if (!empty($_SESSION["advSearch"]["Country"])) {
        $arr[] = "co" . AS_FIELD . $_SESSION["advSearch"]["Country"];
    }
    if (!empty($_SESSION["advSearch"]["Year"])) {
        $arr[] = "y" . AS_FIELD . $_SESSION["advSearch"]["Year"];
    }
    $advSearchTerm = (empty($arr) ? null : "'" . mysqli_real_escape_string($conn, join(AS_TOKEN, $arr)) . "'");

    $lowNew = floatval($_SESSION['lowestPrice']['New']);
    $lowUsed = floatval($_SESSION['lowestPrice']['Used']);
    $lowCD = floatval($_SESSION['lowestPrice']['CD']);
    $lowRecord = floatval($_SESSION['lowestPrice']['Record']);
    $lowDigital = floatval($_SESSION['lowestPrice']['Digital']);
    $lowBook = floatval($_SESSION['lowestPrice']['Book']);
    $count = count($_SESSION['resultArr']);
    $userId = (empty($_SESSION['sessData']['userID']) ? null : $_SESSION['sessData']['userID']);
    $ip = inet_pton($_SERVER['REMOTE_ADDR']);
    $onlyStore = $noDiscogs ? 'Y' : 'N';
    $sessionId = session_id();

    $sql = "INSERT
                INTO searches
                (sessId, access, ip, zip, onlyStore, condNew, condUsed, mediaCD, mediaRecord, mediaDigital, mediaBook, searchTerm, advSearchTerm, lowNew, lowUsed, lowCD, lowRecord, lowDigital, lowBook, count, userId)
                VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)";
    $stmt = mysqli_prepare($conn, $sql);
    mysqli_stmt_bind_param($stmt, 'sdsssssssssssdddddddd', $sessionId, $access, $ip, $zip, $onlyStore, $condNew, $condUsed, $mediaCD, $mediaRecord,
                             $mediaDigital, $mediaBook, $searchTerm, $advSearchTerm, $lowNew, $lowUsed, $lowCD, $lowRecord, $lowDigital, $lowBook, $count, $userId);

    if (!($result = mysqli_stmt_execute($stmt))) {
        error_log("MySQL Write Searches Error: " . mysqli_error($conn) . " (" . mysqli_errno($conn) . ")");
    }

    mysqli_stmt_close($stmt);

    return(mysqli_insert_id($conn));
}

function getUrl($url, $userAgent = null) {
    $ch = curl_init();

    // Set request header with language and charset
    $header = array(
        "Accept-Language: en-US,en;q=0.5",
        "Accept-Charset: UTF-8,*;q=0.5"
    );
    curl_setopt($ch, CURLOPT_HTTPHEADER, $header);

    // Set optional user-agent
    if ($userAgent) {
        curl_setopt($ch, CURLOPT_USERAGENT, $userAgent);
    }

    curl_setopt($ch, CURLOPT_ENCODING, "gzip,deflate");
    curl_setopt($ch, CURLOPT_AUTOREFERER, true);
    curl_setopt($ch, CURLOPT_HEADER, 0);
    curl_setopt($ch, CURLOPT_CONNECTTIMEOUT, 5);
    curl_setopt($ch, CURLOPT_TIMEOUT, 15);
    curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
    curl_setopt($ch, CURLOPT_URL, $url);
    $response = curl_exec($ch);
    if ($response === false) {
        error_log('Curl Request Error: ' . curl_error($ch) . ' (' . curl_errno($ch) . ')');
        error_log('Url: ' . $url);
        $response = '';
    }

    curl_close($ch);

    return $response;
}

function getMultiUrl($urls, $userAgent = null) {
    $multi = curl_multi_init();
    $channels = [];
    $response = [];
    // Set request header with language and charset
    $header = array(
        "Accept-Language: en-US,en;q=0.5",
        "Accept-Charset: UTF-8,*;q=0.5"
    );

    // Loop through the URLs, create curl-handles
    // and attach the handles to our multi-request
    foreach ($urls as $url) {
        $ch = curl_init();

        curl_setopt($ch, CURLOPT_HTTPHEADER, $header);

        // Set optional user-agent
        if ($userAgent) {
            curl_setopt($ch, CURLOPT_USERAGENT, $userAgent);
        }

        if (defined('CURL_HTTP_VERSION_2_0')) {
            curl_setopt($ch, CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_2_0);
        }
        curl_setopt($ch, CURLOPT_ENCODING, "gzip,deflate");
        curl_setopt($ch, CURLOPT_AUTOREFERER, true);
        curl_setopt($ch, CURLOPT_HEADER, 0);
        curl_setopt($ch, CURLOPT_CONNECTTIMEOUT, 5);
        curl_setopt($ch, CURLOPT_TIMEOUT, 15);
        curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
        curl_setopt($ch, CURLOPT_URL, $url);

        curl_multi_add_handle($multi, $ch);

        $channels[$url] = $ch;
    }

    curl_multi_setopt($multi, CURLMOPT_PIPELINING, 3);

    // While we're still active, execute curl
    $active = null;
    do {
        $mrc = curl_multi_exec($multi, $active);
    } while ($mrc == CURLM_CALL_MULTI_PERFORM);

    while ($active && $mrc == CURLM_OK) {
        // Wait for activity on any curl-connection
        if (curl_multi_select($multi) == -1) {
            continue;
        }

        // Continue to exec until curl is ready to give us more data
        do {
            $mrc = curl_multi_exec($multi, $active);
        } while ($mrc == CURLM_CALL_MULTI_PERFORM);
    }

    // Loop through the channels and retrieve the received
    // content, then remove the handle from the multi-handle
    foreach ($channels as $url => $channel) {
        $response[$url] = curl_multi_getcontent($channel);
        if ($response[$url] === false) {
            error_log('Curl Request Error: ' . curl_error($channel) . ' (' . curl_errno($channel) . ')');
            error_log('Url: ' . $url);
            $response[$url] = '';
        }
        curl_multi_remove_handle($multi, $channel);
    }

    // Close the multi-handle and return our results
    curl_multi_close($multi);

    return($response);
}

// Retrieve search history for current session id
function getSearchHistory() {
    $xh = new Html;
    $xh->init($_SESSION["htmlIndent"]);

    $sql = "select searchTerm, advSearchTerm, max(access) from searches where onlyStore <> 'Y' and (sessId = '" . session_id() . "'";
    if (!empty($_SESSION['sessData']['userID'])) {
        $sql .= " or userID = '" . $_SESSION['sessData']['userID'] . "'";
    }
    $sql .= ") group by searchTerm order by max(access) desc, searchTerm limit 0,50;";
    $conn = MySessionHandler::getDBSessionId();

    if ($result = mysqli_query($conn, $sql)) {
        if (mysqli_num_rows($result) > 0) {
            while ($row = mysqli_fetch_assoc($result)) {
                $xh->add_attribute("value", $row["advSearchTerm"] ? "*ADV*" . $row["advSearchTerm"] : $row["searchTerm"]);
                $xh->tag("option", $row["searchTerm"]);
            }
        }
    }
    else if (mysqli_errno($conn)) {
        error_log("MySQL Read Searches SQL: " . $sql);
        error_log("MySQL Read Searches Error: " . mysqli_error($conn) . " (" . mysqli_errno($conn) . ")");
    }

    $html = $xh->flush();
    //error_log(print_r($html, 1));

    return $html;
}

// Retrieve coupons codes
function getCouponCodes() {
    $lastAdvertiser = "";

    $xh = new Html;
    $xh->init($_SESSION["htmlIndent"]);

    if (!isLoggedIn()) {
        $xh->add_attribute("class", "container bg-warning text-center py-3");
        $xh->tag('div');
            $xh->add_attribute("class", "display-6");
            $xh->tag('p');
                $xh->add_attribute("class", "material-icons");
                $xh->tag('i', "error_outline");
                $xh->tag('span', " Please login to your Find Cheap Music account in order to see the coupons.");
            $xh->close(); // p
        $xh->close(); // div

        $html = $xh->flush();
       //error_log(print_r($html, 1));

        return $html;
    }

    $sql = 'select advertiser, date_format(enddate, "%M %e, %Y") as enddate, description, couponcode, url, image from coupons where DATE(NOW()) between startdate and enddate group by advertiser, description order by advertiser, id';
    $conn = MySessionHandler::getDBSessionId();

    if ($result = mysqli_query($conn, $sql)) {
        if (mysqli_num_rows($result) > 0) {
            $xh->add_attribute("class", "container py-4 border bg-light");
            $xh->add_attribute("id", "couponList");
            $xh->tag('div');
            while ($row = mysqli_fetch_assoc($result)) {
                if ($row["advertiser"] != $lastAdvertiser) {
                    if (!empty($lastAdvertiser)) {
                        $xh->close(); // ul
                    }
                    $xh->add_attribute("class", "text-center mt-3 mb-1");
                    $xh->tag('h2', $row["advertiser"]);
                    $xh->add_attribute("class", "list-group");
                    $xh->tag('ul');
                    $lastAdvertiser = $row["advertiser"];
                }
                if (!empty($row["url"])) {
                    $xh->add_attribute("class", "list-group-item");
                    $xh->tag('li');
                        $xh->add_attribute("class", "container btn btn-link text-center coupon-link");
                        $xh->add_attribute("target", "_blank");
                        $xh->add_attribute("href", htmlentities($row["url"]));
                        $xh->add_attribute("rel", "nofollow noreferrer noopener");
                        $xh->add_attribute("data-advertiser", $row["advertiser"]);
                        $xh->tag('a');
                            $str = '<strong>' . $row["description"];
                            if (!empty($row["couponcode"])) {
                                $str .= ' (Use Coupon Code "' . $row["couponcode"] . '")';
                            }
                            $str .= '</strong>';
                            $xh->tag('div', $str);
                            if (!empty($row["image"])) {
                                $xh->add_attribute("class", "text-center mt-3");
                                $xh->tag('div');
                                    $xh->add_attribute("class", "border-0 lazyload");
                                    $xh->add_attribute("src", PIXEL);
                                    $xh->add_attribute("data-src", htmlentities($row["image"]));
                                    $xh->add_attribute("alt", $row["advertiser"] . " Coupon");
                                    $xh->single_tag('img');
                                $xh->close(); // div
                            }
                        $xh->close(); // a
                    $xh->close(); // li
                }
            }

            if (!empty($lastAdvertiser)) {
                $xh->close(); // ul
            }

    $xh->add_attribute("nonce", base64_encode($_SESSION["nonce"]));
    $xh->tag('script');
        $str  = my_trim('document.addEventListener("DOMContentLoaded", function() {');
        $str .= my_trim('    document.getElementById("couponList").addEventListener("click", function(event) {');
        $str .= my_trim('        e = event.target.closest("a");');
        $str .= my_trim('        if (e && e.classList.contains("coupon-link")) {');
        $str .= my_trim('            window.dataLayer.push({ "event" : "trackEvent", "eventCategory" : "Coupon", "eventAction" : "Click", "eventLabel" : e.getAttribute("data-advertiser")});');
        $str .= my_trim('        }');
        $str .= my_trim('    });');
        $str .= my_trim('});');
    $xh->insert_code($str);
    $xh->close(); // script

            $xh->close(); // div
        }
    }
    else if (mysqli_errno($conn)) {
        $xh->add_attribute("class", "container bg-info text-center py-3");
        $xh->tag('div');
            $xh->add_attribute("class", "display-6");
            $xh->tag('p');
                $xh->add_attribute("class", "material-icons");
                $xh->tag('i', "loyalty");
                $xh->tag('span', " No Coupons available at the moment...");
            $xh->close(); // p
        $xh->close(); // div
    }

    $html = $xh->flush();
    //error_log(print_r($html, 1));

    return $html;
}

// Delete left over progressbar files older than 2 days
function cleanupPbFiles() {
    $files = glob(TMP_DIR . "pb*.txt");
    $now = time();
    foreach ($files as $file) {
        if (is_file($file)) {
            if ($now - filemtime($file) >= 60 * 60 * 24 * 2) { // 2 days and older
                unlink($file);
            }
        }
    }
}

// Update progressbar file for a session
function updatePbFile($flag = false, $desc = null) {
    static $max_pb = 10; // max progressbar steps
    static $current = 0;
    static $lastTime = 0;
    static $startTime = 0;
    static $timers = [];

    if ($flag) {
        if ($desc == "Start") {
            $current = 0;
            $timers = [];
            $startTime = $lastTime = microtime(true);
        } else if (strpos($desc, "End:") === 0) {
            $nowTime = microtime(true);
            $timers["Total"] = intval(($nowTime - $startTime) * 1000);
            $pieces = explode(":", $desc);
            savePbTimers($timers, $pieces[1]);
        }
    }
    else {
        ++$current;
        $nowTime = microtime(true);
        $diffTime = $nowTime - $lastTime;
        $lastTime = $nowTime;
        $timers[$desc] = intval($diffTime * 1000);
   }

    if ($current > $max_pb) {
        error_log("max_pb $max_pb is too small, current step is $current. Adjust tools.php (updatePbFile).");
        $max_pb = $current;
    }
    $filename = session_id() . "_" . MySessionHandler::getSessionTab();
    $arr_content = array();

    $percent = intval($current / $max_pb * 100);

    $arr_content['percent'] = $percent;
    $arr_content['message'] = $current . " search(es) processed.";
    $file = TMP_DIR . "pb_" . $filename . ".txt";

    if ($percent >= 100) {
        @unlink($file);
    } else {
        file_put_contents($file, json_encode($arr_content));
    }
}

// Linkshare / CJ Affiliate csv dump
function ls_cj_csv($fields) {
    static $fh = null;
    $delimiter = ',';
    $enclosure = '"';
    $mysql_null = false;

    if (!$fh) {
        $fh = fopen("ls_cj.csv", "a+");
    }

    $delimiter_esc = preg_quote($delimiter, '/');
    $enclosure_esc = preg_quote($enclosure, '/');

    $output = array();
    foreach ($fields as $field) {
        if ($field === null && $mysql_null) {
            $output[] = 'NULL';
            continue;
        }

        $output[] = preg_match("/(?:${delimiter_esc}|${enclosure_esc}|\s)/", $field) ? ($enclosure . str_replace($enclosure, $enclosure . $enclosure, $field) . $enclosure) : $field;
    }

    fwrite($fh, join($delimiter, $output) . "\n");
}

// Login in check
function isLoggedIn() {
    return (!empty($_SESSION['sessData']['userLoggedIn']) && !empty($_SESSION['sessData']['userID'])) ? true : false;
}

// unset all login system session data
function unsetSessData() {
    unset($_SESSION['sessData']['userLoggedIn']);
    unset($_SESSION['sessData']['userID']);
    unset($_SESSION['sessData']['loginType']);
}

// get user image name
function getUserImage($userData) {
    if (empty($userData) || empty($userData['picture'])) {
        return 'login/assets/images/default.png';
    }

    $httpPos = strpos($userData['picture'], 'http');
    if ($httpPos === false) {
        return 'login/' . UPLOAD_PATH . 'profile_picture/' . $userData['picture'];
    }

    return $userData['picture'];
}

function startsWith($haystack, $needle) {
    return substr_compare($haystack, $needle, 0, strlen($needle)) === 0;
}

function endsWith($haystack, $needle) {
    return substr_compare($haystack, $needle, -strlen($needle)) === 0;
}

function displayBarcode($barcode) {
    $barcode = trim(preg_replace("/[^0-9]/", "", $barcode));
    $barcodeType = clsLibGTIN::GTINCheck($barcode, false, 1);

    if ($barcodeType == "UPC" && strlen($barcode) == 12) {
        return substr($barcode, 0, 1) . "-" . substr($barcode, 1, 5) . "-" . substr($barcode, 6, 5) . "-" . substr($barcode, 11, 1);
    }
    else if (($barcodeType == "EAN" || $barcodeType == "ISBN") && strlen($barcode) == 13) {
        return substr($barcode, 0, 1) . "-" . substr($barcode, 1, 6) . "-" . substr($barcode, 7, 6);
    }
    else if ($barcodeType == "EAN" && strlen($barcode) == 14) {
        return substr($barcode, 0, 1) . "-" . substr($barcode, 1, 2) . "-" . substr($barcode, 3, 5) . "-" . substr($barcode, 8, 5) . "-" . substr($barcode, 13, 1);
    }
    else {
        return $barcode;
    }
}

// fuzzy search to verify titles are relevant
function verifyResultArr() {
    require_once ('php/Fuse/Bitap/Bitap.php');
    require_once ('php/Fuse/Bitap/matched_indices.php');
    require_once ('php/Fuse/Bitap/pattern_alphabet.php');
    require_once ('php/Fuse/Bitap/regex_search.php');
    require_once ('php/Fuse/Bitap/score.php');
    require_once ('php/Fuse/Bitap/search.php');
    require_once ('php/Fuse/Helpers/deep_value.php');
    require_once ('php/Fuse/Helpers/is_list.php');
    require_once ('php/Fuse/Fuse.php');

    if (!empty($_SESSION["advSearch"]["Barcode"]) || empty($_SESSION["resultArr"]) || empty($_SESSION["searchTerm"])) {
        return;
    }

    $options = [
      'shouldSort' => false,
    //  'tokenize' => true,
    //  'matchAllTokens' => true,
    //  'findAllMatches' => true,
      'includeScore' => true,
      'includeMatches' => true,
      'threshold' => 0.6,
      'location' => 0,
      'distance' => 100,
      'minMatchCharLength' => 5,
      'keys' => [ "Title" ]
    ];

    $fuse = new Fuse($_SESSION["resultArr"], $options);
    $result = $fuse->search($_SESSION["searchTerm"]);

    $_SESSION["resultArr"] = [];
    foreach($result as $r) {
        $r['item']['score'] = (!empty($r['score']) ? $r['score'] : 0);
        $r['item']['indices'] = (!empty($r['matches'][0]['indices']) ? $r['matches'][0]['indices'] : []);
        $_SESSION['resultArr'][] = $r['item'];
    }

    /* debug start
    $lines = [];
    foreach($_SESSION['resultArr'] as $r) {
        $p = 0;
        $t = '';
        foreach($r['indices'] as $ind) {
            if ($p < $ind[0]) {
                $t .= substr($r['Title'], $p, $ind[0] - $p);
            }
            $t .= "<b>" . substr($r['Title'], $ind[0], $ind[1] - $ind[0] + 1) . "</b>";
            $p = $ind[1] + 1;
        }
        if ($p < strlen($r['Title'])) {
            $t .= substr($r['Title'], $p);
        }
        $lines[] = array ('score' => $r['score'], 'title' => $t);
    }

    usort($lines, 'compare_score');
    echo "<p>";
    foreach($lines as $l) {
        echo $l['score'] . ": " . $l['title'] . "<br/>";
    }
    echo "</p>";
    debug end */
}

// compare score for sort low to high
function compare_score($a, $b) {
    return ($a['score'] > $b['score']);
}

function my_error_log($msg) {
    error_log("[" . date("d-M-Y H:m:s") . "] " . $msg . PHP_EOL, 3, LOG_DIR . "my_php_error.log");
}

function savePbTimers($timers, $id) {
    $conn = MySessionHandler::getDBSessionId();

    $sql = "INSERT
                INTO searchPerformance
                (id, Discogs, eBay_New, Linkshare, CJ_Affiliate, Walmart, iTunes, Amazon_API, Amazon_Scrape, Impact, eBay_Used, Total)
                VALUES ($id, " . $timers['Discogs'] . ",
" . $timers['eBay New'] . ",
" . $timers['Linkshare'] . ",
" . $timers['CJ Affiliate'] . ",
" . $timers['Walmart'] . ",
" . $timers['iTunes'] . ",
" . $timers['Amazon API'] . ",
" . $timers['Amazon Scrape'] . ",
" . $timers['Impact'] . ",
" . $timers['eBay Used'] . ",
" . $timers['Total'] . ")";

    if (!($result = mysqli_query($conn, $sql))) {
        error_log("MySQL Write SearchPerformance Error: " . mysqli_error($conn) . " (" . mysqli_errno($conn) . ")");
    }

    return $result;
}

function saveSearchCache($vendor, $query, $subquery, $text) {
    $conn = MySessionHandler::getDBSessionId();

    $created = mysqli_real_escape_string($conn, time());
    $v = mysqli_real_escape_string($conn, $vendor);
    $q = mysqli_real_escape_string($conn, $query);
    $s = mysqli_real_escape_string($conn, $subquery);
    $r = base64_encode(gzencode($text));

    $sql = "INSERT
                INTO searchCache
                (created, vendor, query, subquery, result)
                VALUES ('$created', '$v', '$q', '$s', '$r')
                ON DUPLICATE KEY UPDATE result = '$r'";

    if (!($result = mysqli_query($conn, $sql))) {
        error_log("MySQL Write SearchCache Error: " . mysqli_error($conn) . " (" . mysqli_errno($conn) . ")");
    }

    return $result;
}


function expireSearchCache() {
    $conn = MySessionHandler::getDBSessionId();
    $t = MySessionHandler::getDBExpirationTime();

    $expired = mysqli_real_escape_string($conn, time() - $t);

    $sql = "DELETE
                FROM searchCache
                WHERE created < $expired";

    if (!($result = mysqli_query($conn, $sql))) {
        error_log("MySQL Delete SearchCache Error: " . mysqli_error($conn) . " (" . mysqli_errno($conn) . ")");
    }
}

function getSearchCache($vendor, $query, $subquery) {
    $conn = MySessionHandler::getDBSessionId();

    $v = mysqli_real_escape_string($conn, $vendor);
    $q = mysqli_real_escape_string($conn, $query);
    $s = mysqli_real_escape_string($conn, $subquery);
    $sql = "select result from searchCache where vendor = '$v' and query = '$q' and subquery = '$s'";

    if ($result = mysqli_query($conn, $sql)) {
        if (mysqli_num_rows($result) == 1) {
            if ($row = mysqli_fetch_assoc($result)) {
                return gzdecode(base64_decode($row["result"]));
            }
        }
    }
    else if (mysqli_errno($conn)) {
        error_log("MySQL Read SearchCache SQL: " . $sql);
        error_log("MySQL Read SearchCache Error: " . mysqli_error($conn) . " (" . mysqli_errno($conn) . ")");
    }

    return false;
}


function getGeoLocation() {
    if (!empty($_SESSION['buyer']['Zip']) || empty($_SERVER['REMOTE_ADDR'])) {
        return;
    }
    
    $zip = null;

    $conn = MySessionHandler::getDBSessionId();
    $ip = inet_pton($_SERVER['REMOTE_ADDR']);

    $sql = "select zip from geoLocation where ip = ?";
    $stmt = mysqli_prepare($conn, $sql);
    mysqli_stmt_bind_param($stmt, 's', $ip);

    if (mysqli_stmt_execute($stmt)) {
        mysqli_stmt_bind_result($stmt, $zip);
        mysqli_stmt_fetch($stmt);
    }
    else if (mysqli_errno($conn)) {
        error_log("MySQL Read geoLocation SQL: " . $sql);
        error_log("MySQL Read geoLocation Error: " . mysqli_error($conn) . " (" . mysqli_errno($conn) . ")");
    }

    mysqli_stmt_close($stmt);

    if (!empty($zip)) {
        $_SESSION['buyer']['Zip'] = $zip;
        return;
    }

    $vendors = Vendors::getInstance();
    $config = $vendors->getVendor(Vendors::IPAPI);
    $key = $config['KEY'];

    $loc = file_get_contents("http://api.ipapi.com/api/" . $_SERVER['REMOTE_ADDR'] . "?access_key=" . $key);
    if ($loc == false) {
        return;
    }

    $json = json_decode($loc);
    if (!empty($json->error)) {
        error_log("geoLocation Error: " . $json->error->info . " (" . $json->error->code . " | " . $json->error->type . ")");
        return;
    }

    $created = mysqli_real_escape_string($conn, time());
    $countryCode = mysqli_real_escape_string($conn, $json->{'country_code'});
    $zip = mysqli_real_escape_string($conn, $json->zip);
    $language = mysqli_real_escape_string($conn, $json->location->languages[0]->code);
    $is_eu = $json->location->{'is_eu'} ? '1' : '0';

    $sql = "INSERT
                INTO geoLocation
                (ip, created, countryCode, zip, language, is_eu)
                VALUES (?, ?, ?, ?, ?, ?)";

    $stmt = mysqli_prepare($conn, $sql);
    mysqli_stmt_bind_param($stmt, 'sdssss', $ip, $created, $countryCode, $zip, $language, $is_eu);

    if (!($result = mysqli_stmt_execute($stmt))) {
        error_log("MySQL Write geoLocation SQL: " . $sql);
        error_log("MySQL Write geoLocation Error: " . mysqli_error($conn) . " (" . mysqli_errno($conn) . ")");
    }

    mysqli_stmt_close($stmt);
}

function timeStampUrl($file) {
    if (@file_exists($file)) {
        return $file . "?" . filemtime($file);
    }

    return $file;
}

function my_trim($str) {
    return (trim($str) . ($_SESSION["htmlIndent"] > 0 ? "\n" : ""));
}

function buildDiscogsSearchTerm() {
    if ($_SERVER["REQUEST_METHOD"] == "POST" || $_SERVER["REQUEST_METHOD"] == "GET") {
        unset($_SESSION["advSearch"]);
        $_SESSION["searchTerm"] = "";
        $_SESSION["advSearchTerm"] = "";
    }

    if ($_SERVER["REQUEST_METHOD"] == "POST") {
        if ($_POST["submitBtn"] == "Search") {
            if (empty($_POST['searchTerm'])) {
                return;
            }

            if (strpos($_POST["searchTerm"], "*ADV*") === 0) {
                $_SESSION["advSearchTerm"] = substr($_POST["searchTerm"], 5);
                $_SESSION["advSearch"] = [];
                $_SESSION["searchTerm"] = "";
                $arr = explode(AS_TOKEN, $_SESSION["advSearchTerm"], 5);
                foreach ($arr as $crit) {
                    $s = explode(AS_FIELD, $crit);
                    $_SESSION["searchTerm"] .= " " . $s[1];

                    if ($s[0] === "t") {
                        $_SESSION["advSearch"]["Title"] = $s[1];
                    } else if ($s[0] === "a") {
                        $_SESSION["advSearch"]["Artist"] = $s[1];
                    } else if ($s[0] === "tr") {
                        $_SESSION["advSearch"]["Track"] = $s[1];
                    } else if ($s[0] === "b") {
                        $_SESSION["advSearch"]["Barcode"] = $s[1];
                    } else if ($s[0] === "c") {
                        $_SESSION["advSearch"]["Catno"] = $s[1];
                    } else if ($s[0] === "l") {
                        $_SESSION["advSearch"]["Label"] = $s[1];
                    } else if ($s[0] === "co") {
                        $_SESSION["advSearch"]["Country"] = $s[1];
                    } else if ($s[0] === "y") {
                        $_SESSION["advSearch"]["Year"] = $s[1];
                    }
                }
                $_SESSION["searchTerm"] = trim($_SESSION["searchTerm"]);
            } else {
                $_SESSION["searchTerm"] = searchFriendlyString($_POST['searchTerm']);

                $barcodeType = clsLibGTIN::GTINCheck($_SESSION["searchTerm"], false, 1);
                $barcodeValue = clsLibGTIN::GTINCheck($_SESSION["searchTerm"]);
                if (!empty($barcodeType) && !empty($barcodeValue)) {
                    $_SESSION["advSearch"]["Barcode"] = $barcodeValue;
                    $_SESSION["advSearchTerm"] = "b" . AS_FIELD . $barcodeValue;
                }
            }
        } else if ($_POST["submitBtn"] == "advSearch") {
            $_SESSION["searchTerm"] = searchFriendlyString($_POST["searchTerm"]);
            $_SESSION["advSearch"] = [];
            $arr = [];
            if (!empty($_POST["advTitle"])) {
                $_SESSION["advSearch"]["Title"] = searchFriendlyString($_POST["advTitle"]);
                $arr[] = "t" . AS_FIELD . $_SESSION["advSearch"]["Title"];
            }
            if (!empty($_POST["advArtist"])) {
                $_SESSION["advSearch"]["Artist"] = searchFriendlyString($_POST["advArtist"]);
                $arr[] = "a" . AS_FIELD . $_SESSION["advSearch"]["Artist"];
            }
            if (!empty($_POST["advTrack"])) {
                $_SESSION["advSearch"]["Track"] = searchFriendlyString($_POST["advTrack"]);
                $arr[] = "tr" . AS_FIELD . $_SESSION["advSearch"]["Track"];
            }
            if (!empty($_POST["advBarcode"])) {
                $_SESSION["advSearch"]["Barcode"] = searchFriendlyString($_POST["advBarcode"]);
                $arr[] = "b" . AS_FIELD . $_SESSION["advSearch"]["Barcode"];
            }
            if (!empty($_POST["advCatno"])) {
                $_SESSION["advSearch"]["Catno"] = searchFriendlyString($_POST["advCatno"]);
                $arr[] = "c" . AS_FIELD . $_SESSION["advSearch"]["Catno"];
            }
            if (!empty($_POST["advLabel"])) {
                $_SESSION["advSearch"]["Label"] = searchFriendlyString($_POST["advLabel"]);
                $arr[] = "l" . AS_FIELD . $_SESSION["advSearch"]["Label"];
            }
            if (!empty($_POST["advCountry"])) {
                $_SESSION["advSearch"]["Country"] = searchFriendlyString($_POST["advCountry"]);
                $arr[] = "co" . AS_FIELD . $_SESSION["advSearch"]["Country"];
            }
            if (!empty($_POST["advYear"])) {
                $_SESSION["advSearch"]["Year"] = searchFriendlyString($_POST["advYear"]);
                $arr[] = "y" . AS_FIELD . $_SESSION["advSearch"]["Year"];
            }
            $_SESSION["advSearchTerm"] = join(AS_TOKEN, $arr);
        } else if ($_POST["submitBtn"] == "discogsSearch") {
            if (!empty($_POST['discogsBarcode']) && empty($_POST['discogsTitle']) && empty($_POST['discogsArtist'])) {
                $_SESSION["advSearch"]["Barcode"] = searchFriendlyString($_POST['discogsBarcode']);
                $_SESSION["searchTerm"] = $_SESSION["advSearch"]["Barcode"];
                $_SESSION["advSearchTerm"] = "b" . AS_FIELD . $_SESSION["searchTerm"];
            }
            else
             {
                $_SESSION["searchTerm"] = "";
                $arr = [];
                if (!empty($_POST['discogsTitle'])) {
                    $_SESSION["advSearch"]["Title"] = searchFriendlyString($_POST['discogsTitle']);
                    $arr[] = "t" . AS_FIELD . $_SESSION["advSearch"]["Title"];
                    $_SESSION["searchTerm"] .= trim($_SESSION["advSearch"]["Title"]);
                }

                if (!empty($_POST['discogsTitle']) && !empty($_POST['discogsArtist'])) {
                    $_SESSION["searchTerm"] .= " ";
                }

                if (!empty($_POST['discogsArtist'])) {
                    $_SESSION["advSearch"]["Artist"] = searchFriendlyString($_POST['discogsArtist']);
                    $arr[] = "a" . AS_FIELD . $_SESSION["advSearch"]["Artist"];
                    $_SESSION["searchTerm"] .= trim($_SESSION["advSearch"]["Artist"]);
                }

                $_SESSION["advSearchTerm"] = join(AS_TOKEN, $arr);
            }
        }
    } else if ($_SERVER["REQUEST_METHOD"] == "GET") {
        $_SESSION["searchTerm"] = searchFriendlyString($_GET["q"] ?? "");

        $barcodeType = clsLibGTIN::GTINCheck($_SESSION["searchTerm"], false, 1);
        $barcodeValue = clsLibGTIN::GTINCheck($_SESSION["searchTerm"]);
        if (!empty($barcodeType) && !empty($barcodeValue)) {
            $_SESSION["advSearch"]["Barcode"] = $barcodeValue;
            $_SESSION["advSearchTerm"] = "b" . AS_FIELD . $barcodeValue;
        }
   }
}