<?php
function cmt_favicon_option_value($title, $default = NULL) {
	global $conn;
	$sql = "SELECT value FROM settings_options WHERE title = :title LIMIT 1";
	$result = $conn->prepare($sql);
	$result->bindValue(':title', $title, PDO::PARAM_STR);
	$result->execute();
	if ($result->rowCount()){
		$arr = $result->fetch();
		return $arr['value'];
	}
	return $default;
}

function cmt_favicon_option_save($title, $value, $c_public = '0') {
	global $conn;
	$sql_tmp = "SELECT id FROM settings_options WHERE title = :title LIMIT 1";
	$result_tmp = $conn->prepare($sql_tmp);
	$result_tmp->bindValue(':title', $title, PDO::PARAM_STR);
	$result_tmp->execute();
	if ($result_tmp->rowCount()){
		$sql = "UPDATE settings_options SET value = :value, c_public = :c_public WHERE title = :title";
	} else {
		$sql = "INSERT INTO settings_options SET value = :value, c_public = :c_public, title = :title";
	}
	$result = $conn->prepare($sql);
	$result->bindValue(':value', $value, PDO::PARAM_STR);
	$result->bindValue(':c_public', $c_public, PDO::PARAM_STR);
	$result->bindValue(':title', $title, PDO::PARAM_STR);
	$result->execute();
}

function cmt_favicon_hex($value, $default) {
	$value = trim((string) $value);
	if (!preg_match('/^#[a-f0-9]{6}$/i', $value)) return $default;
	return strtolower($value);
}

function cmt_favicon_color($image, $hex, $alpha = 0) {
	$hex = ltrim($hex, '#');
	$r = hexdec(substr($hex, 0, 2));
	$g = hexdec(substr($hex, 2, 2));
	$b = hexdec(substr($hex, 4, 2));
	return imagecolorallocatealpha($image, $r, $g, $b, $alpha);
}

function cmt_favicon_image_normalize($image) {
	if (!$image) return false;
	$width = imagesx($image);
	$height = imagesy($image);
	if (!$width || !$height) return false;
	
	$canvas = imagecreatetruecolor($width, $height);
	imagealphablending($canvas, false);
	imagesavealpha($canvas, true);
	$transparent = imagecolorallocatealpha($canvas, 0, 0, 0, 127);
	imagefilledrectangle($canvas, 0, 0, $width, $height, $transparent);
	imagecopy($canvas, $image, 0, 0, 0, 0, $width, $height);
	imagedestroy($image);
	
	return $canvas;
}

function cmt_favicon_image_from_file($file) {
	if (!extension_loaded('gd')) return false;
	
	$extension = strtolower(pathinfo($file, PATHINFO_EXTENSION));
	$info = @getimagesize($file);
	if ($info && isset($info['mime'])){
		$image = false;
		switch ($info['mime']){
			case 'image/png':
				$image = @imagecreatefrompng($file);
				break;
			case 'image/jpeg':
				$image = @imagecreatefromjpeg($file);
				break;
			case 'image/gif':
				$image = @imagecreatefromgif($file);
				break;
			case 'image/webp':
				$image = function_exists('imagecreatefromwebp') ? @imagecreatefromwebp($file) : false;
				break;
			case 'image/bmp':
				$image = function_exists('imagecreatefrombmp') ? @imagecreatefrombmp($file) : false;
				break;
		}
		if ($image) return cmt_favicon_image_normalize($image);
	}
	
	if (($extension == 'svg' || ($info && isset($info['mime']) && $info['mime'] == 'image/svg+xml')) && extension_loaded('imagick')){
		try {
			$imagick = new Imagick();
			$imagick->setBackgroundColor(new ImagickPixel('transparent'));
			$imagick->setResolution(512, 512);
			$imagick->readImage($file);
			$imagick->setImageFormat('png32');
			$image = @imagecreatefromstring($imagick->getImagesBlob());
			$imagick->clear();
			$imagick->destroy();
			if ($image) return cmt_favicon_image_normalize($image);
		} catch (Exception $e) {
			return false;
		}
	}
	
	return false;
}

