<?php
//作成者：https://note.com/tekito_note
//もし不具合があった場合、ご連絡ください。

require __DIR__ . '/../tegalog-auth.php';

// 設定は uploader.ini から読み込む（本体スクリプトを手動更新してもiniは上書きされない）
$ini = tgauth_loadIni(__DIR__ . '/uploader.ini');

// 実行環境(Apache/CGI/PHP-FPM等)によってカレントディレクトリがこのファイルの場所と
// 一致するとは限らないため、相対パスは常にこのファイルの場所を基準に解決する。
function exuploader_resolvePath(string $path): string {
    if (preg_match('#^([A-Za-z]:[\\\\/]|/)#', $path)) {
        return $path; // 既に絶対パス
    }
    return __DIR__ . '/' . $path;
}

$uploadDir           = exuploader_resolvePath($ini['uploadDir']        ?? '../images/');
$miniDir             = exuploader_resolvePath($ini['miniDir']          ?? '../images/mini/');
$thumbnailWidth      = (int) ($ini['thumbnailWidth']  ?? 300);
$fontFile            = exuploader_resolvePath($ini['fontFile']         ?? 'Mplus1-Regular.ttf');
$resizeOption        = $ini['resizeOption']       ?? '1';
$maxDimension        = (int) ($ini['maxDimension']    ?? 1000);
$defaultWatermark    = exuploader_resolvePath($ini['defaultWatermark'] ?? 'watermark.png');
$watermarkScale      = (float) ($ini['watermarkScale'] ?? 0.2);
$fileNamingMode      = (int) ($ini['fileNamingMode']  ?? 3);
$maxFileCount        = (int) ($ini['maxFileCount']    ?? 20);
$maxFileSizeMB       = (int) ($ini['maxFileSizeMB']   ?? 20);
$imageConvertOption  = $ini['imageConvertOption'] ?? '1';
$jpegQuality         = (int) ($ini['jpegQuality']     ?? 85);
$maxImagePixels      = (int) ($ini['maxImagePixels']  ?? 40000000);

$allowedExtensions  = ['jpg', 'jpeg', 'png', 'gif', 'webp', 'avif'];
$allowedWatermarks  = (array) $defaultWatermark;
$fileNames = [];

// 実行環境に必須拡張(GD)が無ければ、分かりにくいFatal Errorになる前に明確なエラーを返す
if (!extension_loaded('gd')) {
    http_response_code(500);
    header('Content-Type: application/json');
    echo json_encode(['error' => 'サーバーにGD拡張が導入されていないため、画像アップロード機能を利用できません。']);
    exit;
}

// 実行環境のGDがWebPに対応していなければ、WebP変換の指定を強制的に無効化し、許可拡張子からもWebPを除外する
if (!function_exists('imagewebp') || !function_exists('imagecreatefromwebp')) {
    if ($imageConvertOption === '1') {
        $imageConvertOption = '0';
    }
    $allowedExtensions = array_values(array_diff($allowedExtensions, ['webp']));
}

// 実行環境のGDがAVIFに対応していなければ、AVIF変換の指定を強制的に無効化し、許可拡張子からもAVIFを除外する
if (!function_exists('imagecreatefromavif') || !function_exists('imageavif')) {
    if ($imageConvertOption === '2') {
        $imageConvertOption = '0';
    }
    $allowedExtensions = array_values(array_diff($allowedExtensions, ['avif']));
}

// ディレクトリがない場合作成
foreach ([$uploadDir, $miniDir] as $dir) {
    if (!file_exists($dir) && !mkdir($dir, 0755, true)) {
        throw new Exception("{$dir} の作成に失敗しました");
    }
}

// ── アクセス制御 ──────────────────────────────────────────────
// てがろぐ本体のセッションファイル(psif.cgi)を直接参照してログイン状態を判定する
// （tegalog-auth.php 側の実装。自己参照HTTPリクエストを使わないため、
//   Referer偽装やTLS証明書検証などの懸念が構造的に発生しない）
if (!tgauth_isLoggedIn()) {
    http_response_code(401);
    header('Content-Type: application/json');
    echo json_encode(['error' => 'ログインが必要です']);
    exit;
}

// 設定値を返すエンドポイント
if (isset($_GET['getConfig'])) {
    header('Content-Type: application/json');
    echo json_encode([
        'imageConvertOption'  => $imageConvertOption,
        'resizeOption'        => $resizeOption,
        'maxDimension'        => $maxDimension,
        'fileNamingMode'      => $fileNamingMode,
        'maxFileCount'        => $maxFileCount,
        'maxFileSizeMB'       => $maxFileSizeMB,
        'thumbnailWidth'      => $thumbnailWidth,
    ]);
    exit;
}

