<?php 
namespace Next3Offload\Modules;
defined( 'ABSPATH' ) || exit;

class Webp{
    private static $instance;

    const PNGS_SIZE_LIMIT = 1048576;

    public $options_map = array(
      'completed' => 'next3_optimizer_webp_conversion_completed',
      'status'    => 'next3_optimizer_webp_conversion_status',
      'stopped'   => 'next3_optimizer_webp_conversion_stopped',
    );

    public $non_optimized = 'next3_optimizer_total_non_converted_images';

    public $batch_skipped = 'next3_optimizer_is_converted_to_webp';

    public function init() {
      $settings_options = next3_options();
      $webp_enable = ($settings_options['optimization']['webp_enable']) ?? 'no';
      $compression_enable = ($settings_options['optimization']['compression']) ?? 'no';
      $copy_file = ($settings_options['storage']['copy_file']) ?? 'no';

      //delete attachment data
      add_action( 'delete_attachment', array( $this, 'delete_webp_copy' ) );

      if( $webp_enable == 'yes'){
        add_action( 'edit_attachment', array( $this, 'regenerate_webp_copy' ) );
        add_action( 'wp_generate_attachment_metadata', array( $this, 'optimize_new_image' ), 119, 2 );
      }
    }
    
    public function delete_webp_files() {
      $basedir = self::get_uploads_dir();

      $result = true;
      // delete all webp file
      if( function_exists('exec')){
        exec( "find $basedir -name '*.webp' -type f -print0 | xargs -L 500 -0 rm", $output, $result );
      }
      if ( true !== boolval( $result ) ) {
        $this->reset_image_optimization_status();
      }
      
      // back to orginal file
      if( function_exists('exec')){
        exec( "find $basedir -regextype posix-extended -type f -regex '.*bak.(png|jpg|jpeg|gif)$' -exec rename '.bak' '' {} \;", $output, $result );
      }
      return $result;
    }

    public function delete_webp_copy( $id ) {
      
      $main_image = next3_get_attached_file( $id, true);
      $metadata   = next3_wp_get_attachment_metadata( $id, true);
      $basename   = basename( $main_image );
  
      if(strpos($main_image, ".webp") === false){
          $main_image .= '.webp';
      }
      if ( !file_exists( $main_image ) ) {
          return;
      }
      @unlink( $main_image );
  
      if ( ! empty( $metadata['sizes'] ) ) {
        foreach ( $metadata['sizes'] as $size ) {
          @unlink( str_replace( $basename, $size['file'], $main_image ));
        }
      }
      
    }

    public function regenerate_webp_copy( $id ) {
      $this->delete_webp_copy( $id );
     
      //get meta data
      $metadata = next3_wp_get_attachment_metadata( $id, true);
      
      delete_post_meta($id, 'next3_optimizer_is_converted_to_webp');

      // check settings 
      $settings_options = next3_options();
      $copy_file = ($settings_options['storage']['copy_file']) ?? 'no';
      $remove_local = isset($settings_options['storage']['remove_local']) ? true : false;
      $compression_enable = ($settings_options['optimization']['compression']) ?? 'no';
      if( $compression_enable == 'yes'){
        next3_core()->optimizer_ins->optimize( $id, $metadata);
      }

      // convert webp format
      $this->optimize( $id, $metadata );

      $status_data = next3_service_status();
      $offload_status = ($status_data['offload']) ?? false;
      if( !$offload_status ){
        return $id;
      }

      // offload settings
      if( next3_get_post_meta($id, '_next3_attached_url') === false){     
        if( $copy_file == 'yes'){
            next3_core()->action_ins->wpmedia_to_aws('', $id, $remove_local);
        }
      }
    }

    public function optimize_new_image( $data, $attachment_id ) {
      if( next3_get_post_meta($attachment_id, '_next3_attached_url') === false){    
        // Optimize the image.
        $this->regenerate_webp_copy( $attachment_id);
      }
      return $data;
    }

    public function optimize( $id, $metadata, $main_image = '') {
      if( true === next3_check_post_meta($id, 'next3_optimizer_is_converted_to_webp')){
        return true;
      }

      if( empty($main_image) ){
        $main_image = next3_get_attached_file( $id, true);
      }
      
      $basename = basename( $main_image );

      // overwrite image
      if(!is_readable($main_image) ){
        $old_main_image = preg_replace( '~.(png|jpg|jpeg|gif)$~', '.bak.$1', $main_image );
        if ( file_exists( $old_main_image ) ) {
          copy( $old_main_image, $main_image );
        }
      }
  
      $status = $this->generate_webp_file( $main_image );
      
      if ( true === boolval( $status ) ) {
        return false;
      }
  
      if ( ! empty( $metadata['sizes'] ) ) {
        foreach ( $metadata['sizes'] as $size ) {
          $checkwebp = str_replace( $basename, $size['file'], $main_image );
          
          // overwrite image
          if( !is_readable($checkwebp) ){
            $old_main_image = preg_replace( '~.(png|jpg|jpeg|gif)$~', '.bak.$1', $checkwebp );
            if ( file_exists( $old_main_image ) ) {
              copy( $old_main_image, $checkwebp );
            }
          }

          $status = $this->generate_webp_file( $checkwebp );
        }
      }
      
      // Everything ran smoothly.
      if ( true !== boolval( $status ) ) {
        next3_update_post_meta( $id, 'next3_optimizer_is_converted_to_webp', 1 );
      }
      return true;
    }

