<?php /*

 Composr
 Copyright (c) Christopher Graham, 2004-2024

 See docs/LICENSE.md for full licensing information.


 NOTE TO PROGRAMMERS:
   Do not edit this file. If you need to make changes, save your changed file to the appropriate *_custom folder
   **** If you ignore this advice, then your website upgrades (e.g. for bug fixes) will likely kill your changes ****

*/

/**
 * @license    http://opensource.org/licenses/cpal_1.0 Common Public Attribution License
 * @copyright  Christopher Graham
 * @package    core
 */

/*EXTRA FUNCTIONS: timezone_identifiers_list|date_create|*/

/**
 * Turn a boring timezone name into the pretty shortened list of ones Microsoft uses and others now use too.
 *
 * @param  string $in Boring name
 * @return string Nice name
 */
function make_nice_timezone_name(string $in) : string
{
    $list = get_timezone_list();
    if (array_key_exists($in, $list)) {
        return $list[$in];
    }
    return $in;
}

/**
 * Generate and return a list of timezones.
 * This also saves into the cache for quick access from get_timezone_list().
 *
 * @return array Timezone (map between boring-style and human-readable name). Sorted in offset order then likelihood order.
 * @ignore
 */
function _get_timezone_list() : array
{
    // Specific city name overrides
    $overrides = [
        'Antarctica/DumontDUrville' => 'Dumont d\'Urville'
    ];

    $ret = [];

    $timezones = timezone_identifiers_list();
    foreach ($timezones as $timezone) {
        if ($timezone == 'UTC') { // Special meaning
            continue;
        }

        // Get the timezone offset
        $date_tz = new DateTimeZone($timezone);
        $datetime = date_create('now', $date_tz);
        $_offset = $date_tz->getOffset($datetime);

        // Create the offset text
        $offset = floatval($_offset) / 3600.0;
        $offset_text = ' (UTC';
        $offset_text .= ($offset < 0.0) ? '-' : '+';
        $offset_abs = abs($offset);
        $hours = intval(floor($offset_abs));
        $offset_text .= str_pad(strval($hours), 2, '0', STR_PAD_LEFT);
        $offset_text .= ':';
        $offset_text .= str_pad(strval(abs($hours - $offset_abs) * 100), 2, '0', STR_PAD_LEFT);
        $offset_text .= ')';

        // Add to array
        $city = array_key_exists($timezone, $overrides) ? $overrides[$timezone] : cms_mb_ucwords(str_replace('_', ' ', preg_replace('#^.*/#', '', $timezone)));
        $ret[$timezone] = $city . $offset_text;

        // Free up memory
        unset($date_tz);
        unset($datetime);
    }

    require_code('caches2');
    set_cache_entry('timezone_list', (60 * 24), serialize(['type' => 'flat']), $ret);

    return $ret;
}

/**
 * Sanitise a POST inputted date, and get the date/time components.
 * This does not support time zones. Use post_param_date_components_tz instead.
 *
 * @param  ID_TEXT $stub The stub of the parameter name (stub_year, stub_month, stub_day, stub_hour, stub_minute)
 * @param  ?integer $year Default year (null: none)
 * @param  ?integer $month Default month (null: none)
 * @param  ?integer $day Default day (null: none)
 * @param  boolean $get_also Whether to allow over get parameters also
 * @return array The date/time components
 */
