<?php

namespace WebPExpress;

//use \Onnov\DetectEncoding\EncodingDetector;

use \WebPExpress\Config;
use \WebPExpress\ConvertHelperIndependent;
use \WebPExpress\ImageRoots;
use \WebPExpress\PathHelper;
use \WebPExpress\Paths;

class BulkConvert
{

    public static function defaultListOptions($config)
    {
        return [
            //'root' => Paths::getUploadDirAbs(),
            'ext' => $config['destination-extension'],
            'destination-folder' => $config['destination-folder'],  /* hm, "destination-folder" is a bad name... */
            'webExpressContentDirAbs' => Paths::getWebPExpressContentDirAbs(),
            'uploadDirAbs' => Paths::getUploadDirAbs(),
            'useDocRootForStructuringCacheDir' => (($config['destination-structure'] == 'doc-root') && (Paths::canUseDocRootForStructuringCacheDir())),
            'imageRoots' => new ImageRoots(Paths::getImageRootsDefForSelectedIds($config['scope'])),   // (Paths::getImageRootsDef()
            'filter' => [
                'only-converted' => false,
                'only-unconverted' => true,
                'image-types' => $config['image-types'],
                'max-depth' => 100,
            ],
            'flattenList' => true,
        ];
    }

    /**
     *  Get grouped list of files. They are grouped by image roots.
     *
     */
    public static function getList($config, $listOptions = null)
    {

        /*
        isUploadDirMovedOutOfWPContentDir
        isUploadDirMovedOutOfAbsPath
        isPluginDirMovedOutOfAbsPath
        isPluginDirMovedOutOfWpContent
        isWPContentDirMovedOutOfAbsPath */

        if (is_null($listOptions)) {
            $listOptions = self::defaultListOptions($config);
        }

        $rootIds = Paths::filterOutSubRoots($config['scope']);

        $groups = [];
        foreach ($rootIds as $rootId) {
            $groups[] = [
                'groupName' => $rootId,
                'root' => Paths::getAbsDirById($rootId)
            ];
        }

        foreach ($groups as $i => &$group) {
            $listOptions['root'] = $group['root'];
            /*
            No use, because if uploads is in wp-content, the cache root will be different for the files in uploads (if mingled)
            $group['image-root'] = ConvertHelperIndependent::getDestinationFolder(
                $group['root'],
                $listOptions['destination-folder'],
                $listOptions['ext'],
                $listOptions['webExpressContentDirAbs'],
                $listOptions['uploadDirAbs']
            );*/
            $group['files'] = self::getListRecursively('.', $listOptions);
            //'image-root' => ConvertHelperIndependent::getDestinationFolder()
        }

        return $groups;
        //self::moveRecursively($toDir, $fromDir, $srcDir, $fromExt, $toExt);
    }

