<?php
/* Copyright (C) 2025 Eduard Wisch <data@data-it-solution.de>
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 3 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program. If not, see <https://www.gnu.org/licenses/>.
 */

/**
 * \file    core/substitutions/functions_epcqr.lib.php
 * \ingroup epcqr
 * \brief   Substitutionsfunktionen für QR-Codes und Bilder in Dokumenten
 */

/**
 * Füllt Substitutionsarray mit Bildpfaden und QR-Code-Daten
 *
 * @param  array        &$substitutionarray  Array mit Substitutionswerten
 * @param  Translate    $langs               Sprachobjekt
 * @param  CommonObject $object              Dolibarr-Objekt (Rechnung, Angebot, etc.)
 * @param  string       $outputlangs         Ausgabesprache
 * @return void
 */
function epcqr_completesubstitutionarray(&$substitutionarray, $langs, $object, $outputlangs = null)
{
	global $conf, $db;

	dol_syslog("EPCQR: START completesubstitutionarray für ".get_class($object), LOG_DEBUG);

	// Prüfen ob Objekt gültig ist
	if (!is_object($object) || empty($object->id)) {
		dol_syslog("EPCQR: Object ist null oder hat keine ID - überspringe", LOG_DEBUG);
		return;
	}

	// QR-Code Pfad aus Extra-Feldern
	$qrCodePath = '';

	// Prüfen ob Extra-Felder vorhanden sind
	if (isset($object->array_options['options_qrcodepath'])) {
		$qrCodePath = $object->array_options['options_qrcodepath'];
		dol_syslog("EPCQR: QR-Code Pfad gefunden: ".$qrCodePath, LOG_DEBUG);
	}

	// Substitution für QR-Code-Bildpfad
	$substitutionarray['qrcode_path'] = $qrCodePath;

	// Für ODT-Dokumente: Spezielle Marker für Bildoperationen
	// Diese werden später von der ODT-Erweiterung verarbeitet
	$substitutionarray['__IMAGE_qrcode__'] = $qrCodePath;

	// Allgemeine Bildfunktion: Jedes Extrafeld das auf "_imagepath" endet
	// wird als Bildpfad interpretiert
	if (!empty($object->array_options)) {
		foreach ($object->array_options as $key => $value) {
			if (preg_match('/^options_(.+)_imagepath$/', $key, $matches)) {
				$fieldname = $matches[1];
				$substitutionarray['__IMAGE_'.$fieldname.'__'] = $value;
				dol_syslog("EPCQR: Bildpfad gefunden für ".$fieldname.": ".$value, LOG_DEBUG);
			}
		}
	}

	dol_syslog("EPCQR: ENDE completesubstitutionarray", LOG_DEBUG);
}

/**
 * Fügt Bilder in ODT-Dokumente ein
 *
 * Diese Funktion wird nach der ODT-Generierung aufgerufen und
 * ersetzt Bild-Marker durch tatsächliche Bilder im ODT
 *
 * @param  string  $odfFilePath  Pfad zur generierten ODT-Datei
 * @param  array   $imageData    Array mit Bildinformationen
 * @return bool                  true bei Erfolg, false bei Fehler
 */
