<?php

/* --------------------------------------------------------------------

  This file is part of Chevereto Free.
  https://chevereto.com/free

  (c) Rodolfo Berrios <rodolfo@chevereto.com>

  For the full copyright and license information, please view the LICENSE
  file that was distributed with this source code.

  --------------------------------------------------------------------- */

namespace CHV\Render;

use G;
use CHV;

if (!defined('access') or !access) {
    die('This file cannot be directly accessed.');
}

/* ---------------------------------------------------------------------------------------------------------------------------------------- */
/*** STRING DATA FUNCTIONS ***/

function get_email_body_str($file)
{
    ob_start();
    G\Render\include_theme_file($file);
    $mail_body = ob_get_contents();
    ob_end_clean();

    return $mail_body;
}

// For inline JS and CSS code from a given file
function get_theme_inline_code($file, $type = null)
{
    if (!isset($type)) {
        $type = pathinfo(rtrim($file, '.php'), PATHINFO_EXTENSION);
    }
    if (!CHV\getSetting('minify_enable') or !in_array($type, ['js', 'css'])) {
        G\Render\include_theme_file($file);
    } else {
        $ob_start = ob_start();
        G\Render\include_theme_file($file);
        if ($ob_start) {
            $code = ob_get_clean();
            ob_flush();
            if ($code) {
                return get_cond_minified_code($code, $type);
            }
        }
    }
}
function show_theme_inline_code($file, $type = null)
{
    G\Render\include_theme_file($file);
    // echo get_theme_inline_code($file, $type); // Don't minify this (saves execution time and is not needed)
}

/* ---------------------------------------------------------------------------------------------------------------------------------------- */
/*** THEME DATA FUNCTIONS ***/

function get_theme_file_url($file, $options = [])
{
    $filepath = G_APP_PATH_THEME . $file;
    $filepath_override = G_APP_PATH_THEME . 'overrides/' . $file;
    if (file_exists($filepath_override)) {
        $filepath = $filepath_override;
    }

    return get_static_url($filepath, $options);
}

function get_static_url($filepath, $options = [])
{
    $options = array_merge(['versionize' => true, 'minify' => null], $options);
    if ($options['minify'] !== false) {
        $filepath = get_cond_minified_file($filepath, $options['forced']); // Handle the conditional minify
    }
    $return = G\absolute_to_url($filepath, defined('CHV_ROOT_URL_STATIC') ? CHV_ROOT_URL_STATIC : null);
    if ($options['versionize']) {
        $return = versionize_src($return);
    }

    return $return;
}

function get_cond_minified_file($filepath, $forced = false)
{
    // Check for theme override
    if (G\starts_with(G_APP_PATH_THEME, $filepath)) {
        $filepath_override = G\str_replace_first(G_APP_PATH_THEME, G_APP_PATH_THEME . 'overrides/', $filepath);
        if (file_exists($filepath_override)) {
            $filepath = $filepath_override;
        }
    }
    if (!CHV\getSetting('minify_enable')) {
        return $filepath;
    }

    return get_minified($filepath, ['forced' => $forced, 'output' => 'file']);
}
function get_cond_minified_code($code, $type = 'js')
{
    if (!CHV\getSetting('minify_enable')) {
        return $code;
    }

    return get_minified($code, ['source_method' => 'inline', 'source_type' => $type, 'output' => 'inline']);
}

function get_minified($var, $options = [])
{
    $options = array_merge(['source_method' => 'file', 'forced' => false], (array) $options);
    try {
        $minify = new G\Minify(array_merge($options, [
            'source' => $var,
        ]));
        $minify->exec();
        $var = $minify->result;
    } catch (G\MinifyException $e) {
        error_log($e->getMessage());
    }

    return $var;
}

function theme_file_exists($var)
{
    return file_exists(G_APP_PATH_THEME . $var);
}

/* ---------------------------------------------------------------------------------------------------------------------------------------- */
/*** HTML TAGS ***/

function get_html_tags()
{
    $classes = 'device-' . (G\Handler::getCond('mobile_device') ? 'mobile' : 'nonmobile') . ' tone-' . G\Handler::getVar('theme_tone') . ' top-bar-' . G\Handler::getVar('theme_top_bar_color') . ' unsafe-blur-' . (CHV\getSetting('theme_nsfw_blur') ? 'on' : 'off');

    return get_lang_html_tags() . ' class="' . $classes . '"';
}

/* ---------------------------------------------------------------------------------------------------------------------------------------- */
/*** LANGUAGE TAGS ***/

function get_lang_html_tags()
{
    $lang = CHV\get_language_used();

    return 'xml:lang="' . $lang['base'] . '" lang="' . $lang['base'] . '" dir="' . $lang['dir'] . '"';
}

/* ---------------------------------------------------------------------------------------------------------------------------------------- */
/*** FORM ASSETS ***/

function get_select_options_html($arr, $selected)
{
    $html = '';
    foreach ($arr as $k => $v) {
        $selected = is_bool($selected) ? ($selected ? 1 : 0) : $selected;
        $html .= '<option value="' . $k . '"' . ($selected == $k ? ' selected' : '') . '>' . $v . '</option>' . "\n";
    }

    return $html;
}