if ($_SERVER['REQUEST_METHOD'] === 'POST') {
    header('Content-Type: application/json');
    $renames = [];
    try {
        if (isset($_FILES['files']) && count($_FILES['files']['name']) > 0) {
            if (count($_FILES['files']['name']) > $maxFileCount) {
                http_response_code(400);
                echo json_encode(['error' => "一度にアップロードできるファイル数は{$maxFileCount}枚までです"]);
                exit;
            }

            $memoryLimit = getMemoryLimitBytes();

            foreach ($_FILES['files']['tmp_name'] as $index => $fileTmpPath) {
                try {
                    if (memory_get_usage() > $memoryLimit * 0.8) {
                        throw new Exception("メモリ不足のため処理を中断しました");
                    }

                    if ($_FILES['files']['error'][$index] !== UPLOAD_ERR_OK) continue;

                    if ($_FILES['files']['size'][$index] > $maxFileSizeMB * 1024 * 1024) {
                        throw new Exception("ファイルサイズが上限を超えています");
                    }

                    $fileExtension = strtolower(pathinfo($_FILES['files']['name'][$index], PATHINFO_EXTENSION));
                    if (!in_array($fileExtension, $allowedExtensions)) continue;

                    $convertTargetExt = getConvertTargetExt($imageConvertOption);
                    $savedExt = ($convertTargetExt !== null && $fileExtension !== $convertTargetExt) ? $convertTargetExt : $fileExtension;

                    // クライアントが選択時に決めたファイル名をベース名として使用する
                    // （本文タグと保存ファイル名を一致させるため。安全な文字のみ許可）
                    $clientFileName = $_POST['fileNames'][$index] ?? $_FILES['files']['name'][$index];
                    $basePart = pathinfo(normalizeToUtf8($clientFileName), PATHINFO_FILENAME);
                    $baseName = str_replace([' ', '.'], '_', preg_replace('/[^\p{L}\p{N}\s\-\_]/u', '', $basePart));
                    if ($baseName === '') {
                        $baseName = substr(bin2hex(random_bytes(6)), 0, 12);
                    }
                    // ファイルシステムのファイル名バイト数制限（多くは255バイト前後）に収まるよう、
                    // 拡張子・重複回避用の連番の余地を残しつつマルチバイト文字の境界を壊さずに切り詰める
                    // （日本語等はUTF-8で1文字最大4バイトのため、文字数ではなくバイト数で制限する）
                    $maxBaseNameBytes = 200;
                    if (strlen($baseName) > $maxBaseNameBytes) {
                        $baseName = function_exists('mb_strcut')
                            ? mb_strcut($baseName, 0, $maxBaseNameBytes, 'UTF-8')
                            : substr($baseName, 0, $maxBaseNameBytes);
                    }

                    // move_uploaded_file は元の拡張子で保存（画像形式変換は加工後に行う）
                    $tempFileName = $baseName . '.' . $fileExtension;
                    $dest_path    = $uploadDir . $tempFileName;
                    for ($counter = 1; file_exists($dest_path); $counter++) {
                        $tempFileName = $baseName . '_' . $counter . '.' . $fileExtension;
                        $dest_path    = $uploadDir . $tempFileName;
                    }
                    // 画像形式変換後の最終ファイル名
                    $newFileName = pathinfo($tempFileName, PATHINFO_FILENAME) . '.' . $savedExt;

                    if (!move_uploaded_file($fileTmpPath, $dest_path)) continue;

                    // 実際に画像として読めるか検証
                    $dest_imageSize = @getimagesize($dest_path);
                    if ($dest_imageSize === false) {
                        unlink($dest_path);
                        throw new Exception("無効な画像ファイルです");
                    }
                    if (!isPixelCountAllowed($dest_imageSize, $maxImagePixels)) {
                        unlink($dest_path);
                        throw new Exception("画像の解像度が大きすぎます");
                    }

                    // このファイルに対して行う処理を先に判定しておく
                    $wantsThumbnail  = ($_POST['createThumbnail'] ?? '') === 'yes';
                    $applyToThumb    = ($_POST['applyThumbnail'] ?? '0') === '1';
                    $needsProcessing = (($_POST['insertText'] ?? '') === 'yes') || (($_POST['applyWatermark'] ?? '') === 'yes');

                    // サムネイルが「未加工の原本」を基準にする可能性がある場合のみ、原本をコピーしておく
                    // （加工後の$dest_pathをそのままサムネイルにも使う場合はコピー不要）
                    $originalImagePath = null;
                    if ($wantsThumbnail && !($needsProcessing && $applyToThumb)) {
                        $originalImagePath = $uploadDir . 'temp_original_' . $index . '.' . $fileExtension;
                        copy($dest_path, $originalImagePath);
                    }

                    removeICCProfile($dest_path);
                    if (in_array($fileExtension, ['jpg', 'jpeg'])) fixOrientation($dest_path);
                    if ($resizeOption === '1') resizeImage($dest_path, $maxDimension);

                    // 加工処理（アップロード画像＝常に未トリミングの状態に対して行う）
                    if ($needsProcessing) {
                        applyPostProcessing($dest_path, $defaultWatermark, $allowedWatermarks);
                    }

                    // サムネイル作成（画像形式変換前に元形式で処理する）
                    if ($wantsThumbnail) {
                        $thumbnailPath = null;

                        // 「サムネイルのみトリミング」：トリミング済み画像が別途送られてきているかどうか
                        $thumbSrcFileOk = isset($_FILES['thumbSourceFiles']['tmp_name'][$index])
                            && $_FILES['thumbSourceFiles']['error'][$index] === UPLOAD_ERR_OK
                            && $_FILES['thumbSourceFiles']['size'][$index] <= $maxFileSizeMB * 1024 * 1024;

                        if ($thumbSrcFileOk) {
                            // トリミング済み画像を基準に、テキスト・透かしをこちらに対して計算し直す
                            // （アップロード画像側＝$dest_pathは未トリミングのまま維持する）
                            $thumbSourcePath = $uploadDir . 'temp_thumbsrc_' . $index . '.' . $fileExtension;
                            $moved = move_uploaded_file($_FILES['thumbSourceFiles']['tmp_name'][$index], $thumbSourcePath);
                            $thumbSrcImageSize = $moved ? @getimagesize($thumbSourcePath) : false;
                            if ($thumbSrcImageSize !== false && isPixelCountAllowed($thumbSrcImageSize, $maxImagePixels)) {
                                if ($needsProcessing && $applyToThumb) {
                                    applyPostProcessing($thumbSourcePath, $defaultWatermark, $allowedWatermarks);
                                }
                                $thumbnailPath = createThumbnail($thumbSourcePath, $miniDir, $fileExtension);
                                unlink($thumbSourcePath);
                            } else {
                                if (file_exists($thumbSourcePath)) unlink($thumbSourcePath);
                            }
                            if ($thumbnailPath === null) {
                                $thumbSrcFileOk = false; // 失敗時は通常経路にフォールバックする
                            }
                        }

                        if (!$thumbSrcFileOk) {
                            $thumbSource = ($needsProcessing && $applyToThumb) ? $dest_path : $originalImagePath;
                            if ($thumbSource !== null) {
                                $thumbnailPath = createThumbnail($thumbSource, $miniDir, $fileExtension);
                            }
                        }

                        if ($thumbnailPath !== null) {
                            // 画像形式変換は拡張子が元形式のままの状態で行う（rename前に実施）
                            if ($convertTargetExt !== null && $fileExtension !== $convertTargetExt) {
                                $thumbnailPath = convertImageFormat($thumbnailPath, $miniDir, $convertTargetExt);
                            }
                            $thumbnailNewPath = $miniDir . pathinfo($newFileName, PATHINFO_FILENAME) . '.' . $savedExt;
                            if ($thumbnailPath !== $thumbnailNewPath) {
                                rename($thumbnailPath, $thumbnailNewPath);
                            }
                        } else {
                            error_log("サムネイル生成に失敗したためスキップしました（index={$index}）");
                        }
                    }

                    // 画像形式変換（すべての加工後）
                    if ($convertTargetExt !== null && $fileExtension !== $convertTargetExt) {
                        $dest_path = convertImageFormat($dest_path, $uploadDir, $convertTargetExt);
                    }

                    if ($originalImagePath !== null) unlink($originalImagePath);

                    $actualName   = basename($dest_path);
                    $expectedName = $_POST['displayNames'][$index] ?? $actualName;
                    if ($expectedName !== $actualName) {
                        $renames[$expectedName] = $actualName;
                    }
                    $fileNames[] = $actualName;

                } catch (Exception $e) {
                    error_log("Error processing file: " . $e->getMessage());
                    http_response_code(500);
                    echo json_encode(['error' => '画像の処理中にエラーが発生しました']);
                    exit;
                }
            }
            echo json_encode(['files' => $fileNames, 'renames' => $renames]);
        } else {
            echo json_encode(['files' => [], 'renames' => []]);
        }
    } catch (Exception $e) {
        error_log("Error in main process: " . $e->getMessage());
        http_response_code(500);
        echo json_encode(['error' => 'サーバーエラーが発生しました']);
    }
}

