<?PHP
/**
 * Main file for functions.
 *
 * @file
 *
 * @author Joshua Ramon Enslin <joshua@jrenslin.de>
 */

require __DIR__ . "/standardHTML.php";
require __DIR__ . "/mdEmbeds.php";

/**
 * Function ensureDir checks if a directory exists, and creates it if it does not yet.
 *
 * @param string $filepath File path.
 *
 * @return void
 */
function ensureDir(string $filepath) {
    if (!is_dir($filepath)) mkdir($filepath);
}

/**
 * Function ensureJson checks that a JSON file exists. If not, it creates an empty one at the specified location.
 *
 * @param string $filepath File path to the JSON file.
 *
 * @return void
 */
function ensureJson(string $filepath) {

    if (!file_exists($filepath) or filesize($filepath) < 2) {
        file_put_contents($filepath, "[]");
    }

}

/**
 * Function for ensuring the existence of an appropriate environment.
 *
 * @return void
 */
function ensureEnvironment() {

    // Enable error reporting

    error_reporting(E_ALL);
    ini_set("display_errors", 1);

    // Ensure existence of directories

    foreach ([__DIR__ . "/../files", __DIR__ . "/../data", __DIR__ . "/../data/static", __DIR__ . "/../data/caches", __DIR__ . "/../js"] as $folder) {
        ensureDir($folder);
    }

    // Ensure existence of settings files

    foreach ([
        __DIR__ . "/../data/settings.json",
        __DIR__ . "/../data/caches/pages.json",
        __DIR__ . "/../data/caches/publicPages.json",
        __DIR__ . "/../data/users.json",
    ] as $jsonFile) {
        ensureJson($jsonFile);
    }

    // Load settings

    $settings = array_merge(
        [
            "startPage"             => "1",
            "pageTitle"             => "md:cms",
            "logo"                  => "",
            "url"                   => "",
            "css"                   => "default",
            "defaultLang"           => "en",
            "cacheRefreshInterval"  => 0,
            "mdVersion"             => "https://rlp.museum-digital.de/",
            "mdImgFolder"           => "https://rlp.museum-digital.de/data/rlp/",
            "hideInstitution"       => 0,
            "limitToInstitutions"   => [],
            "sendHTTPHeaders"       => 1,
            "CSPimageSources"       => "",
            "CSPobjectSources"      => "",
            "maxFileSize"           => 300000,
        ],
        json_decode(file_get_contents(__DIR__ . "/../data/settings.json"), true)
    );

    $GLOBALS['settings'] = $settings;

    if ($settings['sendHTTPHeaders']) {
        header('X-Content-Type-Options: nosniff');
        header('X-XSS-Protection: 1; mode=block');
        header('Strict-Transport-Security: max-age=31536000; preload');
        header('Referrer-Policy: strict-origin');
    }

}

/**
 * Function for loading contents from cache.
 *
 * @return array
 */
function loadPages() {

    $pages = json_decode(file_get_contents(__DIR__ . "/../data/caches/pages.json"), true);
    return $pages;

}

/**
 * Function for loading contents from cache.
 *
 * @return array
 */
function loadPublicPages() {

    $pages = json_decode(file_get_contents(__DIR__ . "/../data/caches/publicPages.json"), true);
    return $pages;

}

/**
 * Query or cache pages.
 *
 * @param string $url      URL to query.
 * @param string $area     The type of the queried page. If caching is enabled, renew cache every X seconds.
 * @param array  $settings Settings variable.
 *
 * @return array
 */
function queryCachePage(string $url, string $area = "", array $settings = ['cacheRefreshInterval' => 0]) {

    // Ignore caching if cacheRefreshInterval equals zero.
    if ($settings['cacheRefreshInterval'] == 0) {
        return file_get_contents($url);
    }

    $fileDir = __DIR__ . "/../data/caches/$area";
    ensureDir($fileDir);

    $fileName = md5($url);
    $filePath = "$fileDir/$fileName.json";

    // Load from cache.
    if (file_exists($filePath) && time() - filemtime($filePath) < $settings['cacheRefreshInterval']) {
        return file_get_contents($filePath);
    }

    // Refresh cache.

    $contents = file_get_contents($url);
    file_put_contents($filePath, $contents, LOCK_EX);

    return $contents;

}