function get_checkbox_html($options = [])
{
    if (!array_key_exists('name', $options)) {
        return 'ERR:CHECKBOX_NAME_MISSING';
    }

    $options = array_merge([
        'value_checked' => 1,
        'value_unchecked' => 0,
        'label' => $options['name'],
        'checked' => false,
    ], $options);

    $tooltip = $options['tooltip'] ? (' rel="tooltip" title="' . $options['tooltip'] . '"') : null;

    $html = '<div class="checkbox-label">' . "\n" .
        '	<label for="' . $options['name'] . '"' . $tooltip . '>' . "\n" .
        '		<input type="hidden" name="' . $options['name'] . '" value="' . $options['value_unchecked'] . '">' . "\n" .
        '		<input type="checkbox" name="' . $options['name'] . '" id="' . $options['name'] . '" ' . ((bool) $options['checked'] ? ' checked' : null) . ' value="' . $options['value_checked'] . '">' . $options['label'] . "\n" .
        '	</label>' . "\n" .
        '</div>';

    return $html;
}

function get_recaptcha_component($id = 'g-recaptcha')
{
    if (CHV\getSetting('recaptcha_version') == 3) {
        return ['recaptcha_invisible_html', get_recaptcha_invisible_html()];
    }

    return ['recaptcha_html', get_recaptcha_html($id)];
}

function get_recaptcha_invisible_html()
{
    return '<script>
    const recaptchaAction = "' . G\get_route_name() . '";
    const recaptchaLocal = "' . G\get_base_url('recaptcha-verify') . '";
    grecaptcha.ready(function() {
        grecaptcha.execute("' . CHV\getSetting('recaptcha_public_key') . '", {action: recaptchaAction})
        .then(function(token) {
            fetch(recaptchaLocal + "/?action=" + recaptchaAction + "&token="+token).then(function(response) {
                response.json().then(function(data) {
                    console.log(data);
                });
            });
        });
    });
    </script>';
}

function get_recaptcha_html($id = 'g-recaptcha')
{
    return strtr('<div id="%id" data-recaptcha-element class="g-recaptcha"></div>', ['%id' => $id]);
}

/**
 * ----------------------------------------------------------------------------------------------------------------------------------------
 * ----------------------------------------------------------------------------------------------------------------------------------------
 * ----------------------------------------------------------------------------------------------------------------------------------------.
 */
function get_share_links($share_element)
{
    if (function_exists('get_share_links')) {
        return \get_share_links($share_element);
    }

    if (!$share_element['twitter']) {
        $share_element['twitter'] = CHV\getSetting('twitter_account');
    }

    $share_element['urlencoded'] = array();

    foreach ($share_element as $key => $value) {
        if ($key == 'urlencoded') {
            continue;
        }
        $share_element['urlencoded'][$key] = rawurlencode($value);
    }

    global $share_links_networks;
    G\Render\include_theme_file('custom_hooks/share_links');

    if (!$share_links_networks) {
        $share_links_networks = array(
            'facebook' => array(
                'url' => 'http://www.facebook.com/share.php?u=%URL%',
                'label' => 'Facebook',
            ),
            'twitter' => array(
                'url' => 'https://twitter.com/intent/tweet?original_referer=%URL%&url=%URL%&text=%TITLE%' . ($share_element['twitter'] ? '&via=%TWITTER%' : null),
                'label' => 'Twitter',
            ),
            'whatsapp'    => array(
                'url'    => 'whatsapp://send?text=%TITLE% - ' . _s('view on %s', G\safe_html(CHV\getSetting('website_name'))) . ': %URL%',
                'label' => 'WhatsApp',
                'mobileonly' => true,
            ),
            'reddit' => array(
                'url' => 'http://reddit.com/submit?url=%URL%',
                'label' => 'reddit',
            ),
            'vk' => array(
                'url' => 'http://vk.com/share.php?url=%URL%',
                'label' => 'VK',
            ),
            'blogger' => array(
                'url' => 'http://www.blogger.com/blog-this.g?n=%TITLE%&source=&b=%HTML%',
                'label' => 'Blogger',
            ),
            'tumblr' => array(
                'url' => 'http://www.tumblr.com/share/photo?source=%PHOTO_URL%&caption=%TITLE%&clickthru=%URL%&title=%TITLE%',
                'label' => 'Tumblr.',
            ),
            'pinterest' => array(
                'url' => 'http://www.pinterest.com/pin/create/bookmarklet/?media=%PHOTO_URL%&url=%URL%&is_video=false&description=%DESCRIPTION%&title=%TITLE%',
                'label' => 'Pinterest',
            ),
            'stumbleupon' => array(
                'url' => 'http://www.stumbleupon.com/submit?url=%URL%',
                'label' => 'StumbleUpon',
            ),
            'mail' => array(
                'url' => 'mailto:?subject=%TITLE%&body=%URL%',
                'label' => 'Email',
            ),
        );
    }

    $return = array();

    foreach ($share_links_networks as $key => $value) {
        $search = array('%URL%', '%TITLE%', '%DESCRIPTION%', '%HTML%', '%PHOTO_URL%', '%TWITTER%');
        $replace = array('url', 'title', 'description', 'HTML', 'image', 'twitter');

        for ($i = 0; $i < count($replace); ++$i) {
            if (array_key_exists($replace[$i], $share_element['urlencoded'])) {
                $replace[$i] = $share_element['urlencoded'][$replace[$i]];
            }
        }

        $value['url'] = str_replace($search, $replace, $value['url']);

        $return[] = '<li' . ($value['mobileonly'] ? ' class="hidden phone-show"' : null) . '><a data-href="' . $value['url'] . '" class="popup-link btn-32 btn-social btn-' . $key . '" rel="tooltip" data-tiptip="top" title="' . $value['label'] . '"><span class="btn-icon icon-' . $key . '"></span></a></li>';
    }

    return $return;
}