// ── ヘルパー関数 ──────────────────────────────────────────────

// 指定パスの画像に対して、テキスト挿入・透かし合成を$_POSTの指定に従って適用する
// （メイン画像／サムネイル用トリミング画像の両方から共通で呼ばれる）
function applyPostProcessing(string $path, string $defaultWatermark, array $allowedWatermarks): void {
    if (($_POST['insertText'] ?? '') === 'yes') {
        addTextAreaToImage($path, $_POST['customText'] ?? '');
    }
    if (($_POST['applyWatermark'] ?? '') === 'yes') {
        $watermarkFile = resolveWatermarkFile($_POST['watermarkFile'] ?? null, $defaultWatermark, $allowedWatermarks);
        applyWatermark($path, $watermarkFile, $_POST['watermarkPosition'] ?? 'center');
    }
}

// getimagesize()の結果（[0]=幅, [1]=高さ）が、許容ピクセル数の上限内かどうかを判定する
// （画像デコード時のメモリ消費を悪用したDoS＝いわゆる「ピクセル数爆弾」対策）
function isPixelCountAllowed(array $imageSize, int $maxImagePixels): bool {
    $width  = $imageSize[0] ?? 0;
    $height = $imageSize[1] ?? 0;
    return ($width * $height) <= $maxImagePixels;
}

