<?php 
/*
 * Copyright (c) 2025, Tribal Limited
 * All rights reserved.
 * 
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are met:
 *     * Redistributions of source code must retain the above copyright
 *       notice, this list of conditions and the following disclaimer.
 *     * Redistributions in binary form must reproduce the above copyright
 *       notice, this list of conditions and the following disclaimer in the
 *       documentation and/or other materials provided with the distribution.
 *     * Neither the name of Zenario, Tribal Limited nor the
 *       names of its contributors may be used to endorse or promote products
 *       derived from this software without specific prior written permission.
 * 
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
 * DISCLAIMED. IN NO EVENT SHALL TRIBAL LTD BE LIABLE FOR ANY
 * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 */


namespace ze;

use PhpOffice\PhpSpreadsheet\Spreadsheet;

class phraseAdm {

	public static function seconds($seconds) {
		if (!class_exists('DateTime')) {
			$a = [
				['second', '[[n]] seconds', (int) $seconds]];
		} else {
			$zero = new \DateTime('@0');
			$dt = $zero->diff(new \DateTime('@'. (int) $seconds));
			$a = [
				['second', '[[n]] seconds', (int) $dt->format('%s')],
				['minute', '[[n]] minutes', (int) $dt->format('%i')],
				['hour', '[[n]] hours', (int) $dt->format('%h')], 
				['day', '[[n]] days', (int) $dt->format('%a')]];
		}
	
		$t = '';
		$s = '';
		foreach ($a as $v) {
			if ($v = \ze\admin::nzPhrase('', $v[0], $v[1], $v[2], ['n' => $v[2]])) {
				$t = $v. $s. $t;
			
				if ($s === '') {
					$s = \ze\admin::phrase(' and ');
				} else {
					$s = \ze\admin::phrase(', ');
				}
			}
		}
	
		return $t;
	}

	//check if a Visitor Phrase is protected
	public static function isProtected($languageId, $moduleClass, $phraseCode, $keepExistingTranslations) {

		$sql = "
			SELECT";
	
		//Are we adding a new VLP, or updating an existing one?
		if (!$keepExistingTranslations) {
			//If we are editing an existing one, do not overwrite a protected phrase
			$sql .= "
				protect_flag";
	
		} else {
			//If we are adding a new VLP, don't allow anything that already exists (and has a value) to be overwritten
			$sql .= "
				local_text IS NOT NULL AND local_text != ''";
		}
	
		$sql .= "
			FROM " . DB_PREFIX . "visitor_phrases
			WHERE language_id = '". \ze\escape::asciiInSQL($languageId). "'
			  AND module_class_name = '". \ze\escape::asciiInSQL($moduleClass). "'
			  AND code = '". \ze\escape::sql($phraseCode). "'";
	
	
		//Return true for protected, false for exists but not protected, and 0 for when the phrase does not exist
		if ($row = \ze\sql::fetchRow(\ze\sql::select($sql))) {
			return (bool) $row[0];
		} else {
			return 0;
		}
	}
	
	public static function isArchived($languageId, $moduleClass, $phraseCode) {

		$sql = "
			SELECT archived
			FROM " . DB_PREFIX . "visitor_phrases
			WHERE language_id = '". \ze\escape::asciiInSQL($languageId). "'
			  AND module_class_name = '". \ze\escape::asciiInSQL($moduleClass). "'
			  AND code = '". \ze\escape::sql($phraseCode). "'";
	
	
		//Return true for archived, false for exists bug not archived, and 0 for when the phrase does not exist
		if ($row = \ze\sql::fetchRow(\ze\sql::select($sql))) {
			return (bool) $row[0];
		} else {
			return 0;
		}
	}

	//Update a Visitor Phrase from the importer
	public static function importVisitorPhrase($languageId, $moduleClass, $phraseCode, $localText, $keepExistingTranslations, $languageIsEnabled, $addPhrasesThatDontExist, &$numberOf) {
	
		//Don't attempt to add empty phrases
		if (!$phraseCode || $localText === null || $localText === false || $localText === '') {
			return;
		}
		
		//In 10.0, the export logic exports the class name as, for example:
		//zenario_common_features (Common Features)
		//so the import logic needs to discard everything after the class name.
		//Please note: this was dropped again as of 10.2, but I have kept this code
		//for compatibility reasons. It does not change anything.
		$moduleClassArray = explode(' ', $moduleClass);
		$moduleClass = $moduleClassArray[0];
		if ($moduleClass == 'Core Features') {
			$moduleClass = '';
		}
		
		//Also check if the module class name provided
		//is an existing module, and is running.
		if (!$moduleClass || !\ze\module::isRunning($moduleClass)) {
			return;
		}
	
		//Check if the language is enabled on site. If not enabled, do not import the phrase.
		if ($languageIsEnabled) {
			//Check if the phrase already exists in the DB in English.
			//If it does not, do not import it.
			$sql = "
				SELECT id
				FROM " . DB_PREFIX . "visitor_phrases
				WHERE language_id = '". \ze\escape::asciiInSQL(\ze::$defaultLang). "'
				  AND module_class_name = '". \ze\escape::asciiInSQL($moduleClass). "'
				  AND code = '". \ze\escape::sql($phraseCode). "'";
		
		
			//Return true for protected, false for exists bug not protected, and 0 for when the phrase does not exist
			if ($addPhrasesThatDontExist || ($row = \ze\sql::fetchRow(\ze\sql::select($sql)))) {
				//Check if the phrase is protected
				if ($protected = \ze\phraseAdm::isProtected($languageId, $moduleClass, $phraseCode, $keepExistingTranslations)) {
					++$numberOf['protected'];
				
				} else {
					//Update or insert the phrase
					\ze\row::set(
						'visitor_phrases',
						[
							'local_text' => trim($localText)
						],
						[
							'language_id' => $languageId,
							'module_class_name' => $moduleClass,
							'code' => $phraseCode
						]
					);
					
					if ($archived = \ze\phraseAdm::isArchived($languageId, $moduleClass, $phraseCode, $keepExistingTranslations)) {
						if ($archived) {
							++$numberOf['restored_from_archive'];
						}
					}
					
					//If a phrase was archived before but has now been imported, remove the archived flag
					\ze\row::set(
						'visitor_phrases',
						[
							'archived' => 0
						],
						[
							'module_class_name' => $moduleClass,
							'code' => $phraseCode
						]
					);
				
					//\ze\phraseAdm::isProtected() returns false for phrases that are unprotected, and 0 for phrases that do not exist
					if ($protected === 0) {
						++$numberOf['added'];
				
					} else {
						++$numberOf['updated'];
					}
				}
			} else {
				++$numberOf['skipped'];
			}
		} else {
			++$numberOf['skipped'];
			$numberOf['language_not_enabled'] = true;
		}
	}

