<?php
/**
 * Admin ajax functions to be tested
 */
require_once( ABSPATH . 'wp-admin/includes/ajax-actions.php' );

/**
 * Testing `_wp_attachment_backup_sizes` meta for ajax media editing
 *
 * Does NOT test that the edits made are correct
 * ONLY tests the creation/modification of the `_wp_attachment_backup_sizes` meta
 *
 * @package    WordPress
 * @subpackage UnitTests
 * @since      x.y.z
 * @group      ajax
 *
 * @todo I've tagged all these tests with {@ticket 44127} because I decided they
 * 		 were needed while I was working on that ticket.  However, I'm now thinking
 * 		 I should open a new ticket specifically stating the need for these tests,
 * 		 independent of that ticket.  Opinions?
 * @todo Are tests needed for flip & rotate as well as crop?  I don't think so
 * 		 because I don't think the actual edit operation affects what is
 * 		 stored in _wp_attachment_backup_sizes, but need confirmation of that.
 * @todo As far as I can tell, the tests that depend on IMAGE_EDIT_OVERWRITE
 * 		 must be at the end of the file.  Is there any way to avoid this?
 */
class Tests_Ajax_MediaEditBackupSizes extends WP_Ajax_UnitTestCase {
	/**
	 * The attachment ID
	 *
	 * @var int
	 */
	public $attachment_id;

	/**
	 * Uploaded file bits.
	 *
	 * Keys are filenames and values are the result of calling `wp_upload_bits()` for those files.
	 *
	 * @var array
	 */
	public $uploaded_bits = array();

	/**
	 * Setup the test fixture.
	 */
	public function setUp() {
		parent::setUp();

		require_once( ABSPATH . 'wp-admin/includes/image-edit.php' );
	}

	/**
	 * Tear down the test fixture.
	 */
	public function tearDown() {
		// Cleanup
		$this->remove_added_uploads();
		parent::tearDown();
	}

	/**
	 * Upload an image.
	 *
	 * @param string $file
	 * @return int
	 */
	public function _insert_attachment( $file ) {
		if ( ! isset( $this->uploaded_bits[ $file ] ) ) {
			// only upload a given file once, regardless of how many tests it's used in
			$filename = DIR_TESTDATA . '/images/' . $file;
			$contents = file_get_contents( $filename );

			$this->uploaded_bits[ $file ] = wp_upload_bits( basename( $filename ), null, $contents );
		}

		$this->attachment_id = $this->_make_attachment( $this->uploaded_bits[ $file ] );
	}

	/**
	 * Setup the `$_REQUEST` array for wp_save_image()
	 *
	 * @param array $args
	 */
	public function _setUpRequest( $args ) {
		$defaults = array(
			'action' => 'image-editor',
			'context' => 'edit-attachment',
			'postid' => $this->attachment_id,
		);

		$args = wp_parse_args( $args, $defaults );
		foreach ( $args as $arg => $value ) {
			$_REQUEST[$arg] = $value;
		}
	}

	/**
	 * @ticket 44127
	 */
	public function testScaleImage() {
		if ( defined( 'IMAGE_EDIT_OVERWRITE' ) && IMAGE_EDIT_OVERWRITE ) {
			// skip test
			$this->markTestSkipped( 'testScaleImage() skipped because IMAGE_EDIT_OVERWRITE is defined and is true' );
		}

		$file = '33772.jpg';
		$this->_insert_attachment( $file );

		// setup the expected backup sizes
		$orig_metadata = wp_get_attachment_metadata( $this->attachment_id );
 		$expected = array(
 			'full-orig' => array(
 				'width' => $orig_metadata['width'],
 				'height' => $orig_metadata['height'],
 				'file' => $file,
 			),
 		);

		$this->_setUpRequest( array( 'do' => 'scale', 'fwidth' => '1200', 'fheight' => '675' ) );
		$ret = wp_save_image( $this->attachment_id );

 		$actual = get_post_meta( $this->attachment_id, '_wp_attachment_backup_sizes', true );
 		$this->assertEquals( $expected, $actual );
	}