/**
 * PEAFOWL FRAMEWORK
 * ----------------------------------------------------------------------------------------------------------------------------------------.
 */
function include_peafowl_head()
{
    echo    '<meta name="generator" content="' . G_APP_NAME . ' ' . CHV\get_chevereto_version() . '">' . "\n" .
        '<link rel="stylesheet" href="' . get_static_url(CHV_PATH_PEAFOWL . 'peafowl.css') . '">' . "\n" .
        '<link rel="stylesheet" href="' . get_theme_file_url('style.css') . '">' . "\n\n" .
        '<script data-cfasync="false">document.documentElement.className+=" js";var devices=["phone","phablet","tablet","laptop","desktop","largescreen"],window_to_device=function(){for(var e=[480,768,992,1200,1880,2180],t=[],n="",d=document.documentElement.clientWidth||document.getElementsByTagName("body")[0].clientWidth||window.innerWidth,c=0;c<devices.length;++c)d>=e[c]&&t.push(devices[c]);for(0==t.length&&t.push(devices[0]),n=t[t.length-1],c=0;c<devices.length;++c)document.documentElement.className=document.documentElement.className.replace(devices[c],""),c==devices.length-1&&(document.documentElement.className+=" "+n),document.documentElement.className=document.documentElement.className.replace(/\s+/g," ");if("laptop"==n||"desktop"==n){var o=document.getElementById("pop-box-mask");null!==o&&o.parentNode.removeChild(o)}};window_to_device(),window.onresize=window_to_device;function jQueryLoaded(){!function(n,d){n.each(readyQ,function(d,e){n(e)}),n.each(bindReadyQ,function(e,i){n(d).bind("ready",i)})}(jQuery,document)}!function(n,d,e){function i(d,e){"ready"==d?n.bindReadyQ.push(e):n.readyQ.push(d)}n.readyQ=[],n.bindReadyQ=[];var u={ready:i,bind:i};n.$=n.jQuery=function(n){return n===d||void 0===n?u:void i(n)}}(window,document);
            </script>' . "\n\n";
    if (G\Handler::getCond('captcha_show') && CHV\getSetting('recaptcha_version') == 3) {
        echo '<script src="https://www.google.com/recaptcha/api.js?render=' . CHV\getSetting('recaptcha_public_key') . '"></script>';
    }
}
// Get cookie law banner
function get_cookie_law_banner()
{
    return '<div id="cookie-law-banner" data-cookie="CHV_COOKIE_LAW_DISPLAY"><div class="c24 center-box position-relative"><p class="">' . _s('We use our own and third party cookies to improve your browsing experience and our services. If you continue using our website is understood that you accept this %cookie_policy_link.', ['%cookie_policy_link' => '<a href="' . G\get_base_url('page/privacy') . '">' . _s('cookie policy') . '</a>']) . '</p><a data-action="cookie-law-close" title="' . _s('I understand') . '" class="cookie-law-close"><span class="icon icon-close"></span></a></div></div>' . "\n\n";
}

// Sensitive Cookie law display
function display_cookie_law_banner()
{
    if (!CHV\getSetting('enable_cookie_law') or CHV\Login::getUser()) {
        return;
    }
    // No user logged in and cookie law has not been accepted
    if (!isset($_COOKIE['CHV_COOKIE_LAW_DISPLAY']) or (bool) $_COOKIE['CHV_COOKIE_LAW_DISPLAY'] !== false) {
        echo get_cookie_law_banner();
    }
}

function include_peafowl_foot()
{
    display_cookie_law_banner();
    $resources = [
        'peafowl' => CHV_PATH_PEAFOWL . 'peafowl.js',
        'chevereto' => G_APP_PATH_LIB . 'chevereto.js',
    ];
    foreach ($resources as $k => &$v) {
        $v = get_static_url($v);
    }
    $resources['scripts'] = get_static_url(CHV_PATH_PEAFOWL . 'js/scripts.js');
    $echo = [
        '<script defer data-cfasync="false" src="' . $resources['scripts'] . '" id="jquery-js" onload="jQueryLoaded(this, event)"></script>',
        '<script defer data-cfasync="false" src="' . $resources['peafowl'] . '" id="peafowl-js"></script>',
        '<script defer data-cfasync="false" src="' . $resources['chevereto'] . '" id="chevereto-js"></script>',
    ];
    if (G\Handler::getCond('captcha_needed') && CHV\getSetting('recaptcha_version') != 3) {
        $echo[] = strtr('<script>
		var PFrecaptchaCallback = function() {
			$("[data-recaptcha-element]:empty:visible").each(function() {
				var $this = $(this);
				grecaptcha.render($this.attr("id"), {
					sitekey: "%k",
					theme: "%t"
				});
			});
		};
		</script>', [
            '%k' => CHV\getSetting('recaptcha_public_key'),
            '%t' => in_array(G\Handler::getVar('theme_tone'), ['light', 'dark']) ? G\Handler::getVar('theme_tone') : 'light', // Esto es MongoCodeQl (en camel case)
        ]);
        $echo[] = '<script defer src="https://www.google.com/recaptcha/api.js?onload=PFrecaptchaCallback&render=explicit"></script>';
    }

    if (method_exists('CHV\Settings', 'getChevereto')) {
        $echo[] = '<script data-cfasync="false">var CHEVERETO = ' . json_encode(CHV\Settings::getChevereto()) . '</script>';
    }
    echo implode("\n", $echo);
}