function epcqr_insertImagesIntoODT($odfFilePath, $imageData)
{
	global $conf;

	dol_syslog("EPCQR: START insertImagesIntoODT für ".$odfFilePath, LOG_DEBUG);

	if (!file_exists($odfFilePath)) {
		dol_syslog("EPCQR: ODT-Datei existiert nicht: ".$odfFilePath, LOG_ERR);
		return false;
	}

	// Temporäres Verzeichnis erstellen
	$tmpDir = sys_get_temp_dir() . '/odt_epcqr_' . uniqid();
	if (!mkdir($tmpDir)) {
		dol_syslog("EPCQR: Konnte temporäres Verzeichnis nicht erstellen", LOG_ERR);
		return false;
	}

	try {
		// 1. ODT entpacken (ODT ist ZIP)
		$zip = new ZipArchive();
		if ($zip->open($odfFilePath) !== true) {
			dol_syslog("EPCQR: Konnte ODT nicht öffnen: ".$odfFilePath, LOG_ERR);
			epcqr_deleteDir($tmpDir);
			return false;
		}
		$zip->extractTo($tmpDir);
		$zip->close();

		// 2. Pictures-Verzeichnis erstellen falls nicht vorhanden
		if (!is_dir($tmpDir.'/Pictures')) {
			mkdir($tmpDir.'/Pictures');
		}

		// 3. Bilder kopieren und XML vorbereiten
		$manifest = file_get_contents($tmpDir.'/META-INF/manifest.xml');
		$content = file_get_contents($tmpDir.'/content.xml');
		$imageReplacements = array();

		foreach ($imageData as $marker => $imagePath) {
			if (empty($imagePath) || !file_exists($imagePath)) {
				dol_syslog("EPCQR: Bild existiert nicht: ".$imagePath." für Marker ".$marker, LOG_WARNING);
				continue;
			}

			// Bildinfo ermitteln
			$imageInfo = @getimagesize($imagePath);
			if ($imageInfo === false) {
				dol_syslog("EPCQR: Konnte Bildinfo nicht lesen: ".$imagePath, LOG_WARNING);
				continue;
			}

			$extension = strtolower(pathinfo($imagePath, PATHINFO_EXTENSION));
			$mimeType = $imageInfo['mime'];
			$imageWidth = $imageInfo[0];
			$imageHeight = $imageInfo[1];

			// Eindeutigen Bildnamen generieren
			$imageFilename = 'img_'.md5($marker).'.'.$extension;
			$imageDest = $tmpDir.'/Pictures/'.$imageFilename;

			// Bild kopieren
			if (!copy($imagePath, $imageDest)) {
				dol_syslog("EPCQR: Konnte Bild nicht kopieren: ".$imagePath, LOG_ERR);
				continue;
			}

			// Manifest.xml aktualisieren
			if (strpos($manifest, 'Pictures/'.$imageFilename) === false) {
				$newEntry = '<manifest:file-entry manifest:media-type="'.$mimeType.'" manifest:full-path="Pictures/'.$imageFilename.'"/>';
				$manifest = str_replace('</manifest:manifest>', $newEntry."\n</manifest:manifest>", $manifest);
			}

			// Bildgröße in cm berechnen (Annahme: 96 DPI)
			$widthCm = round(($imageWidth / 96) * 2.54, 2);
			$heightCm = round(($imageHeight / 96) * 2.54, 2);

			// Maximale Größe begrenzen
			$maxWidth = 6; // cm
			$maxHeight = 6; // cm

			if ($widthCm > $maxWidth || $heightCm > $maxHeight) {
				$ratio = min($maxWidth / $widthCm, $maxHeight / $heightCm);
				$widthCm = round($widthCm * $ratio, 2);
				$heightCm = round($heightCm * $ratio, 2);
			}

			// ODF draw:frame XML-Element erstellen
			$imageXml = '<draw:frame draw:name="'.htmlspecialchars($marker).'" '
				.'text:anchor-type="as-char" '
				.'svg:width="'.$widthCm.'cm" '
				.'svg:height="'.$heightCm.'cm">'
				.'<draw:image xlink:href="Pictures/'.$imageFilename.'" '
				.'xlink:type="simple" '
				.'xlink:show="embed" '
				.'xlink:actuate="onLoad"/>'
				.'</draw:frame>';

			$imageReplacements['{'.$marker.'}'] = $imageXml;

			dol_syslog("EPCQR: Bild vorbereitet: ".$marker." -> ".$imageFilename, LOG_DEBUG);
		}

		// 4. Marker in content.xml ersetzen
		foreach ($imageReplacements as $marker => $xml) {
			$content = str_replace($marker, $xml, $content);
		}

		// 5. Dateien zurückschreiben
		file_put_contents($tmpDir.'/META-INF/manifest.xml', $manifest);
		file_put_contents($tmpDir.'/content.xml', $content);

		// 6. Neue ODT erstellen
		$zip = new ZipArchive();
		if ($zip->open($odfFilePath, ZipArchive::CREATE | ZipArchive::OVERWRITE) !== true) {
			dol_syslog("EPCQR: Konnte neue ODT nicht erstellen", LOG_ERR);
			epcqr_deleteDir($tmpDir);
			return false;
		}

		// mimetype MUSS zuerst und unkomprimiert
		$zip->addFile($tmpDir.'/mimetype', 'mimetype');
		$zip->setCompressionName('mimetype', ZipArchive::CM_STORE);

		// Restliche Dateien hinzufügen
		$files = new RecursiveIteratorIterator(
			new RecursiveDirectoryIterator($tmpDir, RecursiveDirectoryIterator::SKIP_DOTS),
			RecursiveIteratorIterator::LEAVES_ONLY
		);

		foreach ($files as $file) {
			if (!$file->isFile()) {
				continue;
			}
			$filePath = $file->getRealPath();
			$relativePath = substr($filePath, strlen($tmpDir) + 1);

			// mimetype überspringen (bereits hinzugefügt)
			if ($relativePath === 'mimetype') {
				continue;
			}

			// Backslashes zu Forward-Slashes
			$relativePath = str_replace('\\', '/', $relativePath);

			$zip->addFile($filePath, $relativePath);
		}

		$zip->close();

		// 7. Cleanup
		epcqr_deleteDir($tmpDir);

		dol_syslog("EPCQR: ENDE insertImagesIntoODT - Erfolgreich", LOG_DEBUG);
		return true;
	} catch (Exception $e) {
		dol_syslog("EPCQR: Exception in insertImagesIntoODT: ".$e->getMessage(), LOG_ERR);
		if (is_dir($tmpDir)) {
			epcqr_deleteDir($tmpDir);
		}
		return false;
	}
}

/**
 * Hilfsfunktion: Löscht Verzeichnis rekursiv
 *
 * @param  string  $dir  Verzeichnispfad
 * @return void
 */
function epcqr_deleteDir($dir)
{
	if (!is_dir($dir)) {
		return;
	}
	$items = scandir($dir);
	foreach ($items as $item) {
		if ($item === '.' || $item === '..') {
			continue;
		}
		$path = $dir.'/'.$item;
		if (is_dir($path)) {
			epcqr_deleteDir($path);
		} else {
			unlink($path);
		}
	}
	rmdir($dir);
}
