<?php

require_once "functions/strings.php";
require_once "functions/date_and_time.php";
require_once "functions/distances.php";
require_once "functions/temperatures.php";

/**
|--------------------------------------------------------------------------
| Global Functions
|--------------------------------------------------------------------------
|
| This is a home for functions that don't belong to any one class and
| that should be available anywhere in the application.
|
*/

if (! function_exists('humanBytes')) {
    /**
     * Convert bytes to a human-friendly format.
     *
     * @since 1.0.0
     *
     * @param int|string $number
     * @param int|string|null $precision  Decimal places to show (optional)
     *
     * @return string
     */
    function humanBytes($number, $precision = null): string
    {
        if (empty($number)) {
            return '0 B';
        }

        static $units = ['B', 'KB', 'MB', 'GB'];
        static $bytePreceision = [0, 0, 1, 2];
        $divisor = 1024;

        for ($i = 0; $number / $divisor >= 0.9 && $i < 4; $i++) {
            $number /= $divisor;
        }
        return round($number, is_null($precision) ? $bytePreceision[$i] : $precision) . $units[$i];
    }
}

if (! function_exists('carbon')) {
    /**
     * Return a Carbon object based on a given timestring.
     * It will attempt to find a timezone in the current
     * session but default to UTC.
     *
     * @since 1.0.0
     *
     * @param string|null $timestring
     *
     * @return \Carbon\Carbon
     */
    function carbon(?string $timestring = null): \Carbon\Carbon
    {
        $carbon = Carbon\Carbon::now(session('timezone_name'));
        if (! empty($timestring)) {
            $carbon = Carbon\Carbon::parse($timestring, session('timezone_name'));
        }
        return $carbon;
    }
}

if (! function_exists('translations')) {
    /**
     * Loads translations from a JSON file.
     *
     * @since 1.0.0
     *
     * @param string $jsonFilePath
     *
     * @return array
     */
    function translations(string $jsonFilePath): array
    {
        if (! file_exists($jsonFilePath)) {
            return [];
        }

        $payload = [];
        $jsonData = json_decode(file_get_contents($jsonFilePath), true);

        switch (json_last_error()) {
            case JSON_ERROR_NONE:
                $payload = $jsonData;
                break;
            case JSON_ERROR_DEPTH:
                throw new \Exception('Maximum stack depth exceeded');
                break;
            case JSON_ERROR_STATE_MISMATCH:
                throw new \Exception('Underflow or the modes mismatch');
                break;
            case JSON_ERROR_CTRL_CHAR:
                throw new \Exception('Unexpected control character found');
                break;
            case JSON_ERROR_SYNTAX:
                throw new \Exception('Syntax error, malformed JSON');
                break;
            case JSON_ERROR_UTF8:
                throw new \Exception('Malformed UTF-8 characters, possibly incorrectly encoded');
                break;
            default:
                throw new \Exception('Generic or unknown JSON error while decoding');
                break;
        }

        return $payload;
    }
}

if (! function_exists('is_serialized')) {
    /**
     * Check a value to find if it was serialized.
     *
     * @since 1.0.0
     *
     * @param mixed $data
     * @param bool $strict
     *
     * @return bool
     */
    function is_serialized($data, bool $strict = true): bool
    {
        // If it isn't a string, it isn't serialized.
        if (! is_string($data)) {
            return false;
        }

        $data = trim($data);
        if ($data === 'N;') {
            return true;
        }
        if (strlen($data) < 4) {
            return false;
        }
        if ($data[1] !== ':') {
            return false;
        }
        if ($strict) {
            $lastc = substr($data, -1);
            if ($lastc !== ';' && $lastc !== '}') {
                return false;
            }
        } else {
            $semicolon = strpos($data, ';');
            $brace = strpos($data, '}');
            // Either ; or } must exist.
            if (! $semicolon && ! $brace) {
                return false;
            }
            // But neither must be in the first X characters.
            if ($semicolon && $semicolon < 3) {
                return false;
            }
            if ($brace && $brace < 4) {
                return false;
            }
        }
        $token = $data[0];
        switch ($token) {
            case 's':
                if ($strict) {
                    if (substr($data, -2, 1) !== '"') {
                        return false;
                    }
                } elseif (! strpos($data, '"')) {
                    return false;
                }
                // Or else fall through.
            case 'a':
            case 'O':
                return (bool) preg_match("/^{$token}:[0-9]+:/s", $data);
            case 'b':
            case 'i':
            case 'd':
                $end = $strict ? '$' : '';
                return (bool) preg_match("/^{$token}:[0-9.E+-]+;$end/", $data);
        }
        return false;
    }
}

if (! function_exists('maybe_unserialize')) {
    /**
     * Unserialize data only if it was serialized. Will return
     * an array if it was a serialized string, otherwise it
     * will return whatever was passed into the function
     * leaving it untouched.
     *
     * @since 1.0.0
     *
     * @param mixed $data
     *
     * @return mixed
     */
    function maybe_unserialize($data)
    {
        if (! is_string($data)) {
            return false;
        }

        $data = trim($data);
        // Don't attempt to unserialize data that wasn't serialized going in.
        if (is_serialized($data)) {
            return unserialize(trim($data));
        }
        return $data;
    }
}