    public function generate_webp_file( $filepath ) {
      
      if ( ! file_exists( $filepath ) ) {
        return true;
      }

      //enable read/write permission
		  chmod($filepath, 0777);

      
      if ( file_exists( $filepath . '.webp' ) ) {
        @unlink( $filepath . '.webp' ); //phpcs:ignore
      }

      $backup_filepath = preg_replace( '~.(png|jpg|jpeg|gif)$~', '.bak.$1', $filepath );
      if (
        ! file_exists( $backup_filepath )
      ) {
        copy( $filepath, $backup_filepath );
      }
  
      // Get image type.
      $type = exif_imagetype( $filepath );
  
      $ext = strtolower(pathinfo($filepath, PATHINFO_EXTENSION));
      if (!in_array($ext, [ "jpg", "jpeg", "png", "webp"])){
          $filepath_to = $filepath;
          if(strpos($filepath_to, ".webp") === false){
              $filepath_to .= '.webp';
          }
          return self::convert_webp($filepath, $filepath_to); 
      }
      
      $quality      = apply_filters( 'next3_webp_quality', 100 );
      $quality_type = intval( apply_filters( 'next3_webp_quality_type', 0 ) );
  
      switch ( $type ) {
        case IMAGETYPE_GIF:
          $quality_type = 1 !== $quality_type ? '' : '-lossy';
          $placeholder  = 'gif2webp -q %1$s %2$s %3$s -o %3$s.webp 2>&1';
          break;
  
        case IMAGETYPE_JPEG:
          $quality_type = 1 !== $quality_type ? '' : '-lossless';
          $placeholder  = 'cwebp -q %1$s %2$s %3$s -o %3$s.webp 2>&1';
          break;
		
        case IMAGETYPE_PNG:
          if ( filesize( $filepath ) > self::PNGS_SIZE_LIMIT ) {
            return true;
          }
          $quality_type = 1 !== $quality_type ? '' : '-lossless';
          $placeholder  = 'cwebp -q %1$s %2$s %3$s -o %3$s.webp 2>&1';
          break;
  
        default:
          return true;
      }
      
      // Optimize the image.
      if( function_exists('exec')){
        exec(
          sprintf(
            $placeholder, // The command.
            $quality, // The quality %.
            $quality_type, // The quality type -lossless or -lossy.
            $filepath // Image path.
          ),
          $output,
          $status
        );
      }
     
      if( !function_exists('exec') || true === boolval( $status )){
        
        $filepath_to = $filepath;
        if(strpos($filepath_to, ".webp") === false){
            $filepath_to .= '.webp';
        }
        $status = self::convert_webp($filepath, $filepath_to); 
      }
      return $status;
    }
    
    public static function convert_webp($from, $to, $quality = null){
      $extf = strtolower(pathinfo($from, PATHINFO_EXTENSION));
      $extt = strtolower(pathinfo($to, PATHINFO_EXTENSION));
      $valid = ["bmp", "jpg", "jpeg", "png", "webp"];
     
      if (!in_array($extf, $valid) || !in_array($extt, $valid)) { 
          return true; 
      }
      
      if (in_array($extf, ['jpg', 'jpeg']) ) { $extf = "jpeg"; }
      if (in_array($extt, ['jpg', 'jpeg']) ) { $extt = "jpeg"; }
      
      $fnf = "imagecreatefrom$extf";
      $fnt = "image$extt";

      // check png
      $isAlpha = false;
      $info = getimagesize($from);
      
      // rename file
      //rename($from, $to);
       
      if( function_exists($fnf) && function_exists($fnt)){
        // new code
        if ($info['mime'] == 'image/jpeg'){
          $img = imagecreatefromjpeg($from);
        } else if ($isAlpha = $info['mime'] == 'image/gif') {
          $img = imagecreatefromgif($from);
        } else if ($isAlpha = $info['mime'] == 'image/png') {
          $img = imagecreatefrompng($from);
        } else {
          $img = @$fnf($from);
        }
       
        if( $img ){

          if ($isAlpha) {
            imagepalettetotruecolor($img);
            imagealphablending($img, true);
            imagesavealpha($img, true);
          } else{
            imagepalettetotruecolor($img);
          }
          
          if ($quality !== null) {
              $fnt($img, $to, $quality); 
          } else { 
              $fnt($img, $to); 
          }
          if( imagedestroy($img) ){
            @unlink($from);
          }
          return "0";
        } 
      }
      return true;
    }

    public function reset_image_optimization_status() {
      global $wpdb;
  
      $wpdb->query(
        "
          DELETE FROM $wpdb->postmeta
          WHERE `meta_key` = '" . $this->batch_skipped . "'
        "
      );
    }
  
    public static function get_uploads_dir() {
      // Get the uploads dir.
      $upload_dir = wp_upload_dir();
  
      $base_dir = $upload_dir['basedir'];
  
      if ( defined( 'UPLOADS' ) ) {
        $base_dir = ABSPATH . UPLOADS;
      }
  
      return $base_dir;
    }

    public static function instance(){
        if (!self::$instance){
            self::$instance = new self();
        }
        return self::$instance;
    }
}