function cmt_favicon_font() {
	static $font = false;
	if ($font !== false) return $font ?: NULL;
	
	$font = NULL;
	$root = isset($_SERVER['DOCUMENT_ROOT']) ? rtrim($_SERVER['DOCUMENT_ROOT'], '/\\') : '';
	$base = realpath(__DIR__.'/../../../..');
	$fonts = array();
	
	if ($root){
		$fonts[] = $root.'/comator/includes/fonts/favicon.ttf';
		$fonts[] = $root.'/includes/fonts/favicon.ttf';
		$fonts[] = $root.'/assets/fonts/favicon.ttf';
		$fonts[] = $root.'/fonts/favicon.ttf';
	}
	if ($base){
		$fonts[] = $base.'/comator/includes/fonts/favicon.ttf';
		$fonts[] = $base.'/includes/fonts/favicon.ttf';
		$fonts[] = $base.'/assets/fonts/favicon.ttf';
		$fonts[] = $base.'/fonts/favicon.ttf';
	}
	
	$fonts = array_unique($fonts);
	foreach ($fonts AS $file){
		if (@is_file($file) && @is_readable($file)){
			$font = $file;
			return $font;
		}
	}
	
	return NULL;
}

function cmt_favicon_initials($mode, $name, $initials) {
	if ($mode == 'initials' && trim($initials)){
		$value = mb_strtoupper(trim($initials), 'UTF-8');
		$value = preg_replace('/[^A-Z0-9ÄÖÜ]/u', '', $value);
		return mb_substr($value, 0, 3, 'UTF-8');
	}
	
	if ($mode == 'name' && trim($name)){
		$words = preg_split('/[\s\-_\.]+/u', trim($name));
		$value = NULL;
		foreach ($words AS $word){
			$word = trim($word);
			if ($word) $value .= mb_substr($word, 0, 1, 'UTF-8');
			if (mb_strlen($value, 'UTF-8') >= 3) break;
		}
		if ($value) return mb_strtoupper($value, 'UTF-8');
	}
	
	$host = isset($_SERVER['SERVER_NAME']) ? $_SERVER['SERVER_NAME'] : '';
	$host = preg_replace('/^www\./i', '', $host);
	$parts = preg_split('/[\.-]+/', $host);
	if (isset($parts[0]) && $parts[0]){
		$slug = preg_replace('/[^a-z0-9]/i', '', $parts[0]);
		if (strlen($slug) >= 2) return strtoupper(substr($slug, 0, 2));
		if ($slug) return strtoupper($slug);
	}
	return 'CM';
}

function cmt_favicon_canvas($size, $background = NULL) {
	$image = imagecreatetruecolor($size, $size);
	imagesavealpha($image, true);
	
	if ($background === NULL){
		imagealphablending($image, false);
		$transparent = imagecolorallocatealpha($image, 0, 0, 0, 127);
		imagefilledrectangle($image, 0, 0, $size, $size, $transparent);
	} else {
		imagealphablending($image, true);
		$color = cmt_favicon_color($image, $background);
		imagefilledrectangle($image, 0, 0, $size, $size, $color);
	}
	
	return $image;
}

function cmt_favicon_source_bounds($source, $alpha_threshold = 120) {
	$width = imagesx($source);
	$height = imagesy($source);
	if (!$width || !$height) return false;
	
	$min_x = $width;
	$min_y = $height;
	$max_x = -1;
	$max_y = -1;
	
	for ($y = 0; $y < $height; $y++){
		for ($x = 0; $x < $width; $x++){
			$rgba = imagecolorat($source, $x, $y);
			$alpha = ($rgba & 0x7F000000) >> 24;
			if ($alpha < $alpha_threshold){
				if ($x < $min_x) $min_x = $x;
				if ($y < $min_y) $min_y = $y;
				if ($x > $max_x) $max_x = $x;
				if ($y > $max_y) $max_y = $y;
			}
		}
	}
	
	if ($max_x < $min_x || $max_y < $min_y) return false;
	return array(
		'x' => $min_x,
		'y' => $min_y,
		'width' => ($max_x - $min_x + 1),
		'height' => ($max_y - $min_y + 1)
	);
}