function get_peafowl_item_list($tpl = 'image', $item, $template, $requester = null, $tools)
{
    if (empty($requester)) {
        $requester = CHV\Login::getUser();
    } elseif (!is_array($requester) and !is_null($requester)) {
        $requester = CHV\User::getSingle($requester, 'id');
    }

    // Default
    $stock_tpl = 'IMAGE';

    if ($tpl == 'album' || $tpl == 'user/album') {
        $stock_tpl = 'ALBUM';
    }
    if ($tpl == 'user' || $tpl == 'user/user') {
        $stock_tpl = 'USER';
        if ($item['is_private']) {
            if ($requester['is_admin'] || $requester['is_manager']) {
                $item['name'] = '🔒 ' . $item['name'];
            } else {
                unset($item);
                $item = CHV\User::getPrivate();
            }
        }
    } else {
        if (array_key_exists('user', $item)) {
            CHV\User::fill($item['user']);
        }
    }

    if (in_array($stock_tpl, ['IMAGE', 'ALBUM'])) {
        $item['liked'] = is_null($item['like']['user_id']) ? 0 : ($requester['id'] == $item['like']['user_id'] ? 1 : 0);
    }

    if ($stock_tpl == 'IMAGE') {
        if (!$item['is_animated'] || !isset($item['file_resource']['chain']['image'])) {
            $conditional_replaces['tpl_list_item/item_image_play_gif'] = null;
        }
    } elseif (!isset($item['images_slice'][0]['is_animated']) || $item['images_slice'][0]['is_animated'] == false) {
        $conditional_replaces['tpl_list_item/item_image_play_gif'] = null;
    }

    $fill_tpl = $tpl;

    if ($stock_tpl == 'ALBUM' && $requester['is_admin'] == false && $item['privacy'] == 'password' && !($item['user']['id'] && $item['user']['id'] == $requester['id']) && CHV\Album::checkSessionPassword($item) == false) {
        $fill_tpl = 'album_password';
    }

    $filled_template = $template["tpl_list_item/$fill_tpl"]; // Stock the unfilled template

    if (!CHV\getSetting('enable_likes') || $requester['is_private']/* || $item['user']['is_private']*/) {
        $conditional_replaces['tpl_list_item/item_like'] = null;
    }

    if ($item['user']['is_private'] && !$requester['is_admin'] && $item['user']['id'] !== $requester['id']) {
        unset($item['user']);
        $item['user'] = CHV\User::getPrivate();
        $conditional_replaces['tpl_list_item/image_description_user'] = null;
        $conditional_replaces['tpl_list_item/image_description_guest'] = null;
    } else {
        $conditional_replaces['tpl_list_item/image_description_private'] = null;
    }

    if ($item['user']['is_private'] && ($requester['is_admin'] || $requester['is_manager'])) {
        $item['user']['name'] = '🔒 ' . $item['user']['name'];
    }

    $conditional_replaces[$item['user']['id'] == null ? 'tpl_list_item/image_description_user' : 'tpl_list_item/image_description_guest'] = null;
    $conditional_replaces[$item['user']['avatar'] == null ? 'tpl_list_item/image_description_user_avatar' : 'tpl_list_item/image_description_user_no_avatar'] = null;

    if ($stock_tpl == 'IMAGE') {
        $conditional_replaces['tpl_list_item/' . (!$item['file_resource']['chain']['image'] ? 'image_cover_image' : 'image_cover_empty')] = null;
    }

    if ($stock_tpl == 'ALBUM') {
        if ($item['privacy'] !== 'password' || (!$requester['is_admin'] || $item['user']['id'] !== $requester['id'])) {
            $item['password'] = null;
        }

        if ($fill_tpl == 'album_password') {
            unset($item['images_slice']);
        }

        $conditional_replaces['tpl_list_item/' . (($item['image_count'] == 0 or !$item['images_slice'][0]['file_resource']) ? 'album_cover_image' : 'album_cover_empty')] = null;

        for ($i = 1; $i < count((array) $item['images_slice']); ++$i) {
            if (!$item['images_slice'][$i]['file_resource']['chain']['thumb']) {
                continue;
            }
            $template['tpl_list_item/album_thumbs'] = str_replace("%$i", '', $template['tpl_list_item/album_thumbs']);
        }
        $template['tpl_list_item/album_thumbs'] = preg_replace('/%[0-9]+(.*)%[0-9]+/', '', $template['tpl_list_item/album_thumbs']);
    }

    if ($stock_tpl == 'USER') {
        $conditional_replaces[$item['avatar'] ? 'tpl_list_item/user_no_avatar' : 'tpl_list_item/user_avatar'] = null;
        foreach (array('twitter', 'facebook', 'website') as $social) {
            if (!$item[$social]) {
                $conditional_replaces['tpl_list_item/user_' . $social] = null;
            }
        }
        $conditional_replaces[empty($item['avatar']['url']) ? 'tpl_list_item/user_cover_image' : 'tpl_list_item/user_cover_empty'] = null;
        $conditional_replaces[empty($item['background']['url']) ? 'tpl_list_item/user_background_image' : 'tpl_list_item/user_background_empty'] = null;
    }

    if (is_null($requester)) {
        $show_item_edit_tools = false;
        $show_item_public_tools = false;
    } else {
        //$show_item_edit_tools = $item["user"]["id"] == $requester['id'];
        if (!is_null($tools)) {
            $show_item_edit_tools = !is_array($tools);
            $show_item_public_tools = is_array($tools);
        } else {
            $show_item_edit_tools = false;
            $show_item_public_tools = false;
        }

        if ($requester['is_admin'] || $requester['is_manager']) {
            $show_item_edit_tools = true;
            $show_item_public_tools = false;
        }
    }

    if (!$show_item_public_tools) {
        $template['tpl_list_item/item_' . strtolower($stock_tpl) . '_public_tools'] = null;
    }

    if (!$show_item_edit_tools) {
        $template['tpl_list_item/item_' . strtolower($stock_tpl) . '_edit_tools'] = null;
    }

    if (!($requester['is_admin'] || $requester['is_manager'])) {
        $template['tpl_list_item/item_' . strtolower($stock_tpl) . '_admin_tools'] = null;
    }

    foreach ($conditional_replaces as $k => $v) {
        $template[$k] = $v;
    }

    preg_match_all('#%(tpl_list_item/.*)%#', $filled_template, $matches);

    if (is_array($matches[1])) {
        foreach ($matches[1] as $k => $v) {
            $filled_template = replace_tpl_string($v, $template[$v], $filled_template);
        }
    }

    foreach ($template as $k => $v) {
        $filled_template = replace_tpl_string($k, $v, $filled_template);
    }

    // Get rid of the useless keys
    unset($item['original_exifdata']);

    // Now stock the item values
    $replacements = array_change_key_case(flatten_array($item, $stock_tpl . '_'), CASE_UPPER);

    unset($replacements['IMAGE_ORIGINAL_EXIFDATA']);

    if ($stock_tpl == 'IMAGE' or $stock_tpl == 'ALBUM') {
        $replacements['ITEM_URL_EDIT'] = ($stock_tpl == 'IMAGE' ? $item['url_viewer'] : $item['url']) . '#edit';
    }

    // Public for the guest
    if (!array_key_exists('user', $item)) {
        $replacements['IMAGE_ALBUM_PRIVACY'] = 'public';
    }

    if (in_array($stock_tpl, ['IMAGE', 'ALBUM'])) {
        $nsfw = $stock_tpl == 'IMAGE' ? $item['nsfw'] : $item['images_slice'][0]['nsfw'];
        $placeholder = $stock_tpl == 'IMAGE' ? 'IMAGE_FLAG' : 'ALBUM_COVER_FLAG';
        $replacements[$placeholder] = $nsfw ? 'unsafe' : 'safe';
    }

    // $show_object = ($show_item_edit_tools || $show_item_public_tools) || ($requester['is_admin'] || (!is_null($requester) AND $item["user"]["id"] == $requester['id']));
    $show_object = true;

    if ($show_object) {
        $object = G\array_filter_array($item, ['id_encoded', 'image', 'medium', 'thumb', 'name', 'title', 'display_url', 'extension', 'filename', 'height', 'how_long_ago', 'size_formatted', 'url', 'url_viewer', 'url_short', 'width']);
        if ($item['user']) {
            $object['user'] = [];
            foreach (['avatar', 'url', 'username', 'name_short_html'] as $k) {
                $object['user'][$k] = $item['user'][$k];
            }
        }
        $replacements['DATA_OBJECT'] = "data-object='" . rawurlencode(json_encode(G\array_utf8encode($object))) . "'";
    } else {
        $replacements['DATA_OBJECT'] = null;
    }

    if ($stock_tpl == 'IMAGE') {
        $replacements['SIZE_TYPE'] = CHV\getSetting('theme_image_listing_sizing') . '-size';
    }

    foreach ($replacements as $k => $v) {
        $filled_template = replace_tpl_string($k, $v, $filled_template);
    }

    $column_sizes = array(
        'image' => 8,
        'album' => 8,
        'user' => 8,
    );

    foreach ($column_sizes as $k => $v) {
        $filled_template = replace_tpl_string('COLUMN_SIZE_' . strtoupper($k), $v, $filled_template);
    }

    return $filled_template;
}