// リクエストで指定された透かしファイル名を、許可リストと突き合わせて確定する
function resolveWatermarkFile(?string $requested, string $defaultWatermark, array $allowedWatermarks): string {
    $watermarkFile = basename($requested ?? $defaultWatermark);
    // basename()した比較用に、許可リスト側も同様にbasenameしておく
    // （defaultWatermark自体が "../watermark.png" のような相対パスのため）
    $allowedBasenames = array_map('basename', $allowedWatermarks);
    if (!in_array($watermarkFile, $allowedBasenames, true)) {
        return $defaultWatermark;
    }
    // 一致したファイル名に対応する、元の（ディレクトリ込みの）許可パスを返す
    $matchIndex = array_search($watermarkFile, $allowedBasenames, true);
    return $allowedWatermarks[$matchIndex];
}

// 文字列をUTF-8に正規化する（mbstring拡張が無い環境では何もせずそのまま返す）
function normalizeToUtf8(string $str): string {
    if (!function_exists('mb_convert_encoding')) return $str;

    $encoding = function_exists('mb_detect_encoding')
        ? mb_detect_encoding($str, ['UTF-8', 'SJIS-win', 'EUC-JP', 'ASCII'], true)
        : false;

    return mb_convert_encoding($str, 'UTF-8', $encoding ?: 'UTF-8');
}

// memory_limit設定値をバイト数に変換（-1=無制限はPHP_INT_MAXを返す）
function getMemoryLimitBytes(): int {
    $val = ini_get('memory_limit');
    if ($val === '-1') return PHP_INT_MAX;
    $unit = strtolower(substr($val, -1));
    $num  = (int) $val;
    switch ($unit) {
        case 'g': return $num * 1024 * 1024 * 1024;
        case 'm': return $num * 1024 * 1024;
        case 'k': return $num * 1024;
        default:  return $num;
    }
}

function imageCreateFromExt(string $filePath, string $ext) {
    // 不正なICCプロファイル等によるlibpngの警告がレスポンスに混入し
    // JSONを破壊することがあるため、@で抑制する
    switch ($ext) {
        case 'jpg':
        case 'jpeg': return @imagecreatefromjpeg($filePath);
        case 'png':  return @imagecreatefrompng($filePath);
        case 'gif':  return @imagecreatefromgif($filePath);
        case 'webp': return @imagecreatefromwebp($filePath);
        case 'avif': return @imagecreatefromavif($filePath);
        default:     return false;
    }
}

function imageSaveByExt($image, string $filePath, string $ext): void {
    global $jpegQuality;
    switch ($ext) {
        case 'jpg':
        case 'jpeg': imagejpeg($image, $filePath, $jpegQuality); break;
        case 'png':  imagepng($image, $filePath);  break;
        case 'gif':  imagegif($image, $filePath);  break;
        case 'webp': imagewebp($image, $filePath); break;
        case 'avif': imageavif($image, $filePath); break;
    }
}

