<?php

use ShortPixel\ShortPixelLogger\ShortPixelLogger as Log;
use ShortPixel\Model\FileModel as FileModel;
use ShortPixel\Model\Directorymodel as DirectoryModel;
class ShortPixelPng2Jpg
{
    private $_settings = null;
    public function __construct($settings)
    {
        if (function_exists('wp_raise_memory_limit')) {
            wp_raise_memory_limit('image');
        }
        $this->_settings = $settings;
    }
    protected function canConvertPng2Jpg($image)
    {
        $transparent = 0;
        $transparent_pixel = $img = $bg = false;
        if (!file_exists($image)) {
            Log::addDebug("PNG2JPG FILE MISSING:  " . $image);
            $transparent = 1;
        } elseif (ord(file_get_contents($image, false, null, 25, 1)) & 4) {
            Log::addDebug("PNG2JPG: 25th byte has thrid bit 1 - transparency");
            $transparent = 1;
        } else {
            $contents = file_get_contents($image);
            if (stripos($contents, 'PLTE') !== false && stripos($contents, 'tRNS') !== false) {
                $transparent = 1;
            }
            if (!$transparent) {
                $is = getimagesize($image);
                Log::addDebug("PNG2JPG Image width: " . $is[0] . " height: " . $is[1] . " aprox. size: " . round($is[0] * $is[1] * 5 / 1024 / 1024) . "M memory limit: " . ini_get('memory_limit') . " USED: " . memory_get_usage());
                Log::addDebug("PNG2JPG create from png {$image}");
                $img = @imagecreatefrompng($image);
                Log::addDebug("PNG2JPG created from png");
                if (!$img) {
                    Log::addDebug("PNG2JPG not a PNG, imagecreatefrompng failed ");
                    $transparent = true;
                } else {
                    Log::addDebug("PNG2JPG is PNG");
                    $w = imagesx($img);
                    $h = imagesy($img);
                    Log::addDebug("PNG2JPG width {$w} height {$h}. Now checking pixels.");
                    for ($i = 0; $i < $w; $i++) {
                        for ($j = 0; $j < $h; $j++) {
                            $rgba = imagecolorat($img, $i, $j);
                            if (($rgba & 0x7f000000) >> 24) {
                                $transparent_pixel = true;
                                break;
                            }
                        }
                    }
                }
            }
        }
        Log::addDebug("PNG2JPG is " . (!$transparent && !$transparent_pixel ? " not" : "") . " transparent");
        return array('notTransparent' => !$transparent && !$transparent_pixel, 'img' => $img);
    }
    protected function doConvertPng2Jpg($params, $backup, $suffixRegex = false, $img = false)
    {
        $image = $params['file'];
        $fs = \wpSPIO()->filesystem();
        Log::addDebug("PNG2JPG doConvert {$image}");
        if (!$img) {
            Log::addDebug("PNG2JPG doConvert create from PNG");
            $img = file_exists($image) ? imagecreatefrompng($image) : false;
            if (!$img) {
                Log::addDebug("PNG2JPG doConvert image cannot be created.");
                return (object) array("params" => $params, "unlink" => false);
            }
        }
        $x = imagesx($img);
        $y = imagesy($img);
        Log::addDebug("PNG2JPG doConvert width {$x} height {$y}");
        $bg = imagecreatetruecolor($x, $y);
        if (!$bg) {
            return (object) array("params" => $params, "unlink" => false);
        }
        imagefill($bg, 0, 0, imagecolorallocate($bg, 255, 255, 255));
        imagealphablending($bg, 1);
        imagecopy($bg, $img, 0, 0, 0, 0, $x, $y);
        imagedestroy($img);
        $fsFile = $fs->getFile($image);
        $filename = $fsFile->getFileName();
        $newFileName = $fsFile->getFileBase() . '.jpg';
        $fsNewFile = $fs->getFile($fsFile->getFileDir() . $newFileName);
        $uniquefile = $this->unique_file($fsFile->getFileDir(), $fsNewFile);
        $newPath = $uniquefile->getFullPath();
        $newUrl = str_replace($filename, $uniquefile->getFileName(), $params['url']);
        if (imagejpeg($bg, $newPath, 90)) {
            Log::addDebug("PNG2JPG doConvert created JPEG at {$newPath}");
            $newSize = filesize($newPath);
            $origSize = filesize($image);
            if ($newSize > $origSize * 0.95 || $newSize == 0) {
                Log::addDebug("PNG2JPG converted image is larger ({$newSize} vs. {$origSize}), keeping the PNG");
                unlink($newPath);
                return (object) array("params" => $params, "unlink" => false);
            }
            if ($backup) {
                $imageForBk = trailingslashit(dirname($image)) . ShortPixelAPI::MB_basename($newPath, '.jpg') . '.png';
                Log::addDebug("imageForBk should be PNG: {$imageForBk}");
                if ($image != $imageForBk) {
                    Log::addDebug("PNG2JPG doConvert rename {$image} to {$imageForBk}");
                    @rename($image, $imageForBk);
                }
                if (!file_exists($imageForBk)) {
                    unlink($newPath);
                    return (object) array("params" => $params, "unlink" => false);
                }
                $image = $imageForBk;
                $ret = ShortPixelAPI::backupImage($image, array($image));
                if ($ret['Status'] !== ShortPixelAPI::STATUS_SUCCESS) {
                    Log::addDebug("PNG2JPG couldn't backup, keeping the PNG");
                    unlink($newPath);
                    return (object) array("params" => $params, "unlink" => false);
                }
            }
            $params['file'] = $newPath;
            Log::addDebug("Original_file should be PNG: {$image}");
            $params['original_file'] = $image;
            $params['url'] = $newUrl;
            $params['type'] = 'image/jpeg';
            $params['png_size'] = $origSize;
            $params['jpg_size'] = $newSize;
        }
        return (object) array("params" => $params, "unlink" => $image);
    }
    private function unique_file(DirectoryModel $dir, FileModel $file, $number = 0)
    {
        if (!$file->exists()) {
            return $file;
        }
        $number = 0;
        $fs = \wpSPIO()->filesystem();
        $base = $file->getFileBase();
        $ext = $file->getExtension();
        while ($file->exists()) {
            $number++;
            $numberbase = $base . '-' . $number;
            Log::addDebug('check for unique file -- ' . $dir->getPath() . $numberbase . '.' . $ext);
            $file = $fs->getFile($dir->getPath() . $numberbase . '.' . $ext);
        }
        return $file;
    }
    protected function isExcluded($params)
    {
        if (is_array($this->_settings->excludePatterns)) {
            foreach ($this->_settings->excludePatterns as $item) {
                $type = trim($item["type"]);
                if (in_array($type, array('name', 'path')) && WpShortPixel::matchExcludePattern($params['file'], $item['value'])) {
                    return true;
                }
                if (isset($params['width']) && isset($params['height']) && 'size' == $type && WPShortPixel::isProcessableSize($params['width'], $params['height'], $item['value'])) {
                    return true;
                }
            }
        }
        return false;
    }
    public function convertPng2Jpg($params)
    {
        if (!$this->_settings->png2jpg || strtolower(substr($params['file'], -4)) !== '.png') {
            return $params;
        }
        if ($this->isExcluded($params)) {
            return $params;
        }
        $image = $params['file'];
        Log::addDebug("Convert Media PNG to JPG on upload: {$image}");
        if ($this->_settings->png2jpg == 2) {
            $doConvert = true;
        } else {
            $ret = $this->canConvertPng2Jpg($image);
            $doConvert = $ret['notTransparent'];
        }
        if ($doConvert) {
            $ret = $this->doConvertPng2Jpg($params, $this->_settings->backupImages, false, isset($ret['img']) ? $ret['img'] : false);
            if ($ret->unlink) {
                @unlink($ret->unlink);
            }
            $paramsC = $ret->params;
            if ($paramsC['type'] == 'image/jpeg') {
                $conv = $this->_settings->convertedPng2Jpg;
                foreach ($conv as $key => $val) {
                    if (time() - $val['timestamp'] > 3600) {
                        unset($conv[$key]);
                    }
                }
                $conv[$paramsC['file']] = array('pngFile' => $paramsC['original_file'], 'backup' => $this->_settings->backupImages, 'optimizationPercent' => round(100.0 * (1.0 - $paramsC['jpg_size'] / $paramsC['png_size'])), 'timestamp' => time());
                $this->_settings->convertedPng2Jpg = $conv;
            }
            return $paramsC;
        }
        return $params;
    }
    public function checkConvertMediaPng2Jpg($itemHandler)
    {
        $meta = $itemHandler->getRawMeta();
        $ID = $itemHandler->getId();
        $fs = \wpSPIO()->filesystem();
        if (!$this->_settings->png2jpg || !isset($meta['file']) || strtolower(substr($meta['file'], -4)) !== '.png') {
            return;
        }
        if ($this->isExcluded($meta)) {
            return;
        }
        Log::addDebug("Send to processing: Convert Media PNG to JPG #{$ID} META: " . json_encode($meta));
        $image = $meta['file'];
        $imageFile = $fs->getAttachedFile($ID);
        $imagePath = $imageFile->getFullPath();
        $basePath = trailingslashit(str_replace($image, "", $imagePath));
        $imageUrl = wp_get_attachment_url($ID);
        $baseUrl = self::removeUrlProtocol(trailingslashit(str_replace($image, "", $imageUrl)));
        if (isset($meta['ShortPixel']['Retries']) && $meta['ShortPixel']['Retries'] > 3 && isset($meta['ShortPixel']['ErrCode']) && $meta['ShortPixel']['ErrCode'] == ShortPixelAPI::ERR_PNG2JPG_MEMORY) {
            Log::addWarn("PNG2JPG too many memory failures!");
            throw new Exception('Not enough memory to convert from PNG to JPG.', ShortPixelAPI::ERR_PNG2JPG_MEMORY);
        }
        $meta['ShortPixelImprovement'] = 'Error: <i>Not enough memory to convert from PNG to JPG.</i>';
        if (!isset($meta['ShortPixel']) || !is_array($meta['ShortPixel'])) {
            $meta['ShortPixel'] = array();
        }
        $meta['ShortPixel']['Retries'] = isset($meta['ShortPixel']['Retries']) ? $meta['ShortPixel']['Retries'] + 1 : 1;
        $meta['ShortPixel']['ErrCode'] = ShortPixelAPI::ERR_PNG2JPG_MEMORY;
        update_post_meta($ID, '_wp_attachment_metadata', $meta);
        if ($this->_settings->png2jpg == 2) {
            $doConvert = true;
        } else {
            $retC = $this->canConvertPng2Jpg($imagePath);
            $doConvert = $retC['notTransparent'];
        }
        if (!$doConvert) {
            Log::addDebug("PNG2JPG not a PNG, or transparent when this setting is off - " . $imagePath);
            return $meta;
        }
        Log::addDebug(" CONVERTING MAIN: {$imagePath}");
        $retMain = $this->doConvertPng2Jpg(array('file' => $imagePath, 'url' => false, 'type' => 'image/png'), $this->_settings->backupImages, false, isset($retC['img']) ? $retC['img'] : false);
        Log::addDebug("PNG2JPG doConvert Main RETURNED " . json_encode($retMain));
        $ret = $retMain->params;
        $toUnlink = array();
        $toReplace = array();
        unset($meta['ShortPixelImprovement']);
        unset($meta['ShortPixel']['ErrCode']);
        $meta['ShortPixel']['Retries'] -= 1;
        update_post_meta($ID, '_wp_attachment_metadata', $meta);
        if ($ret['type'] == 'image/jpeg') {
            $toUnlink[] = $retMain->unlink;
            do_action('shortpixel/image/convertpng2jpg_before', $ID, $meta);
            $baseRelPath = dirname($image);
            if ($baseRelPath == '.') {
                $baseRelPath = '';
            } else {
                $baseRelPath = trailingslashit($baseRelPath);
            }
            $toReplace[self::removeUrlProtocol($imageUrl)] = $baseUrl . $baseRelPath . wp_basename($ret['file']);
            $pngSize = $ret['png_size'];
            $jpgSize = $ret['jpg_size'];
            Log::addDebug(" IMAGE PATH: {$imagePath}");
            $imagePath = isset($ret['original_file']) ? $ret['original_file'] : $imagePath;
            Log::addDebug(" SET IMAGE PATH: {$imagePath}");
            $duplicates = $this->updateFileAlsoInWPMLDuplicates($ID, $meta, str_replace($basePath, '', $ret['file']));
            Log::addDebug(" WPML duplicates: " . json_encode($duplicates));
            $originalSizes = isset($meta['sizes']) ? $meta['sizes'] : array();
            $filesConverted = array();
            foreach ($meta['sizes'] as $size => $info) {
                if (isset($filesConverted[$info['file']])) {
                    Log::addDebug("PNG2JPG DUPLICATED THUMB: " . $size);
                    if ($filesConverted[$info['file']] === false) {
                        Log::addDebug("PNG2JPG DUPLICATED THUMB not converted");
                        continue;
                    }
                    Log::addDebug("PNG2JPG DUPLICATED THUMB already converted");
                    $rett = $filesConverted[$info['file']];
                } else {
                    $retThumb = $this->doConvertPng2Jpg(array('file' => $basePath . $baseRelPath . $info['file'], 'url' => false, 'type' => 'image/png'), $this->_settings->backupImages, "[0-9]+x[0-9]+");
                    $rett = $retThumb->params;
                }
                Log::addDebug("PNG2JPG doConvert thumb RETURNED " . json_encode($rett));
                if ($rett['type'] == 'image/jpeg') {
                    $toUnlink[] = $retThumb->unlink;
                    Log::addDebug("PNG2JPG thumb is jpg");
                    $pngSize += $rett['png_size'];
                    $jpgSize += $rett['jpg_size'];
                    Log::addDebug("PNG2JPG total PNG size: {$pngSize} total JPG size: {$jpgSize}");
                    $originalSizes[$size]['file'] = wp_basename($rett['file'], '.jpg') . '.png';
                    Log::addDebug("PNG2JPG thumb original: " . $originalSizes[$size]['file']);
                    $toReplace[$baseUrl . $baseRelPath . $info['file']] = $baseUrl . $baseRelPath . wp_basename($rett['file']);
                    $filesConverted[$info['file']] = $rett;
                    $this->updateThumbAlsoInWPMLDuplicates($ID, $meta, $duplicates, $size, wp_basename($rett['file']));
                } else {
                    $filesConverted[$info['file']] = false;
                }
            }
            $meta['ShortPixelPng2Jpg'] = array('originalFile' => $imagePath, 'originalSizes' => $originalSizes, 'backup' => $this->_settings->backupImages, 'optimizationPercent' => round(100.0 * (1.0 - $jpgSize / $pngSize)));
            update_post_meta($ID, '_wp_attachment_metadata', $meta);
            $itemHandler->deleteItemCache();
            Log::addDebug("Updated meta: " . json_encode($meta));
            wp_update_post(array('ID' => $ID, 'post_mime_type' => 'image/jpeg'));
            do_action('shortpixel/image/convertpng2jpg_after', $ID, $meta);
        }
        if (count($toReplace)) {
            self::png2JpgUpdateUrls(array(), $toReplace);
        }
        $fs = \wpSPIO()->filesystem();
        foreach ($toUnlink as $unlink) {
            if ($unlink) {
                Log::addDebug("PNG2JPG remove file {$unlink}");
                $fileObj = $fs->getFile($unlink);
                $fileObj->delete();
            }
        }
        Log::addDebug("PNG2JPG done. Return: " . json_encode($meta));
        return $meta;
    }
    protected function updateFileAlsoInWPMLDuplicates($parentID, &$parentMeta, $file)
    {
        $duplicates = ShortPixelMetaFacade::getWPMLDuplicates($parentID);
        foreach ($duplicates as $ID) {
            $meta = $parentID == $ID ? $parentMeta : wp_get_attachment_metadata($ID);
            $meta['file'] = $file;
            $meta['type'] = 'image/jpeg';
            if ($parentID == $ID) {
                $parentMeta = $meta;
            }
            update_attached_file($ID, $meta['file']);
            update_post_meta($ID, '_wp_attachment_metadata', $meta);
        }
        return $duplicates;
    }
    protected function updateThumbAlsoInWPMLDuplicates($parentID, &$parentMeta, $duplicates, $size, $thumbnail)
    {
        foreach ($duplicates as $ID) {
            $meta = $parentID == $ID ? $parentMeta : wp_get_attachment_metadata($ID);
            $meta['sizes'][$size]['file'] = wp_basename($thumbnail);
            $meta['sizes'][$size]['mime-type'] = 'image/jpeg';
            if ($parentID == $ID) {
                $parentMeta = $meta;
            }
            update_post_meta($ID, '_wp_attachment_metadata', $meta);
        }
    }
    public static function png2JpgUpdateUrls($options, $map)
    {
        global $wpdb;
        Log::addDebug("PNG2JPG update URLS " . json_encode($map));
        $results = array();
        $queries = array('content' => array("UPDATE {$wpdb->posts} SET post_content = replace(post_content, %s, %s)", __('Content Items (Posts, Pages, Custom Post Types, Revisions)', 'shortpixel-image-optimiser')), 'excerpts' => array("UPDATE {$wpdb->posts} SET post_excerpt = replace(post_excerpt, %s, %s)", __('Excerpts', 'shortpixel-image-optimiser')), 'attachments' => array("UPDATE {$wpdb->posts} SET guid = replace(guid, %s, %s) WHERE post_type = 'attachment'", __('Attachments', 'shortpixel-image-optimiser')), 'links' => array("UPDATE {$wpdb->links} SET link_url = replace(link_url, %s, %s)", __('Links', 'shortpixel-image-optimiser')), 'custom' => array("UPDATE {$wpdb->postmeta} SET meta_value = replace(meta_value, %s, %s)", __('Custom Fields', 'shortpixel-image-optimiser')), 'guids' => array("UPDATE {$wpdb->posts} SET guid = replace(guid, %s, %s) ", __('GUIDs', 'shortpixel-image-optimiser')));
        if (count($options) == 0) {
            $options = array_keys($queries);
        }
        $startTime = microtime(true);
        foreach ($options as $option) {
            if ($option == 'custom') {
                $n = 0;
                $page_size = WpShortPixelMediaLbraryAdapter::getOptimalChunkSize('postmeta');
                for ($page = 0; $items = $wpdb->get_results("SELECT * FROM {$wpdb->postmeta} LIMIT " . $page * $page_size . ", {$page_size}"); $page++) {
                    foreach ($items as $item) {
                        $value = $item->meta_value;
                        if (trim($value) == '' || $item->meta_key == '_wp_attached_file' || $item->meta_key == '_wp_attachment_metadata') {
                            continue;
                        }
                        $edited = (object) array('data' => $value, 'replaced' => false);
                        foreach ($map as $oldurl => $newurl) {
                            if (strlen($newurl)) {
                                $editedOne = self::png2JpgUnserializeReplace($oldurl, $newurl, $edited->data);
                                $edited->data = $editedOne->data;
                                $edited->replaced = $edited->replaced || $editedOne->replaced;
                            }
                        }
                        if ($edited->replaced) {
                            $fix = $wpdb->query("UPDATE {$wpdb->postmeta} SET meta_value = '" . $edited->data . "' WHERE meta_id = " . $item->meta_id);
                            if ($fix) {
                                $n++;
                            }
                        }
                    }
                    $timeElapsed = microtime(true) - $startTime;
                    if ($timeElapsed > SHORTPIXEL_MAX_EXECUTION_TIME / 2) {
                        if (\wpSPIO()->env()->is_function_usable('set_time_limit') && set_time_limit(SHORTPIXEL_MAX_EXECUTION_TIME)) {
                            $startTime += SHORTPIXEL_MAX_EXECUTION_TIME / 2;
                        } else {
                            break;
                        }
                    }
                }
                $results[$option] = array($n, $queries[$option][1]);
            } else {
                foreach ($map as $oldurl => $newurl) {
                    if (strlen($newurl)) {
                        $prepared = $wpdb->prepare($queries[$option][0], $oldurl, $newurl);
                        Log::addDebug("Prepared Query", $prepared);
                        $result = $wpdb->query($prepared);
                        $results[$option] = array($result, $queries[$option][1]);
                    }
                }
            }
        }
        return $results;
    }
    public static function removeUrlProtocol($url)
    {
        return preg_replace("/^http[s]{0,1}:\\/\\//", "", $url);
    }
    public static function png2JpgUnserializeReplace($from = '', $to = '', $data = '', $serialised = false)
    {
        $replaced = false;
        try {
            if (false !== is_serialized($data)) {
                if (false === strpos($data, wp_basename($from))) {
                    return (object) array('data' => $data, 'replaced' => false);
                }
                $unserialized = unserialize($data);
                $ret = self::png2JpgUnserializeReplace($from, $to, $unserialized, true);
                $data = $ret->data;
                $replaced = $replaced || $ret->replaced;
            } elseif (is_array($data)) {
                $_tmp = array();
                foreach ($data as $key => $value) {
                    $ret = self::png2JpgUnserializeReplace($from, $to, $value, false);
                    $_tmp[$key] = $ret->data;
                    $replaced = $replaced || $ret->replaced;
                }
                $data = $_tmp;
                unset($_tmp);
            } elseif (is_object($data)) {
                foreach (get_object_vars($data) as $key => $value) {
                    $ret = self::png2JpgUnserializeReplace($from, $to, $value, false);
                    $_tmp[$key] = $ret->data;
                    $replaced = $replaced || $ret->replaced;
                }
                $data = (object) $_tmp;
            } elseif (is_string($data)) {
                if (false !== strpos($data, $from)) {
                    $replaced = true;
                    $data = str_replace($from, $to, $data);
                } elseif (strlen($from) > strlen($data) && strlen($data) >= strlen(wp_basename($from)) && strpos($from, $data) == strlen($from) - strlen($data)) {
                    $replaced = true;
                    $data = substr($to, strlen($from) - strlen($data));
                }
            }
            if ($serialised) {
                return (object) array('data' => serialize($data), 'replaced' => $replaced);
            }
        } catch (Exception $error) {
        }
        return (object) array('data' => $data, 'replaced' => $replaced);
    }
}