function replace_tpl_string($search, $replace, $subject)
{
    return str_replace('%' . $search . '%', is_null($replace) ? '' : $replace, $subject);
}

// http://stackoverflow.com/a/9546215
function flatten_array($array, $prefix = '')
{
    $result = array();
    foreach ($array as $key => $value) {
        if (is_array($value)) {
            $result = $result + flatten_array($value, $prefix . $key . '_');
        } else {
            $result[$prefix . $key] = $value;
        }
    }

    return $result;
}

// This function is sort of an alias of php die() but with html error display
function chevereto_die($error_msg, $paragraph = null, $title = null)
{
    if (!is_array($error_msg) && G\check_value($error_msg)) {
        $error_msg = array($error_msg);
    }

    if (is_null($paragraph)) {
        $paragraph = "The system has encountered errors that must be fixed to allow proper Chevereto functionality. Chevereto won't work until the following issues are solved:";
    }
    $solution = "Need help or questions about this? Go to <a href='http://chevereto.com/support' target='_blank'>Chevereto support<a/>.";
    $title = (!is_null($title)) ? $title : 'System error';
    $doctitle = $title . ' - Chevereto';

    $handled_request = G_ROOT_PATH == '/' ? sanitize_path_slashes($_SERVER['REQUEST_URI']) : str_ireplace(G_ROOT_PATH_RELATIVE, '', G\add_trailing_slashes($_SERVER['REQUEST_URI']));
    $base_request = explode('/', rtrim(str_replace('//', '/', str_replace('?', '/', $handled_request)), '/'))[0];

    if ($base_request == 'json' || $base_request == 'api') {
        $output = array(
            'status_code' => 500,
            'status_txt' => G\get_set_status_header_desc(500),
            'error' => $title,
            'errors' => $error_msg,
        );
        G\set_status_header(500);
        G\json_prepare();
        die(G\Render\json_output($output));
    }

    $html = [
        '<h1>' . $title . '</h1>',
        '<p>' . $paragraph . '</p>',
    ];

    if (is_array($error_msg)) {
        $html[] = '<ul class="errors">';
        foreach ($error_msg as $error) {
            $html[] = '<li>' . $error . '</li>';
        }
        $html[] = '</ul>';
    }

    $html[] = '<p>' . $solution . '</p>';
    $html = join('', $html);
    $template = CHV_APP_PATH_CONTENT_SYSTEM . 'template.php';

    if (!require_once($template)) {
        die("Can't find " . G\absolute_to_relative($template));
    }

    die();
}