    /**
     * $filter: all | converted | not-converted. "not-converted" for example returns paths to images that has not been converted
     */
    public static function getListRecursively($relDir, &$listOptions, $depth = 0)
    {
        $dir = $listOptions['root'] . '/' . $relDir;

        // Canonicalize because dir might contain "/./", which causes file_exists to fail (#222)
        $dir = PathHelper::canonicalize($dir);

        if (!@file_exists($dir) || !@is_dir($dir)) {
            return [];
        }

        $fileIterator = new \FilesystemIterator($dir);

        $results = [];
        $filter = &$listOptions['filter'];

        while ($fileIterator->valid()) {
            $filename = $fileIterator->getFilename();

            if (($filename != ".") && ($filename != "..")) {

                if (@is_dir($dir . "/" . $filename)) {
                    if ($listOptions['flattenList']) {
                        $results = array_merge($results, self::getListRecursively($relDir . "/" . $filename, $listOptions, $depth+1));
                    } else {
                        $r = [
                            'name' => $filename,
                            'isDir' => true,
                        ];
                        if ($depth > $listOptions['max-depth']) {
                            return $r;  // one item is enough to determine that it is not empty
                        }
                        if ($depth < $listOptions['max-depth']) {
                            $r['children'] = self::getListRecursively($relDir . "/" . $filename, $listOptions, $depth+1);
                            $r['isEmpty'] = (count($r['children']) == 0);
                        } else if ($depth == $listOptions['max-depth']) {
                            $c = self::getListRecursively($relDir . "/" . $filename, $listOptions, $depth+1);
                            $r['isEmpty'] = (count($c) == 0);
                            //$r['isEmpty'] = !(new \FilesystemIterator($dir))->valid();
                        }
                        $results[] = $r;
                    }
                } else {
                    // its a file - check if its a jpeg or png

                    if (!isset($filter['_regexPattern'])) {
                        $imageTypes = $filter['image-types'];
                        $fileExtensions = [];
                        if ($imageTypes & 1) {
                          $fileExtensions[] = 'jpe?g';
                        }
                        if ($imageTypes & 2) {
                          $fileExtensions[] = 'png';
                        }
                        $filter['_regexPattern'] = '#\.(' . implode('|', $fileExtensions) . ')$#';
                    }

                    if (preg_match($filter['_regexPattern'], $filename)) {
                        $addThis = true;

                        $destination = ConvertHelperIndependent::getDestination(
                            $dir . "/" . $filename,
                            $listOptions['destination-folder'],
                            $listOptions['ext'],
                            $listOptions['webExpressContentDirAbs'],
                            $listOptions['uploadDirAbs'],
                            $listOptions['useDocRootForStructuringCacheDir'],
                            $listOptions['imageRoots']
                        );
                        $webpExists = @file_exists($destination);

                        if (($filter['only-converted']) || ($filter['only-unconverted'])) {
                            //$cacheDir = $listOptions['image-root'] . '/' . $relDir;

                            // Check if corresponding webp exists
                            /*
                            if ($listOptions['ext'] == 'append') {
                                $webpExists = @file_exists($cacheDir . "/" . $filename . '.webp');
                            } else {
                                $webpExists = @file_exists(preg_replace("/\.(jpe?g|png)\.webp$/", '.webp', $filename));
                            }*/

                            if (!$webpExists && ($filter['only-converted'])) {
                                $addThis = false;
                            }
                            if ($webpExists && ($filter['only-unconverted'])) {
                                $addThis = false;
                            }
                        } else {
                            $addThis = true;
                        }

                        if ($addThis) {

                            $path = substr($relDir . "/", 2) . $filename;   // (we cut the leading "./" off with substr)

                            // Additional safety check: verify the file actually exists before adding to list
                            $fullPath = $dir . "/" . $filename;
                            if (!file_exists($fullPath)) {
                                continue; // Skip this file if it doesn't exist
                            }

                            // Check if the string can be encoded to json (if not: change it to a string that can)
                            if (json_encode($path, JSON_UNESCAPED_UNICODE) === false) {
                                /*
                                json_encode failed. This means that the string was not UTF-8.
                                Lets see if we can convert it to UTF-8.
                                This is however tricky business (see #471)
                                */

                                $encodedToUTF8 = false;

                                // First try library that claims to do better than mb_detect_encoding
                                /*
                                DISABLED, because Onnov EncodingDetector requires PHP 7.2
                                https://wordpress.org/support/topic/get-http-error-500-after-new-update-2/

                                if (!$encodedToUTF8) {
                                    $detector = new EncodingDetector();

                                    $dectedEncoding = $detector->getEncoding($path);

                                    if ($dectedEncoding !== 'utf-8') {
                                        if (function_exists('iconv')) {
                                            $res = iconv($dectedEncoding, 'utf-8//TRANSLIT', $path);
                                            if ($res !== false) {
                                                $path = $res;
                                                $encodedToUTF8 = true;
                                            }
                                        }
                                    }


                                    try {
                                        // iconvXtoEncoding should work now hm, issue #5 has been fixed
                                        $path = $detector->iconvXtoEncoding($path);
                                        $encodedToUTF8 = true;
                                    } catch (\Exception $e) {

                                    }
                                }*/

                                // Try mb_detect_encoding
                                if (!$encodedToUTF8) {
                                    if (function_exists('mb_convert_encoding')) {
                                        $encoding = mb_detect_encoding($path, mb_detect_order(), true);
                                    		if ($encoding) {
                                    			$path = mb_convert_encoding($path, 'UTF-8', $encoding);
                                          $encodedToUTF8 = true;
                                    		}
                                    }
                                }

                                if (!$encodedToUTF8) {
                                    /*
                                    We haven't yet succeeded in encoding to UTF-8.
                                    What should we do?
                                    1. Skip the file? (no, the user will not know about the problem then)
                                    2. Add it anyway? (no, if this string causes problems to json_encode, then we will have
                                          the same problem when encoding the entire list - result: an empty list)
                                    3. Try wp_json_encode? (no, it will fall back on "wp_check_invalid_utf8", which has a number of
                                          things we do not want)
                                    4. Encode it to UTF-8 assuming that the string is encoded in the most common encoding (Windows-1252) ?
                                          (yes, if we are lucky with the guess, it will work. If it is in another encoding, the conversion
                                          will not be correct, and the user will then know about the problem. And either way, we will
                                          have UTF-8 string, which will not break encoding of the list)
                                    */

                                    // https://stackoverflow.com/questions/6606713/json-encode-non-utf-8-strings
                                    if (function_exists('mb_convert_encoding')) {
                                        $path = mb_convert_encoding($path, "UTF-8", "Windows-1252");
                                    } elseif (function_exists('iconv')) {
                                        $path = iconv("CP1252", "UTF-8", $path);
                                    } elseif (function_exists('utf8_encode')) {
                                        // utf8_encode converts from ISO-8859-1 to UTF-8
                                        $path = utf8_encode($path);
                                    } else {
                                        $path = '[cannot encode this filename to UTF-8]';
                                    }

                                }

                            }
                            if ($listOptions['flattenList']) {
                              $results[] = $path;
                            } else {
                              $results[] = [
                                'name' => basename($path),
                                'isConverted' => $webpExists
                              ];
                              if ($depth > $listOptions['max-depth']) {
                                  return $results;  // one item is enough to determine that it is not empty
                              }

                            }
                        }
                    }
                }
            }
            $fileIterator->next();
        }
        return $results;
    }

/*
    public static function convertFile($source)
    {
        $config = Config::loadConfigAndFix();
        $options = Config::generateWodOptionsFromConfigObj($config);

        $destination = ConvertHelperIndependent::getDestination(
            $source,
            $options['destination-folder'],
            $options['destination-extension'],
            Paths::getWebPExpressContentDirAbs(),
            Paths::getUploadDirAbs()
        );
        $result = ConvertHelperIndependent::convert($source, $destination, $options);

        //$result['destination'] = $destination;
        if ($result['success']) {
            $result['filesize-original'] = @filesize($source);
            $result['filesize-webp'] = @filesize($destination);
        }
        return $result;
    }
*/

    public static function processAjaxListUnconvertedFiles()
    {
        if (!check_ajax_referer('webpexpress-ajax-list-unconverted-files-nonce', 'nonce', false)) {
            wp_send_json_error('The security nonce has expired. You need to reload the settings page (press F5) and try again)');
            wp_die();
        }

        $config = Config::loadConfigAndFix();
        $arr = self::getList($config);

        // We use "wp_json_encode" rather than "json_encode" because it handles problems if there is non UTF-8 characters
        // There should be none, as we have taken our measures, but no harm in taking extra precautions
        $json = wp_json_encode($arr, JSON_UNESCAPED_SLASHES | JSON_NUMERIC_CHECK | JSON_PRETTY_PRINT | JSON_UNESCAPED_UNICODE);
        if ($json === false) {
            // TODO: We can do better error handling than this!
            echo '';
        } else {
            echo $json;
        }

        wp_die();
    }

}
