<?php

namespace yndenz\Plugins\OptimizeImages;

use Exception;
use Spatie\ImageOptimizer\OptimizerChainFactory;
use Spatie\ImageOptimizer\Optimizers\Gifsicle;
use Spatie\ImageOptimizer\Optimizers\Jpegoptim;
use Spatie\ImageOptimizer\Optimizers\Optipng;
use Spatie\ImageOptimizer\Optimizers\Pngquant;
use WP_Post;
use WP_Query;

class Images {

    public static $supported_mime_types = array('image/jpeg', 'image/gif', 'image/png');
    public static $statusses = array('pending', 'done', 'failed');

    public static function init() {
        add_action('wp_ajax_optimize_images_load', array(static::class, 'load'));
        add_action('wp_ajax_optimize_images_optimize', array(static::class, 'optimize'));
        add_filter('wp_generate_attachment_metadata', array(static::class, 'afterUpload'), 10, 2);
    }

    /**
     * Fetch a list of images.
     */
    public static function load() {
        $query = new WP_Query(array(
            'post_type' => 'attachment',
            'post_mime_type' => static::$supported_mime_types,
            'posts_per_page' => 20,
            'post_status' => 'any',
            'paged' => $_GET['page'],
            'meta_query' => array(
                'relation' => 'OR',
                'exists_clause' => array(
                    'key' => 'optimize_images_status',
                    'compare' => 'EXISTS',
                ),
                'not_exists_clause' => array(
                    'key' => 'optimize_images_status',
                    'compare' => 'NOT EXISTS'
                )
            ),
            'orderby' => 'exists_clause date',
            'order' => 'ASC'
        ));

        $files = array_map(function ($attachment) {
            $status = get_post_meta($attachment->ID, 'optimize_images_status', true);
            if (empty($status)) {
                $status = 0;
            }

            return array(
                'id' => $attachment->ID,
                'filename' => wp_get_attachment_image_url($attachment->ID, 'medium'),
                'status' => static::$statusses[$status]
            );
        }, $query->posts);

        wp_send_json(array('files' => $files, 'num_pages' => $query->max_num_pages));
    }

    /**
     * Optimize an image.
     *
     * @param int|null $attachment_id ID of the attachment. If null, GET parameter for the request is used (AJAX calls).
     *
     * @throws Exception
     */
    public static function optimize($attachment_id = null) {
        if (empty($attachment_id) && !empty($_GET)) {
            $attachment_id = $_GET['id'];
        }

        $attachment = get_post($attachment_id);
        if (!$attachment instanceof WP_Post) {
            throw new Exception('Image not found');
        }

        $optimizerChain = OptimizerChainFactory::create();
        $optimizerChain->addOptimizer(new Jpegoptim(array('-m85', '--strip-all', '--all-progressive')));
        $optimizerChain->addOptimizer(new Pngquant(array('--force')));
        $optimizerChain->addOptimizer(new Optipng(array('-i0', '-o2', '-quiet')));
        $optimizerChain->addOptimizer(new Gifsicle(array('-b', '-O3')));
        foreach (get_intermediate_image_sizes() as $size) {
            $file = str_replace(WP_CONTENT_URL, WP_CONTENT_DIR, wp_get_attachment_image_src($attachment->ID, $size)[0]);
            if (file_exists($file)) {
                $optimizerChain->optimize($file);
            }
        }

        $new_status = array_search('done', static::$statusses);

        if (update_post_meta($attachment->ID, 'optimize_images_status', $new_status) === false) {
            throw new Exception('Image optimized, but failed to update meta');
        }

        if ($_SERVER['REQUEST_METHOD'] === 'GET') {
            wp_send_json(array('status' => static::$statusses[$new_status]));
        }
    }

    /**
     * Optimize images automatically after they are being uploaded and resized.
     *
     * @param array $metadata
     * @param int $attachment_id
     *
     * @return array
     */
    public static function afterUpload($metadata, $attachment_id) {
        try {
            static::optimize($attachment_id);
        } catch (Exception $e) {
            // do nothing
        }
        return $metadata;
    }

}