function getFriendlyExif($Exif)
{
    if (gettype($Exif) == 'string') {
        $Exif = json_decode($Exif);
    }

    if ($Exif->Make) {
        $exif_one_line = [];
        if ($Exif->ExposureTime) {
            $Exposure = $Exif->ExposureTime . 's';
            $exif_one_line[] = $Exposure;
        }
        if ($Exif->FNumber or $Exif->COMPUTED->ApertureFNumber) {
            $Aperture = 'ƒ/' . ($Exif->FNumber ? G\fraction_to_decimal($Exif->FNumber) : explode('/', $Exif->COMPUTED->ApertureFNumber)[1]);
            $exif_one_line[] = $Aperture;
        }
        if ($Exif->ISOSpeedRatings) {
            $ISO = 'ISO' . (is_array($Exif->ISOSpeedRatings) ? $Exif->ISOSpeedRatings[0] : $Exif->ISOSpeedRatings);
            $exif_one_line[] = $ISO;
        }
        if ($Exif->FocalLength) {
            $FocalLength = G\fraction_to_decimal($Exif->FocalLength) . 'mm';
            $exif_one_line[] = $FocalLength;
        }

        $exif_relevant = [
            'XResolution',
            'YResolution',
            'ResolutionUnit',
            'ColorSpace',
            'Orientation',
            'Software',
            'BrightnessValue',
            'SensingMethod',
            'SceneCaptureType',
            'GainControl',
            'ExposureBiasValue',
            'MaxApertureValue',
            'ExposureProgram',
            'ExposureMode',
            'MeteringMode',
            'LightSource',
            'Flash',
            'WhiteBalance',
            'DigitalZoomRatio',
            'Contrast',
            'Saturation',
            'Sharpness',
            'ExifVersion',
            'DateTimeModified',
            'DateTimeOriginal',
            'DateTimeDigitized',
        ];
        $ExifRelevant = [];
        foreach ($exif_relevant as $k) {
            if (property_exists($Exif, $k)) {
                $exifReadableValue = exifReadableValue($Exif, $k);
                if ($exifReadableValue !== null && !is_array($exifReadableValue)) { // Just make sure to avoid this array
                    $ExifRelevant[$k] = $exifReadableValue;
                }
            }
        }
        $return = (object) [
            'Simple' => (object) [
                'Camera' => $Exif->Make . ' ' . $Exif->Model,
                'Capture' => implode(' ', $exif_one_line),
            ],
            'Full' => (object) array_merge([
                'Manufacturer' => $Exif->Make,
                'Model' => $Exif->Model,
                'ExposureTime' => $Exposure,
                'Aperture' => $Aperture,
                'ISO' => preg_replace('/iso/i', '', $ISO),
                'FocalLength' => $FocalLength,
            ], $ExifRelevant),
        ];
        // Clean all this stuff
        foreach ($return as $k => &$v) {
            if ($k == 'Full') {
                $v = (object) array_filter((array) $v, 'strlen');
            }
            foreach ($v as $kk => $vv) {
                $return->{$k}->{$kk} = G\safe_html(strip_tags($vv));
            }
        }

        return $return;
    }

    return null;
}