// JPEG の Orientation を物理回転に変換
function fixOrientation(string $filePath): void {
    if (!function_exists('exif_read_data')) return;
    $exif = @exif_read_data($filePath);
    $orientation = $exif['Orientation'] ?? 1;
    if ($orientation === 1) return;

    $image = @imagecreatefromjpeg($filePath);
    if (!$image) return;

    // flip系は先にインプレース処理してから $image を回転対象にする
    if (in_array($orientation, [2, 4, 5, 7])) {
        imageflip($image, in_array($orientation, [2, 5, 7]) ? IMG_FLIP_HORIZONTAL : IMG_FLIP_VERTICAL);
    }
    switch ($orientation) {
        case 3:
        case 4:  $degrees = 180; break;
        case 5:
        case 6:  $degrees = -90; break;
        case 7:
        case 8:  $degrees = 90;  break;
        default: $degrees = 0;
    }
    $result = $degrees !== 0 ? imagerotate($image, $degrees, 0) : $image;
    imagejpeg($result, $filePath);
    if ($result !== $image) imagedestroy($result);
    imagedestroy($image);
}

// ICCプロファイル除去
function removeICCProfile(string $filePath): void {
    $ext   = strtolower(pathinfo($filePath, PATHINFO_EXTENSION));
    $image = @imageCreateFromExt($filePath, $ext);
    if ($image === false) return;
    imagealphablending($image, false);
    imagesavealpha($image, true);
    imageSaveByExt($image, $filePath, $ext);
    imagedestroy($image);
}

// リサイズ
function resizeImage(string $filePath, int $maxDimension): void {
    [$width, $height] = getimagesize($filePath);
    if ($width <= $maxDimension && $height <= $maxDimension) return;

    if ($width > $height) {
        $newWidth  = $maxDimension;
        $newHeight = (int) ($maxDimension * $height / $width);
    } else {
        $newHeight = $maxDimension;
        $newWidth  = (int) ($maxDimension * $width / $height);
    }

    $ext    = strtolower(pathinfo($filePath, PATHINFO_EXTENSION));
    $source = imageCreateFromExt($filePath, $ext);
    if (!$source) return;

    $resized = imagecreatetruecolor($newWidth, $newHeight);
    imagealphablending($resized, false);
    imagesavealpha($resized, true);
    imagecopyresampled($resized, $source, 0, 0, 0, 0, $newWidth, $newHeight, $width, $height);
    imageSaveByExt($resized, $filePath, $ext);
    imagedestroy($source);
    imagedestroy($resized);
}

// サムネイル作成（読み込みに失敗した場合はnullを返す）
function createThumbnail(string $filePath, string $miniDir, string $fileExtension): ?string {
    global $thumbnailWidth;

    $source = imageCreateFromExt($filePath, $fileExtension);
    if (!$source) {
        error_log("サムネイル用画像を読み込めませんでした: {$filePath}");
        return null;
    }
    $srcW   = imagesx($source);
    $srcH   = imagesy($source);
    $thumbH = (int) ($srcH * $thumbnailWidth / $srcW);

    $thumb = imagecreatetruecolor($thumbnailWidth, $thumbH);
    imagealphablending($thumb, false);
    imagesavealpha($thumb, true);
    imagecopyresampled($thumb, $source, 0, 0, 0, 0, $thumbnailWidth, $thumbH, $srcW, $srcH);

    $thumbnailPath = $miniDir . pathinfo($filePath, PATHINFO_FILENAME) . '.' . pathinfo($filePath, PATHINFO_EXTENSION);
    imageSaveByExt($thumb, $thumbnailPath, $fileExtension);
    imagedestroy($source);
    imagedestroy($thumb);

    return $thumbnailPath;
}

// 画像を指定形式（webp/avif）に変換して保存（元ファイルは削除）。変換後のパスを返す
function convertImageFormat(string $filePath, string $dir, string $targetExt): string {
    $ext        = strtolower(pathinfo($filePath, PATHINFO_EXTENSION));
    $targetPath = $dir . pathinfo($filePath, PATHINFO_FILENAME) . '.' . $targetExt;
    $image      = imageCreateFromExt($filePath, $ext);
    if ($image) {
        // パレット画像（透過PNG/GIF等）のまま変換すると警告が出力され
        // レスポンスのJSONが壊れるため、事前にトゥルーカラーへ変換する
        if (!imageistruecolor($image)) {
            imagepalettetotruecolor($image);
        }
        imageSaveByExt($image, $targetPath, $targetExt);
        imagedestroy($image);
        unlink($filePath);
    }
    return $targetPath;
}