	/**
	 * @ticket 44127
	 */
	public function testMultipleScaleImage() {
		if ( defined( 'IMAGE_EDIT_OVERWRITE' ) && IMAGE_EDIT_OVERWRITE ) {
			// skip test
			$this->markTestSkipped( 'testMultipleScaleImage() skipped because IMAGE_EDIT_OVERWRITE is defined and is true' );
		}

		$file = '33772.jpg';
		$this->_insert_attachment( $file );

		// setup the initial expected backup sizes, we'll append to this
		// each time thru the loop below
		$orig_metadata = wp_get_attachment_metadata( $this->attachment_id );
		$expected = array(
			'full-orig' => array(
				'width' => $orig_metadata['width'],
				'height' => $orig_metadata['height'],
				'file' => $file,
			),
		);

		// save basename for the suffix hash
		$basename = basename( $file, '.jpg' ) . '-e';

		$scaled_sizes = array(
			array( 'width' => '1200', 'height' => '675' ),
			array( 'width' => '800', 'height' => '450' ),
			array( 'width' => '400', 'height' => '225' ),
			array( 'width' => '300', 'height' => '169' ),// same size as 'medium' size
		);

		// loop thru all the scaled_sizes and scale the image to each
		foreach ( $scaled_sizes as $idx => $size ) {
			$this->_setUpRequest( array( 'do' => 'scale', 'fwidth' => $size['width'], 'fheight' => $size['height'] ) );
			$ret = wp_save_image( $this->attachment_id );

			$metadata = wp_get_attachment_metadata( $this->attachment_id );

			if ( 0 !== $idx ) {
				// get the filename hash
				$suffix = str_replace( $basename, '', basename( $metadata['file'], '.jpg' ) );
				// append to the expected backup size
				$expected['full-' . $suffix] = array(
					'width' => $previous_size['width'],
					'height' => $previous_size['height'],
					'file' => $previous_filename,
				);
			}

			// save these for the expected value the next time through the loop
			$previous_filename = basename( $metadata['file'] );
			$previous_size = $size;
		}

		$actual = get_post_meta( $this->attachment_id, '_wp_attachment_backup_sizes', true );
		$this->assertEquals( $expected, $actual );
	}

	/**
	 * @ticket 44127
	 */
	public function testCropImageAll() {
		$file = '33772.jpg';
		$this->_insert_attachment( $file );

		// setup the expected backup sizes
		$orig_metadata = wp_get_attachment_metadata( $this->attachment_id );
		$expected = array(
			'full-orig' => array(
				'width' => $orig_metadata['width'],
				'height' => $orig_metadata['height'],
				'file' => $file,
			),
		);
		foreach ( $orig_metadata['sizes'] as $size => $data ) {
			$expected[$size . '-orig'] = $data;
		}

		$this->_setUpRequest( array( 'do' => 'save', 'target' => 'all', 'history' => '[{\"c\":{\"x\":35,\"y\":23,\"w\":345,\"h\":186}}]' ) );
		$ret = wp_save_image( $this->attachment_id );

		$actual = get_post_meta( $this->attachment_id, '_wp_attachment_backup_sizes', true );
		$this->assertEquals( $expected, $actual );
	}

	/**
	 * @ticket 44127
	 */
	public function testCropImageThumbnail() {
		$file = '33772.jpg';
		$this->_insert_attachment( $file );

		// setup the expected backup sizes
		$orig_metadata = wp_get_attachment_metadata( $this->attachment_id );
		$expected = array(
			'thumbnail-orig' => $orig_metadata['sizes']['thumbnail'],
		);

		$this->_setUpRequest( array( 'do' => 'save', 'target' => 'thumbnail', 'history' => '[{\"c\":{\"x\":35,\"y\":23,\"w\":345,\"h\":186}}]' ) );
		$ret = wp_save_image( $this->attachment_id );

		$actual = get_post_meta( $this->attachment_id, '_wp_attachment_backup_sizes', true );
		$this->assertEquals( $expected, $actual );
	}

	/**
	 * @ticket 44127
	 */
	public function testCropImageNoThumbnail() {
		$file = '33772.jpg';
		$this->_insert_attachment( $file );

		// setup the expected backup sizes
		$orig_metadata = wp_get_attachment_metadata( $this->attachment_id );
		$expected = array(
			'full-orig' => array(
				'width' => $orig_metadata['width'],
				'height' => $orig_metadata['height'],
				'file' => $file,
			),
		);
		foreach ( $orig_metadata['sizes'] as $size => $data ) {
			if ( 'thumbnail' !== $size ) {
				$expected[$size . '-orig'] = $data;
			}
		}

		$this->_setUpRequest( array( 'do' => 'save', 'target' => 'nothumb', 'history' => '[{\"c\":{\"x\":35,\"y\":23,\"w\":345,\"h\":186}}]' ) );
		$ret = wp_save_image( $this->attachment_id );

		$actual = get_post_meta( $this->attachment_id, '_wp_attachment_backup_sizes', true );
		$this->assertEquals( $expected, $actual );
	}