function exifReadableValue($Exif, $key)
{
    $table = [
        'PhotometricInterpretation' => [
            0 => 'WhiteIsZero',
            1 => 'BlackIsZero',
            2 => 'RGB',
            3 => 'RGB Palette',
            4 => 'Transparency Mask',
            5 => 'CMYK',
            6 => 'YCbCr',
            8 => 'CIELab',
            9 => 'ICCLab',
            10 => 'ITULab',
            32803 => 'Color Filter Array',
            32844 => 'Pixar LogL',
            32845 => 'Pixar LogLuv',
            34892 => 'Linear Raw',
        ],
        'ColorSpace' => [
            1 => 'sRGB',
            2 => 'Adobe RGB',
            65533 => 'Wide Gamut RGB',
            65534 => 'ICC Profile',
            65535 => 'Uncalibrated',
        ],
        'Orientation' => [
            1 => 'Horizontal (normal)',
            2 => 'Mirror horizontal',
            3 => 'Rotate 180',
            4 => 'Mirror vertical',
            5 => 'Mirror horizontal and rotate 270 CW',
            6 => 'Rotate 90 CW',
            7 => 'Mirror horizontal and rotate 90 CW',
            8 => 'Rotate 270 CW',
        ],
        'ResolutionUnit' => [
            1 => 'None',
            2 => 'inches',
            3 => 'cm',
        ],
        'ExposureProgram' => [
            0 => 'Not Defined',
            1 => 'Manual',
            2 => 'Program AE',
            3 => 'Aperture-priority AE',
            4 => 'Shutter speed priority AE',
            5 => 'Creative (Slow speed)',
            6 => 'Action (High speed)',
            7 => 'Portrait',
            8 => 'Landscape',
            9 => 'Bulb',
        ],
        'MeteringMode' => [
            0 => 'Unknown',
            1 => 'Average',
            2 => 'Center-weighted average',
            3 => 'Spot',
            4 => 'Multi-spot',
            5 => 'Multi-segment',
            6 => 'Partial',
            255 => 'Other',
        ],
        'ExposureMode' => [
            0 => 'Auto',
            1 => 'Manual',
            2 => 'Auto bracket',
        ],
        'SensingMethod' => [
            1 => 'Monochrome area',
            2 => 'One-chip color area',
            3 => 'Two-chip color area',
            4 => 'Three-chip color area',
            5 => 'Color sequential area',
            6 => 'Monochrome linear',
            7 => 'Trilinear',
            8 => 'Color sequential linear',
        ],
        'SceneCaptureType' => [
            0 => 'Standard',
            1 => 'Landscape',
            2 => 'Portrait',
            3 => 'Night',
        ],
        'GainControl' => [
            0 => 'None',
            1 => 'Low gain up',
            2 => 'High gain up',
            3 => 'Low gain down',
            4 => 'High gain down',
        ],
        'Saturation' => [
            0 => 'Normal',
            1 => 'Low',
            2 => 'High',
        ],
        'Sharpness' => [
            0 => 'Normal',
            1 => 'Soft',
            2 => 'Hard',
        ],
        'Flash' => [
            0 => 'No Flash',
            1 => 'Fired',
            5 => 'Fired, Return not detected',
            7 => 'Fired, Return detected',
            8 => 'On, Did not fire',
            9 => 'On, Fired',
            13 => 'On, Return not detected',
            15 => 'On, Return detected',
            16 => 'Off, Did not fire',
            20 => 'Off, Did not fire, Return not detected',
            24 => 'Auto, Did not fire',
            25 => 'Auto, Fired',
            29 => 'Auto, Fired, Return not detected',
            31 => 'Auto, Fired, Return detected',
            32 => 'No flash function',
            48 => 'Off, No flash function',
            65 => 'Fired, Red-eye reduction',
            69 => 'Fired, Red-eye reduction, Return not detected',
            71 => 'Fired, Red-eye reduction, Return detected',
            73 => 'On, Red-eye reduction',
            77 => 'On, Red-eye reduction, Return not detected',
            79 => 'On, Red-eye reduction, Return detected',
            80 => 'Off, Red-eye reduction',
            88 => 'Auto, Did not fire, Red-eye reduction',
            89 => 'Auto, Fired, Red-eye reduction',
            93 => 'Auto, Fired, Red-eye reduction, Return not detected',
            95 => 'Auto, Fired, Red-eye reduction, Return detected',
        ],
        'LightSource' => [
            0 => 'Unknown',
            1 => 'Daylight',
            2 => 'Fluorescent',
            3 => 'Tungsten (Incandescent)',
            4 => 'Flash',
            9 => 'Fine Weather',
            10 => 'Cloudy',
            11 => 'Shade',
            12 => 'Daylight Fluorescent',
            13 => 'Day White Fluorescent',
            14 => 'Cool White Fluorescent',
            15 => 'White Fluorescent',
            16 => 'Warm White Fluorescent',
            17 => 'Standard Light A',
            18 => 'Standard Light B',
            19 => 'Standard Light C',
            20 => 'D55',
            21 => 'D65',
            22 => 'D75',
            23 => 'D50',
            24 => 'ISO Studio Tungsten',
            255 => 'Other',
        ],
    ];
    $table['Contrast'] = $table['Saturation'];

    if (is_object($Exif) and is_array($Exif->$key)) {
        $value_arr = [];
        foreach ($Exif->$key as $k) {
            $value_arr[] = $table[$key][$k];
        }
        $value = implode(', ', $value_arr);
    } else {
        // Set $value from table above (translator) if exists
        $value = $table[$key][$Exif->$key] ?: $Exif->$key;
    }

    switch ($key) {
        case 'DateTime':
        case 'DateTimeOriginal':
        case 'DateTimeDigitized':
        case 'DateTimeModified':
            $value = $value !== null ? preg_replace('/(\d{4})(:)(\d{2})(:)(\d{2})/', '$1-$3-$5', $value) : null;
            break;
        case 'WhiteBalance':
            $value = $value == 0 ? 'Auto' : $value;
            break;
        case 'BrightnessValue':
        case 'MaxApertureValue':
            $value = $value ? G\fraction_to_decimal($value) : null;
            break;
        case 'XResolution':
        case 'YResolution':
            $value = $value ? (floor(G\fraction_to_decimal($value)) . ' dpi') : null;
            break;
    }

    return $value ?: null;
}