function cmt_favicon_from_source($source, $size, $background = NULL) {
	$image = cmt_favicon_canvas($size, NULL);
	$src_width = imagesx($source);
	$src_height = imagesy($source);
	if (!$src_width || !$src_height) return $image;
	
	$bounds = cmt_favicon_source_bounds($source);
	if (!$bounds) return $image;
	
	$ratio = min($size / $bounds['width'], $size / $bounds['height']);
	$width = max(1, (int) round($bounds['width'] * $ratio));
	$height = max(1, (int) round($bounds['height'] * $ratio));
	$x = (int) round(($size - $width) / 2);
	$y = (int) round(($size - $height) / 2);
	
	imagealphablending($image, false);
	imagesavealpha($image, true);
	imagecopyresampled($image, $source, $x, $y, $bounds['x'], $bounds['y'], $width, $height, $bounds['width'], $bounds['height']);
	
	return $image;
}

function cmt_favicon_from_initials($initials, $size, $background, $foreground) {
	$image = cmt_favicon_canvas($size, $background);
	imagealphablending($image, true);
	$color = cmt_favicon_color($image, $foreground);
	$font = cmt_favicon_font();
	
	if ($font && function_exists('imagettftext')){
		$length = max(1, mb_strlen($initials, 'UTF-8'));
		$font_size = (int) round($size / ($length == 1 ? 1.55 : ($length == 2 ? 2.05 : 2.65)));
		$max_width = $size * 0.74;
		$max_height = $size * 0.58;
		while ($font_size > 8){
			$box = imagettfbbox($font_size, 0, $font, $initials);
			$min_x = min($box[0], $box[2], $box[4], $box[6]);
			$max_x = max($box[0], $box[2], $box[4], $box[6]);
			$min_y = min($box[1], $box[3], $box[5], $box[7]);
			$max_y = max($box[1], $box[3], $box[5], $box[7]);
			$text_width = $max_x - $min_x;
			$text_height = $max_y - $min_y;
			if ($text_width <= $max_width && $text_height <= $max_height) break;
			$font_size--;
		}
		$box = imagettfbbox($font_size, 0, $font, $initials);
		$min_x = min($box[0], $box[2], $box[4], $box[6]);
		$max_x = max($box[0], $box[2], $box[4], $box[6]);
		$min_y = min($box[1], $box[3], $box[5], $box[7]);
		$max_y = max($box[1], $box[3], $box[5], $box[7]);
		$x = (int) round(($size / 2) - (($min_x + $max_x) / 2));
		$y = (int) round(($size / 2) - (($min_y + $max_y) / 2));
		imagettftext($image, $font_size, 0, $x, $y, $color, $font, $initials);
		return $image;
	}
	
	$font_id = 5;
	$text = iconv('UTF-8', 'ASCII//TRANSLIT//IGNORE', $initials);
	if (!$text) $text = preg_replace('/[^A-Z0-9]/i', '', $initials);
	$text = strtoupper(substr($text ?: 'CM', 0, 3));
	$text_width = imagefontwidth($font_id) * strlen($text);
	$text_height = imagefontheight($font_id);
	$tmp = imagecreatetruecolor($text_width, $text_height);
	imagealphablending($tmp, false);
	imagesavealpha($tmp, true);
	$transparent = imagecolorallocatealpha($tmp, 0, 0, 0, 127);
	imagefilledrectangle($tmp, 0, 0, $text_width, $text_height, $transparent);
	$tmp_color = cmt_favicon_color($tmp, $foreground);
	imagestring($tmp, $font_id, 0, 0, $text, $tmp_color);
	
	$max_width = $size * 0.70;
	$max_height = $size * 0.52;
	$ratio = min($max_width / max(1, $text_width), $max_height / max(1, $text_height));
	$target_width = max(1, (int) round($text_width * $ratio));
	$target_height = max(1, (int) round($text_height * $ratio));
	$x = (int) round(($size - $target_width) / 2);
	$y = (int) round(($size - $target_height) / 2);
	imagecopyresampled($image, $tmp, $x, $y, 0, 0, $target_width, $target_height, $text_width, $text_height);
	imagedestroy($tmp);
	return $image;
}