	/**
	 * @ticket 44127
	 */
	public function testScaleImageImageEditOverwrite() {
		if ( ! defined( 'IMAGE_EDIT_OVERWRITE' ) || ! IMAGE_EDIT_OVERWRITE ) {
			// skip test
			$this->markTestSkipped( 'testScaleImageImageEditOverwrite() skipped because IMAGE_EDIT_OVERWRITE is not defined or is false' );
		}

		$file = '33772.jpg';
		$this->_insert_attachment( $file );

		// setup the expected backup sizes
		$orig_metadata = wp_get_attachment_metadata( $this->attachment_id );
 		$expected = array(
 			'full-orig' => array(
 				'width' => $orig_metadata['width'],
 				'height' => $orig_metadata['height'],
 				'file' => $file,
 			),
 		);
		foreach ( $orig_metadata['sizes'] as $size => $data ) {
			// @todo why isn't thumbnail-orig added to the backup sizes when IMAGE_EDIT_OVERWRITE is true?
			//		 is that a bug in wp_save_image()?
			//		 Note: this is NOT the same as skipping 'thumbnail-orig in testCropImageNoThumbnail(),
			//			   which is expected
			if ( 'thumbnail' !== $size ) {
				$expected[$size . '-orig'] = $data;
			}
		}

		$this->_setUpRequest( array( 'do' => 'scale', 'fwidth' => '1200', 'fheight' => '675' ) );
		$ret = wp_save_image( $this->attachment_id );

 		$actual = get_post_meta( $this->attachment_id, '_wp_attachment_backup_sizes', true );
 		$this->assertEquals( $expected, $actual );
	}

	/**
	 * @ticket 44127
	 */
	public function testMultipleScaleImageImageEditOverwrite() {
		if ( ! defined( 'IMAGE_EDIT_OVERWRITE' ) || ! IMAGE_EDIT_OVERWRITE ) {
			// skip test
			$this->markTestSkipped( 'testMultipleScaleImageImageEditOverwrite() skipped because IMAGE_EDIT_OVERWRITE is not defined or is false' );
		}

		$file = '33772.jpg';
		$this->_insert_attachment( $file );

		// setup the expected backup sizes
		$orig_metadata = wp_get_attachment_metadata( $this->attachment_id );
		$expected = array(
			'full-orig' => array(
				'width' => $orig_metadata['width'],
				'height' => $orig_metadata['height'],
				'file' => $file,
			),
		);
		foreach ( $orig_metadata['sizes'] as $size => $data ) {
			// @todo why isn't thumbnail-orig added to the backup sizes when IMAGE_EDIT_OVERWRITE is true?
			//		 is that a bug in wp_save_image()?
			//		 Note: this is NOT the same as skipping 'thumbnail-orig in testCropImageNoThumbnail(),
			//			   which is expected
			if ( 'thumbnail' !== $size ) {
				$expected[$size . '-orig'] = $data;
			}
		}

		// save basename for the suffix hash
		$basename = basename( $file, '.jpg' ) . '-e';

		$scaled_sizes = array(
			array( 'width' => '1200', 'height' => '675' ),
			array( 'width' => '800', 'height' => '450' ),
			array( 'width' => '400', 'height' => '225' ),
			array( 'width' => '300', 'height' => '169' ),// same size as 'medium' size
		);

		// loop thru all the scaled_sizes and scale the image to each
		foreach ( $scaled_sizes as $idx => $size ) {
			$this->_setUpRequest( array( 'do' => 'scale', 'fwidth' => $size['width'], 'fheight' => $size['height'] ) );
			$ret = wp_save_image( $this->attachment_id );

			$metadata = wp_get_attachment_metadata( $this->attachment_id );
		}

		$actual = get_post_meta( $this->attachment_id, '_wp_attachment_backup_sizes', true );
		$this->assertEquals( $expected, $actual );
	}
}