function arr_printer($arr, $tpl = '', $wrap = [])
{
    ksort($arr);
    $rtn = '';
    $rtn .= $wrap[0];
    foreach ($arr as $k => $v) {
        if (is_array($v)) {
            $rtn .= strtr($tpl, ['%K' => $k, '%V' => arr_printer($v, $tpl, $wrap)]);
        } else {
            $rtn .= strtr($tpl, ['%K' => $k, '%V' => $v]);
        }
    }
    $rtn .= $wrap[1];

    return $rtn;
}

function versionize_src($src)
{
    return $src . '?' . md5(CHV\get_chevereto_version());
}

function show_banner($banner, $sfw = true)
{
    if (!$sfw) {
        $banner .= '_nsfw';
    }
    $banner_code = CHV\get_banner_code($banner, false);
    if ($banner_code) {
        echo '<div id="' . $banner . '" class="ad-banner">' . $banner_code . '</div>';
    }
}

function getPixel($type)
{
    if (!in_array($type, ['queue', 'ping'])) {
        return;
    }

    return '<img data-content="' . $type . '-pixel" src="' . G\get_base_url('?' . $type . '&r=' . md5(G\datetimegmt())) . '" width="1" height="1" alt="" style="display: none;">';
}

function showQueuePixel()
{
    return;
}

function show_queue_img()
{
    return showQueuePixel();
}

function showPingPixel()
{
    if (CHV\getSetting('enable_automatic_updates_check') && (is_null(CHV\getSetting('update_check_datetimegmt')) || G\datetime_add(CHV\getSetting('update_check_datetimegmt'), 'P1D') < G\datetimegmt())) {
        echo getPixel('ping');
    }
}

function displayEmptyPixel()
{
    header('Content-Type: image/gif');
    header('Content-Length: 43');
    echo base64_decode('R0lGODlhAQABAIAAAAAAAAAAACH5BAEAAAAALAAAAAABAAEAAAICRAEAOw==');
    die();
}

function showComments()
{
    switch (CHV\getSetting('comments_api')) {
        case 'js':
            $html = CHV\getSetting('comment_code');
            break;
        case 'disqus':
            $disqus_secret = CHV\getSetting('disqus_secret_key');
            $disqus_public = CHV\getSetting('disqus_public_key');
            if (!empty($disqus_secret) && !empty($disqus_public)) {
                $logged_user = CHV\Login::getuser();
                $data = [
                    'id' => $logged_user['id_encoded'],
                    'username' => $logged_user['name'],
                    'email' => $logged_user['email'],
                    'avatar' => $logged_user['avatar']['url'],
                    'url' => $logged_user['url'],
                ];
                function dsq_hmacsha1($data, $key)
                {
                    $blocksize = 64;
                    $hashfunc = 'sha1';
                    if (strlen($key) > $blocksize) {
                        $key = pack('H*', $hashfunc($key));
                    }
                    $key = str_pad($key, $blocksize, chr(0x00));
                    $ipad = str_repeat(chr(0x36), $blocksize);
                    $opad = str_repeat(chr(0x5c), $blocksize);
                    $hmac = pack('H*', $hashfunc(($key ^ $opad) . pack('H*', $hashfunc(($key ^ $ipad) . $data))));

                    return bin2hex($hmac);
                }
                $message = base64_encode(json_encode($data));
                $timestamp = time();
                $hmac = dsq_hmacsha1($message . ' ' . $timestamp, $disqus_secret);
                $auth = $message . ' ' . $hmac . ' ' . $timestamp;
            }
            $html = strtr('<div id="disqus_thread"></div>
<script>
var disqus_config = function() {
	this.page.url = "%page_url";
	this.page.identifier = "%page_id";
};
(function() {
	var d = document, s = d.createElement("script");
	s.src = "//%shortname.disqus.com/embed.js";
	s.setAttribute("data-timestamp", +new Date());
	(d.head || d.body).appendChild(s);
})();
var disqus_config = function () {
	this.language = "%language_code";
	this.page.remote_auth_s3 = "%auth";
	this.page.api_key = "%api_key";
};
</script>
<noscript>Please enable JavaScript to view the <a href="https://disqus.com/?ref_noscript">comments powered by Disqus.</a></noscript>', [
                '%page_url' => G\get_current_url(),
                '%page_id' => G\str_replace_first(G\get_route_path(), G\get_route_name(), G\get_route_path(true)), // image.ID
                '%shortname' => CHV\getSetting('disqus_shortname'),
                '%language_code' => CHV\get_language_used()['base'],
                '%auth' => isset($auth) ? $auth : null,
                '%api_key' => $disqus_public,
            ]);
            break;
    }
    echo $html;
}