function cmt_favicon_save_png($image, $file) {
	imagepng($image, $file, 6);
	chmod($file, 0644);
}

function cmt_favicon_make_ico($png_files, $dest) {
	$items = array();
	foreach ($png_files AS $size => $file){
		if (is_file($file)) $items[] = array('size' => (int) $size, 'data' => file_get_contents($file));
	}
	if (!count($items)) return false;
	
	$count = count($items);
	$offset = 6 + ($count * 16);
	$header = pack('vvv', 0, 1, $count);
	$entries = '';
	$data = '';
	foreach ($items AS $item){
		$size = $item['size'];
		$bytes = strlen($item['data']);
		$entries .= pack('CCCCvvVV', ($size >= 256 ? 0 : $size), ($size >= 256 ? 0 : $size), 0, 0, 1, 32, $bytes, $offset);
		$data .= $item['data'];
		$offset += $bytes;
	}
	file_put_contents($dest, $header.$entries.$data);
	chmod($dest, 0644);
	return true;
}

function cmt_favicon_write_svg($file, $initials) {
	$initials = htmlspecialchars($initials, ENT_QUOTES, 'UTF-8');
	$font_size = (mb_strlen($initials, 'UTF-8') == 1) ? 300 : ((mb_strlen($initials, 'UTF-8') == 2) ? 240 : 190);
	$svg = "<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 512 512'><rect width='512' height='512' rx='96' fill='#000'/><text x='256' y='278' text-anchor='middle' dominant-baseline='middle' font-family='Arial, Helvetica, sans-serif' font-size='".$font_size."' font-weight='700' fill='#fff'>".$initials."</text></svg>";
	file_put_contents($file, $svg);
	chmod($file, 0644);
}

function cmt_favicon_write_meta($dir, $name, $initials, $background, $theme_color) {
	$manifest = array(
		'name' => $name,
		'short_name' => mb_substr($initials ?: $name, 0, 12, 'UTF-8'),
		'icons' => array(
			array('src' => '/images/favicon/android-chrome-192x192.png', 'sizes' => '192x192', 'type' => 'image/png'),
			array('src' => '/images/favicon/android-chrome-512x512.png', 'sizes' => '512x512', 'type' => 'image/png')
		),
		'theme_color' => $theme_color,
		'background_color' => $background,
		'display' => 'standalone'
	);
	file_put_contents($dir.'site.webmanifest', json_encode($manifest, JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES | JSON_PRETTY_PRINT));
	chmod($dir.'site.webmanifest', 0644);
	
	$xml = "<?xml version='1.0' encoding='utf-8'?>\n<browserconfig><msapplication><tile><square150x150logo src='/images/favicon/mstile-150x150.png'/><TileColor>".htmlspecialchars($background, ENT_QUOTES, 'UTF-8')."</TileColor></tile></msapplication></browserconfig>";
	file_put_contents($dir.'browserconfig.xml', $xml);
	chmod($dir.'browserconfig.xml', 0644);
}