/**
 * Function scanDirConts is a wrapper around scandir(), which removes [".", ".."].
 *
 * @param string $folder Folder to scan.
 *
 * @return string[]
 */
function scanDirConts(string $folder):array {
    return array_values(array_diff(scandir($folder), [".", "..", ".git"]));
}


/**
 * Function printHiddenInputs takes an array, in which each entry stands for a new hidden input field.
 * The key to the entry stands for the name attribute, the value for the value attribute.
 *
 * @param array $array  Associative array containing all the variable names and corresponding values to print
 * @param int   $indent Integer describing how far each line in the returned string should be indented.
 *
 * @return string
 */
function printHiddenInputs(array $array, int $indent = 0):string {

    $output = '';
    $indentString = PHP_EOL;

    for ($i = 0; $i < $indent; $i++) $indentString .= ' ';
    foreach ($array as $name => $value) {
        $output .= $indentString . '<input type="hidden" name="'.$name.'" value="'.$value.'" />';
    }
    return $output;

}

/**
 * Function printHiddenInputsFromGlobalsSimple takes an array, in which each entry stands for a new hidden input field.
 * The value stands for the name attribute of the input field and the key in $GLOBALS.
 *
 * @param array $array  Input array.
 * @param int   $indent Number of spaces for indenting the output input fields in HTML. Optional.
 *
 * @return string
 */
function printHiddenInputsFromGlobalsSimple(array $array, int $indent = 0):string {

    $output = '';
    $indentString = PHP_EOL;

    for ($i = 0; $i < $indent; $i++) $indentString .= ' ';
    foreach ($array as $value) {
        if (!isset($GLOBALS[$value])) continue;
        $output .= $indentString . '<input type="hidden" name="'.$value.'" value="'.$GLOBALS[$value].'" />';
    }
    return $output;

}

/**
 * Function write_get_vars prints checks for GET variables specified in the input array and returns them as a single string.
 * Useful for avoiding long blocks of links working to write meaningful links.
 *
 * @param string[] $input Input array: both simple and associative arrays are accepted.
 *
 * @return string
 */
function write_get_vars(array $input):string {

    // Check if keys have been specified in the array (in Python terms, if it is a dict or a list).
    // If keys are not specified, write new working variable $vars with keys equaling the value.
    // $str is the string that will eventually be returned.
    $vars = array();
    $str = '';

    if (isset($input[0])) {
        foreach ($input as $value) $vars[$value] = $value;
    }
    else $vars = $input;

    // For each of the variables specified in $vars, check if a corresponding GET variable is set.
    // If so, add that to the return string.
    // The key is used in place of the original GET variable's name ($value),
    // because some pages may have the same GET variables carry different names.
    foreach ($vars as $key => $value) {
        if (isset($GLOBALS['_GET'][$value])) $str .= '&'.$key.'='.$GLOBALS['_GET'][$value];
    }
    return ($str);

}

/**
 * Function write_post_vars prints checks for POST variables specified in the input
 * array and returns them as a single string.
 * Useful for avoiding long blocks of links working to write meaningful links.
 *
 * @param string[] $input Input array: both simple and associative arrays are accepted.
 *
 * @return string
 */
function write_post_vars(array $input):string {

    // Check if keys have been specified in the array (in Python terms, if it is a dict or a list).
    // If keys are not specified, write new working variable $vars with keys equaling the value.
    // $str is the string that will eventually be returned.
    $vars = array();
    $str = '';

    if (isset($input[0])) {
        foreach ($input as $value) $vars[$value] = $value;
    }
    else $vars = $input;

    // For each of the variables specified in $vars, check if a corresponding GET variable is set.
    // If so, add that to the return string. The key is used in place of the original POST variable's name ($value),
    // because some pages may have the same GET variables carry different names.
    foreach ($vars as $key => $value) {
        if (isset($GLOBALS['_POST'][$value])) $str .= '&'.$key.'='.$GLOBALS['_POST'][$value];
    }
    return ($str);

}

/**
 * Function write_common_vars prints checks for global variables specified in the input
 * array and returns them as a single string.
 * Useful for avoiding long blocks of links working to write meaningful links.
 *
 * @param string[] $input Input array: both simple and associative arrays are accepted.
 *
 * @return string
 */