	//Given an uploaded XML file, pharse that file looking for visitor language phrases
	public static function importVisitorLanguagePack($file, &$languageIdFound, $keepExistingTranslations, $scanning = false, $forceLanguageIdOverride = false, $realFilename = false, $checkPerms = false, $addPhrasesThatDontExist = false) {
		return require \ze::funIncPath(__FILE__, __FUNCTION__);
	}

	public static function scanModulePhraseDir($moduleName, $scanMode) {
		$importFiles = [];
		if ($path = \ze::moduleDir($moduleName, 'phrases/', true)) {
			foreach (scandir($path) as $file) {
				if (is_file($path. $file) && substr($file, 0, 1) != '.') {
				
					$languageIdFound = false;
					$numberOf = \ze\phraseAdm::importVisitorLanguagePack($path. $file, $languageIdFound, $keepExistingTranslations = true, $scanMode);
				
					if (!$numberOf['upload_error']) {
						if ($scanMode === 'number and file') {
							$numberOf['file'] = $file;
							$importFiles[$languageIdFound] = $numberOf;
					
						} elseif ($scanMode === 'full scan') {
							$importFiles[$languageIdFound] = $numberOf['added'];
					
						} else {
							$importFiles[$languageIdFound] = $file;
						}
					}
				}
			}
		}
	
		return $importFiles;
	}
	
	//If an admin has just manually updated a phrase, note down the time that the phrases were
	//last updated to use as a cache-killer.
	//You only need to call this function if an admin adds/changes the text of a phrase. There should
	//be no harm in keeping a few slightly-old cached copies of things that were deleted.
	public static function flagAsUpdated() {
		$time = time();
		$time36 = base_convert($time, 10, 36);
		\ze\site::setSetting('phrases_version', $time36);
	}
	
	
	// Functionality for a "phase debug mode" prototype.
	// Currently not in use/implemented.
	#public static function debugPlainText($phrase) {
	#	$phrase = strtr($phrase,
	#		'ABCDEFGHIJKLMNOPQRSTUVWXYZ abcdefghijklmnopqrstuvwxyz',
	#		'TTTTTTTTTTTTTTTTTTTTTTTTTT tttttttttttttttttttttttttt'
	#	);
	#	
	#	return $phrase;
	#}
	#
	#public static function debugTextWithMergeFields($phrase) {
	#	
	#	$iSplit = preg_split('@(\[\[.*?\]\])@s', $phrase, -1,  PREG_SPLIT_DELIM_CAPTURE);
	#	
	#	$phrase = '';
	#	$iLast = count($iSplit) - 1;
	#	for ($i = 0; $i <= $iLast; $i += 2) {
	#		$phrase .= \ze\phraseAdm::debugPlainText($iSplit[$i]);
	#		
	#		if ($i != $iLast) {
	#			$phrase .= $iSplit[$i + 1];
	#		}
	#	}
	#	
	#	return $phrase;
	#}
	#
	#public static function debugText($phrase, $isHTML) {
	#	
	#	if (!$isHTML) {
	#		return \ze\phraseAdm::debugTextWithMergeFields($phrase);
	#	}
	#	
	#	
	#	$iSplit = preg_split('@(\<[^\>]*\>)@s', $phrase, -1,  PREG_SPLIT_DELIM_CAPTURE);
	#	
	#	$phrase = '<x-zenario-translated-html>';
	#	$iLast = count($iSplit) - 1;
	#	for ($i = 0; $i <= $iLast; $i += 2) {
	#		
	#		
	#		$jSplit = preg_split('@(\&[^\;]*\;)@s', $iSplit[$i], -1,  PREG_SPLIT_DELIM_CAPTURE);
	#		//var_dump($jSplit);
	#		
	#		$jLast = count($jSplit) - 1;
	#		for ($j = 0; $j <= $jLast; $j += 2) {
	#	
	#			$phrase .= \ze\phraseAdm::debugTextWithMergeFields($jSplit[$j]);
	#			
	#			if ($j != $jLast) {
	#				$phrase .= $jSplit[$j + 1];
	#			}
	#		}
	#		
	#		if ($i != $iLast) {
	#			$phrase .= $iSplit[$i + 1];
	#		}
	#	}
	#	$phrase .= '</x-zenario-translated-html>';
	#	
	#	return $phrase;
	#}
	
}