function post_param_date_components(string $stub, ?int $year = null, ?int $month = null, ?int $day = null, bool $get_also = false) : array
{
    $default_ret = [$year, $month, $day, 0, 0, 0];

    if ($get_also) {
        $date = either_param_string($stub, null);
        if ($date !== null) { // HTML5 input style
            $matches = [];
            if (preg_match('#^(\d\d\d\d)-(\d\d)-(\d\d)$#', $date, $matches) == 0) {
                return $default_ret;
            }

            $year = intval($matches[1]);
            $month = intval($matches[2]);
            $day = intval($matches[3]);

            $time = either_param_string($stub . '_time', null);
            if (($time !== null) && (preg_match('#^(\d\d):(\d\d)(:| |$)#', $time, $matches) != 0)) {
                $hour = intval($matches[1]);
                $minute = intval($matches[2]);
                if ((strpos($time, 'AM') !== false) || (strpos($time, 'PM') !== false)) {
                    if ($hour == 12) {
                        $hour = 0;
                    }
                    if (strpos($time, 'PM') !== false) {
                        $hour += 12;
                    }
                }
                $seconds = 0;
            } else {
                $hour = null;
                $minute = null;
                $seconds = null;
            }
        } else { // Legacy input style
            $year = either_param_integer($stub . '_year', null);
            if ($year === null) {
                return $default_ret;
            }
            $month = either_param_integer($stub . '_month', null);
            if ($month === null) {
                return $default_ret;
            }
            $day = either_param_integer($stub . '_day', null);
            if ($day === null) {
                return $default_ret;
            }

            $hour = either_param_integer($stub . '_hour', null);
            $minute = either_param_integer($stub . '_minute', null);
            $seconds = either_param_integer($stub . '_seconds', null);
        }
    } else {
        $date = post_param_string($stub, null);
        if ($date !== null) { // HTML5 input style
            $matches = [];
            if (preg_match('#^(\d\d\d\d)-(\d\d)-(\d\d)$#', $date, $matches) == 0) {
                return $default_ret;
            }

            $year = intval($matches[1]);
            $month = intval($matches[2]);
            $day = intval($matches[3]);

            $time = post_param_string($stub . '_time', null);
            if (($time !== null) && (preg_match('#^(\d\d):(\d\d)(:| |$)#', $time, $matches) != 0)) {
                $hour = intval($matches[1]);
                $minute = intval($matches[2]);
                if ((strpos($time, 'AM') !== false) || (strpos($time, 'PM') !== false)) {
                    if ($hour == 12) {
                        $hour = 0;
                    }
                    if (strpos($time, 'PM') !== false) {
                        $hour += 12;
                    }
                }
                $seconds = 0;
            } else {
                $hour = null;
                $minute = null;
                $seconds = null;
            }
        } else { // Legacy input style
            $year = post_param_integer($stub . '_year', null);
            if ($year === null) {
                return $default_ret;
            }
            $month = post_param_integer($stub . '_month', null);
            if ($month === null) {
                return $default_ret;
            }
            $day = post_param_integer($stub . '_day', null);
            if ($day === null) {
                return $default_ret;
            }

            $hour = post_param_integer($stub . '_hour', null);
            $minute = post_param_integer($stub . '_minute', null);
            $seconds = post_param_integer($stub . '_seconds', null);
        }
    }

    if ($hour === null) {
        if (strpos($stub, 'end') !== false) {
            $hour = 23;
            $minute = 59;
            $seconds = 0;
        } else {
            $hour = 0;
            $minute = 0;
            $seconds = 0;
        }
    }

    if (!checkdate($month, $day, $year)) {
        warn_exit(do_lang_tempcode('INVALID_DATE_GIVEN'));
    }

    return [$year, $month, $day, $hour, $minute, $seconds];
}

/**
 * Sanitise a POST inputted date, and get the Unix timestamp for the inputted date.
 *
 * @param  ID_TEXT $stub The stub of the parameter name (stub_year, stub_month, stub_day, stub_hour, stub_minute)
 * @param  boolean $get_also Whether to allow over get parameters also
 * @param  boolean $do_timezone_conversion Whether to do timezone conversion
 * @return ?TIME The timestamp of the date (null: no input date was chosen)
 * @ignore
 */
function _post_param_date(string $stub, bool $get_also = false, bool $do_timezone_conversion = true) : ?int
{
    require_code('temporal');

    $timezone = post_param_string($stub . '_timezone', get_users_timezone());

    list($year, $month, $day, $hour, $minute, $seconds) = post_param_date_components($stub, null, null, null, $get_also);
    if ($year === null) {
        return null;
    }

    $time = cms_mktime($hour, $minute, $seconds, $month, $day, $year);
    if ($do_timezone_conversion) {
        $time = $time * 2 - tz_time($time, $timezone);
    }

    return $time;
}

/**
 * Sanitise a POST inputted date with a timezone, and get the UTC date components for the inputted date.
 *
 * @param  ID_TEXT $stub The stub of the parameter name (stub_year, stub_month, stub_day, stub_hour, stub_minute)
 * @param  boolean $get_also Whether to allow over get parameters also
 * @return array The date/time components (null: no input date was chosen)
 */
function post_param_date_components_utc(string $stub, bool $get_also = false) : ?array
{
    $timestamp = _post_param_date($stub, $get_also, true);
    if ($timestamp === null) {
        return null;
    }

    require_code('temporal');

    $date = cms_date('Y-n-j-G-i-s', $timestamp);
    list($year, $month, $day, $hour, $minute, $second) = explode("-", $date);

    return [intval($year), intval($month), intval($day), intval($hour), intval($minute), intval($second)];
}