function cmt_favicon_generate($settings, $upload_file = NULL) {
	$background = cmt_favicon_hex($settings['favicon_background'], '#111827');
	$foreground = cmt_favicon_hex($settings['favicon_foreground'], '#ffffff');
	$theme_color = cmt_favicon_hex($settings['theme_color'], $background);
	$name = trim($settings['favicon_name']) ?: (isset($GLOBALS['title_page']) ? strip_tags($GLOBALS['title_page']) : $_SERVER['SERVER_NAME']);
	$initials = cmt_favicon_initials($settings['favicon_mode'], $name, $settings['favicon_initials']);
	$source = $upload_file ? cmt_favicon_image_from_file($upload_file) : false;
	
	$dirs = array('../images/favicon/');
	$sizes = array(
		'favicon-16x16.png' => 16,
		'favicon-32x32.png' => 32,
		'favicon-48x48.png' => 48,
		'apple-touch-icon.png' => 180,
		'android-chrome-192x192.png' => 192,
		'android-chrome-256x256.png' => 256,
		'android-chrome-512x512.png' => 512,
		'mstile-150x150.png' => 150
	);
	
	foreach ($dirs AS $dir){
		if (!is_dir($dir)) mkdir($dir, 0755, true);
		$ico_files = array();
		foreach ($sizes AS $file => $size){
			if ($source) $image = cmt_favicon_from_source($source, $size, $background);
			else $image = cmt_favicon_from_initials($initials, $size, $background, $foreground);
			cmt_favicon_save_png($image, $dir.$file);
			if (in_array($size, array(16, 32, 48))) $ico_files[$size] = $dir.$file;
			imagedestroy($image);
		}
		cmt_favicon_make_ico($ico_files, $dir.'favicon.ico');
		cmt_favicon_write_svg($dir.'safari-pinned-tab.svg', $initials);
		cmt_favicon_write_meta($dir, $name, $initials, $background, $theme_color);
	}
	if ($source) imagedestroy($source);
	
	cmt_favicon_option_save('favicon_last_generated', time(), '0');
	return true;
}

$favicon_keys = array(
	'favicon_mode' => 'domain',
	'favicon_name' => '',
	'favicon_initials' => '',
	'favicon_background' => '#111827',
	'favicon_foreground' => '#ffffff',
	'theme_color' => '#111827'
);

$settings = array();
$changed = false;
foreach ($favicon_keys AS $key => $default){
	$old_value = cmt_favicon_option_value($key, $default);
	$value = isset($_POST[$key]) ? trim(stripslashes(str_replace('<br />', '', $_POST[$key]))) : $old_value;
	if ($key == 'favicon_mode' && !in_array($value, array('domain', 'name', 'initials'))) $value = 'domain';
	if (in_array($key, array('favicon_background', 'favicon_foreground', 'theme_color'))) $value = cmt_favicon_hex($value, $default);
	if ($key == 'favicon_initials') $value = mb_substr($value, 0, 3, 'UTF-8');
	if ((string) $old_value !== (string) $value) $changed = true;
	cmt_favicon_option_save($key, $value, '0');
	$settings[$key] = $value;
}

$upload_file = NULL;
if (isset($_FILES['favicon_file']['tmp_name']) && file_exists($_FILES['favicon_file']['tmp_name']) && is_uploaded_file($_FILES['favicon_file']['tmp_name'])){
	$upload_file = $_FILES['favicon_file']['tmp_name'];
	$file_type = isset($_FILES['favicon_file']['type']) ? $_FILES['favicon_file']['type'] : '';
	$file_name = isset($_FILES['favicon_file']['name']) ? $_FILES['favicon_file']['name'] : '';
	$extension_tmp = explode('.', $file_name);
	$extension = strtolower(end($extension_tmp));
	$extension_new = mime2ext($file_type, $extension);
	if (!in_array($extension_new, array('png', 'jpg', 'jpeg', 'gif', 'webp', 'bmp', 'svg'))) $upload_file = NULL;
}

if ($upload_file || $changed || (isset($_POST['favicon_generate']) && $_POST['favicon_generate'])){
	cmt_favicon_generate($settings, $upload_file);
}