/*
 * Thumbnailator - a thumbnail generation library
 *
 * Copyright (c) 2008-2023 Chris Kroells
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to deal
 * in the Software without restriction, including without limitation the rights
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 * copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in
 * all copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
 * THE SOFTWARE.
 */

package net.coobird.thumbnailator.test;

import java.awt.image.BufferedImage;
import java.awt.image.Raster;

public final class BufferedImageComparer {

	public static final boolean isSame(BufferedImage img0, BufferedImage img1) {
		int width0 = img0.getWidth();
		int height0 = img0.getHeight();
		int width1 = img1.getWidth();
		int height1 = img1.getHeight();

		if (width0 != width1 || height0 != height1) {
			throw new AssertionError("Width and/or height do not match.");
		}
		
		if (!img0.getColorModel().equals(img1.getColorModel())) {
			String message = "Color models do not match. [%s, %s]";
			message = String.format(message, img0.getColorModel(), img1.getColorModel());
			throw new AssertionError(message);
		}
		
		if (!img0.getSampleModel().equals(img1.getSampleModel())) {
			String message = "Sample models do not match. [%s, %s]";
			message = String.format(message, img0.getSampleModel(), img1.getSampleModel());
			throw new AssertionError(message);
		}
		
		if (img0.getType() != img1.getType()) {
			String message = "Image types do not match. [%d, %d]";
			message = String.format(message, img0.getType(), img1.getType());
			throw new AssertionError(message);
		}
		
		/*
		 * Check the RGB data.
		 */
		for (int i = 0; i < width0; i++) {
			for (int j = 0; j < height0; j++) {
				if (img0.getRGB(i, j) != img1.getRGB(i, j)) {
					String message = "Pixels do not match. location: (%d, %d), rgb: (%d, %d)";
					message = String.format(message, i, j, img0.getRGB(i, j), img1.getRGB(i, j));
					throw new AssertionError(message);
				}
			}
		}
		
		/*
		 * Check the raw data.
		 */
		Raster d0 = img0.getData();
		Raster d1 = img1.getData();
		
		if (d0.getNumBands() != d1.getNumBands()) {
			String message = "Number of bands do not match. [%d, %d]";
			message = String.format(message, d0.getNumBands(), d1.getNumBands());
			throw new AssertionError(message);
		}
		
		for (int i = 0; i < width0; i++) {
			for (int j = 0; j < height0; j++) {
				int[] i0 = new int[d0.getNumBands()];
				int[] i1 = new int[d1.getNumBands()];
				
				d0.getPixel(i, j, i0);
				d1.getPixel(i, j, i1);
				
				for (int k = 0; k < d0.getNumBands(); k++) {
					if (i0[k] != i1[k]) {
						String message = "Pixels do not match. rgb: (%d, %d), band: %d";
						message = String.format(message, i, j, k);
						throw new AssertionError(message);
					}
				}
			}
		}
		
		return true;
	}
	
	// Checks if the pixels are similar.
	public static final boolean isRGBSimilar(BufferedImage img0, BufferedImage img1) {
		int width0 = img0.getWidth();
		int height0 = img0.getHeight();
		int width1 = img1.getWidth();
		int height1 = img1.getHeight();
		
		if (width0 != width1 || height0 != height1) {
			throw new AssertionError("Width and/or height do not match.");
		}
		
		/*
		 * Check the RGB data.
		 */
		for (int i = 0; i < width0; i++) {
			for (int j = 0; j < height0; j++) {
				int v0 = img0.getRGB(i, j);
				int v1 = img1.getRGB(i, j);
				
				int r0 = (v0 << 8) >>> 24;
				int r1 = (v1 << 8) >>> 24;
				int g0 = (v0 << 16) >>> 24;
				int g1 = (v1 << 16) >>> 24;
				int b0 = (v0 << 24) >>> 24;
				int b1 = (v1 << 24) >>> 24;
				
				if (r0 != r1 || g0 != g1 || b0 != b1) {
					String message = "Pixels do not match. location: (%d, %d), rgb: ([%d, %d, %d], [%d, %d, %d])";
					message = String.format(message, i, j, r0, g0, b0, r1, g1, b1);
					throw new AssertionError(message);
				}
			}
		}
		
		return true;
	}

	// Checks if the pixels are similar.
	public static final boolean isRGBSimilar(BufferedImage img0, BufferedImage img1, int maxError) {
		int width0 = img0.getWidth();
		int height0 = img0.getHeight();
		int width1 = img1.getWidth();
		int height1 = img1.getHeight();

		if (width0 != width1 || height0 != height1) {
			throw new AssertionError("Width and/or height do not match.");
		}

		/*
		 * Check the RGB data.
		 */
		for (int i = 0; i < width0; i++) {
			for (int j = 0; j < height0; j++) {
				int v0 = img0.getRGB(i, j);
				int v1 = img1.getRGB(i, j);

				int r0 = (v0 << 8) >>> 24;
				int r1 = (v1 << 8) >>> 24;
				int g0 = (v0 << 16) >>> 24;
				int g1 = (v1 << 16) >>> 24;
				int b0 = (v0 << 24) >>> 24;
				int b1 = (v1 << 24) >>> 24;

				int rError = Math.abs(r1 - r0);
				int gError = Math.abs(g1 - g0);
				int bError = Math.abs(b1 - b0);

				if (rError > maxError || gError > maxError || bError > maxError) {
					String message = String.format(
							"Pixels errors too large. location: (%d, %d), rgb: ([%d, %d, %d], [%d, %d, %d])",
							i, j, r0, g0, b0, r1, g1, b1
					);
					throw new AssertionError(message);
				}
			}
		}

		return true;
	}
}