// 画像変換オプション（0/1/2）から、変換先の拡張子を返す（0なら変換なしなのでnull）
function getConvertTargetExt(string $imageConvertOption): ?string {
    if ($imageConvertOption === '1') return 'webp';
    if ($imageConvertOption === '2') return 'avif';
    return null;
}

// テキスト挿入
function addTextAreaToImage(string $filePath, string $text): void {
    global $fontFile;

    $text  = preg_replace('/[[:cntrl:]]/', '', $text);
    $ext   = strtolower(pathinfo($filePath, PATHINFO_EXTENSION));
    $image = imageCreateFromExt($filePath, $ext);
    if (!$image) return;

    $w         = imagesx($image);
    $h         = imagesy($image);
    $fontSize  = $h * 0.02;
    $textColor = imagecolorallocatealpha($image, 0, 0, 0, 0);
    $outline   = imagecolorallocatealpha($image, 255, 255, 255, 0);
    $text      = normalizeToUtf8($text);
    $bbox      = imagettfbbox($fontSize, 0, $fontFile, $text);
    $x         = $w - ($bbox[2] - $bbox[0]) - 20;
    $y         = $h - 20;

    foreach ([[-1,-1],[1,-1],[-1,1],[1,1]] as [$ox, $oy]) {
        imagettftext($image, $fontSize, 0, $x + $ox, $y + $oy, $outline, $fontFile, $text);
    }
    imagettftext($image, $fontSize, 0, $x, $y, $textColor, $fontFile, $text);
    imageSaveByExt($image, $filePath, $ext);
    imagedestroy($image);
}

// 透かし合成
function applyWatermark(string $filePath, string $watermarkPath, string $position): void {
    global $watermarkScale;

    if (!$watermarkPath || !file_exists($watermarkPath)) {
        error_log("警告: 透かし画像 '{$watermarkPath}' が見つかりません");
        return;
    }
    $watermark = @imagecreatefrompng($watermarkPath);
    if (!$watermark) {
        error_log("警告: 透かし画像 '{$watermarkPath}' を読み込めません");
        return;
    }

    $ext   = strtolower(pathinfo($filePath, PATHINFO_EXTENSION));
    $image = imageCreateFromExt($filePath, $ext);
    if (!$image) { imagedestroy($watermark); return; }

    // 既にデコード済みの画像から直接サイズを取る（getimagesizeでのファイル再読込を避ける）
    $imgW = imagesx($image);
    $imgH = imagesy($image);
    $wmW  = imagesx($watermark);
    $wmH  = imagesy($watermark);
    $newWmW = (int) floor($imgW * $watermarkScale);
    $newWmH = (int) floor($wmH / $wmW * $newWmW);

    $resized = imagecreatetruecolor($newWmW, $newWmH);
    imagealphablending($resized, false);
    imagesavealpha($resized, true);
    imagecopyresampled($resized, $watermark, 0, 0, 0, 0, $newWmW, $newWmH, $wmW, $wmH);
    imagedestroy($watermark);

    imagealphablending($image, true);
    imagesavealpha($image, true);

    if ($position === 'repeat') {
        for ($x = 0; $x < $imgW; $x += $newWmW) {
            for ($y = 0; $y < $imgH; $y += $newWmH) {
                imagecopy($image, $resized, $x, $y, 0, 0, $newWmW, $newWmH);
            }
        }
    } else {
        switch ($position) {
            case 'center':
                $x = (int) floor(($imgW - $newWmW) / 2);
                $y = (int) floor(($imgH - $newWmH) / 2);
                break;
            case 'top-right':
                $x = (int) floor($imgW - $newWmW);
                $y = 0;
                break;
            case 'bottom-left':
                $x = 0;
                $y = (int) floor($imgH - $newWmH);
                break;
            case 'bottom-right':
                $x = (int) floor($imgW - $newWmW);
                $y = (int) floor($imgH - $newWmH);
                break;
            default:
                $x = 0;
                $y = 0;
        }
        imagecopy($image, $resized, $x, $y, 0, 0, $newWmW, $newWmH);
    }

    imageSaveByExt($image, $filePath, $ext);
    imagedestroy($image);
    imagedestroy($resized);
}