function write_common_vars(array $input):string {

    // Check if keys have been specified in the array (in Python terms, if it is a dict or a list).
    // If keys are not specified, write new working variable $vars with keys equaling the value.
    // $str is the string that will eventually be returned.
    $vars = array();
    $str = '';

    if (isset($input[0])) {
        foreach ($input as $value) $vars[$value] = $value;
    }
    else $vars = $input;

    // For each of the variables specified in $vars, check if a corresponding GET variable is set.
    // If so, add that to the return string. The key is used in place of the original GET variable's name ($value),
    // because some pages may have the same GET variables carry different names.
    foreach ($vars as $key => $value) {
        if (isset($GLOBALS[$value])) $str .= '&'.$key.'='.$GLOBALS[$value];
    }
    return ($str);

}

/**
 * Function for e.g. printing the navigation.
 *
 * @param array    $pages             Array of all pages to list.
 * @param function $start             Function to run on the called pages in case a new level is entered.
 * @param function $end               Function to run on the called pages in case a new level is entered.
 * @param function $callbackSameLevel Function to run on the called pages of the current level.
 * @param integer  $higher            Run the callback function on those entries where higher equals the given.
 *
 * @return string
 */
function buildPageOrder(
    array $pages,
    $start,
    $end,
    $callbackSameLevel,
    int $higher = 0
):string {

    $output  = "";

    $thisLevel = [];
    foreach ($pages as $page) {
        if ($page["higher"] != $higher) continue;

        $output .= $callbackSameLevel($page, buildPageOrder($pages, $start, $end, $callbackSameLevel, $page["id"]));

    }

    if ($output) $output = $start() . $output . $end();
    return $output;

}

/**
 * Function for checking access to previews.
 *
 * @param bool $sessionStarted Optional parameter depending on the status of the session.
 *
 * @return bool
 */
function checkPreviewAccess($sessionStarted = false) {

    if (!$sessionStarted) {
        session_start();
    }

    if (isset($_SESSION['username'])) $output = true;
    else $output = false;

    if (!$sessionStarted) {
        session_abort();
    }

    return $output;

}

/**
 * Function checking if a string starts with another.
 *
 * @param string $haystack String to check.
 * @param string $needle   Potential start of $haystack.
 *
 * @return boolean
 */
function startswith(string $haystack, string $needle):bool {
    if (substr($haystack, 0, strlen($needle)) == $needle) return (true);
    else return (false);
}

/**
 * Function checking if a string starts with any input from the input array.
 *
 * @param string   $haystack String to check.
 * @param string[] $needles  Array containing potential start values of $haystack.
 *
 * @return boolean
 */
function startswithAny(string $haystack, array $needles):bool {
    $output = false;
    foreach ($needles as $needle) {
        $output = startswith($haystack, $needle);
        if ($output == true) return $output;
    }
    return $output;
}

/**
 * Curling web pages.
 * Function to check errors.
 *
 * @param string $url  URL to query.
 * @param string $host Authentication data. Optional.
 *
 * @return string
 */
function runCurl(string $url, string $host = ""):string {

    $curl = curl_init();

    curl_setopt($curl, CURLOPT_URL, $url);
    // curl_setopt($curl, CURLOPT_RESOLVE, ["www.example.com:443:172.16.1.1"]);
    curl_setopt($curl, CURLOPT_ENCODING, '');
    if ($host) curl_setopt($ch, CURLOPT_HTTPHEADER, array("Host: $host"));
    curl_setopt($curl, CURLOPT_RETURNTRANSFER, 1);
    $result = curl_exec($curl);

    curl_close($curl);
    return $result;

}

/**
 * This function cuts down a string and adds a period in case it's longer than length to create a snippet.
 *
 * @param string $string Input text to cut down
 * @param int    $length Length of the snippet to create
 *
 * @return string
 */
function createTextSnippet($string, $length) {

    if (strlen($string) > $length) {
        $string  = substr($string, 0, $length);
        $string  = substr($string, 0, strrpos($string, ' '));
        $string .= ' ...';
    }
    return $string;

}

?>
