package com.fuse.mediapicker;

import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.util.Log;
import androidx.annotation.Nullable;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;

class ImageResizer {
	private final File externalFilesDirectory;
	private final ExifDataCopier exifDataCopier;

	ImageResizer(File externalFilesDirectory, ExifDataCopier exifDataCopier) {
		this.externalFilesDirectory = externalFilesDirectory;
		this.exifDataCopier = exifDataCopier;
	}

	/**
	 * If necessary, resizes the image located in imagePath and then returns the path for the scaled
	 * image.
	 *
	 * <p>If no resizing is needed, returns the path for the original image.
	 */
	String resizeImageIfNeeded(
			String imagePath,
			@Nullable Double maxWidth,
			@Nullable Double maxHeight,
			@Nullable Integer imageQuality) {
		Bitmap bmp = decodeFile(imagePath);
		if (bmp == null) {
			return null;
		}
		boolean shouldScale =
				maxWidth != null || maxHeight != null || isImageQualityValid(imageQuality);
		if (!shouldScale) {
			return imagePath;
		}
		try {
			String[] pathParts = imagePath.split("/");
			String imageName = pathParts[pathParts.length - 1];
			File file = resizedImage(bmp, maxWidth, maxHeight, imageQuality, imageName);
			copyExif(imagePath, file.getPath());
			return file.getPath();
		} catch (IOException e) {
			throw new RuntimeException(e);
		}
	}

	private File resizedImage(
			Bitmap bmp, Double maxWidth, Double maxHeight, Integer imageQuality, String outputImageName)
			throws IOException {
		double originalWidth = bmp.getWidth() * 1.0;
		double originalHeight = bmp.getHeight() * 1.0;

		if (!isImageQualityValid(imageQuality)) {
			imageQuality = 100;
		}

		boolean hasMaxWidth = maxWidth != null;
		boolean hasMaxHeight = maxHeight != null;

		Double width = hasMaxWidth ? Math.min(originalWidth, maxWidth) : originalWidth;
		Double height = hasMaxHeight ? Math.min(originalHeight, maxHeight) : originalHeight;

		boolean shouldDownscaleWidth = hasMaxWidth && maxWidth < originalWidth;
		boolean shouldDownscaleHeight = hasMaxHeight && maxHeight < originalHeight;
		boolean shouldDownscale = shouldDownscaleWidth || shouldDownscaleHeight;

		if (shouldDownscale) {
			double downscaledWidth = (height / originalHeight) * originalWidth;
			double downscaledHeight = (width / originalWidth) * originalHeight;

			if (width < height) {
				if (!hasMaxWidth) {
					width = downscaledWidth;
				} else {
					height = downscaledHeight;
				}
			} else if (height < width) {
				if (!hasMaxHeight) {
					height = downscaledHeight;
				} else {
					width = downscaledWidth;
				}
			} else {
				if (originalWidth < originalHeight) {
					width = downscaledWidth;
				} else if (originalHeight < originalWidth) {
					height = downscaledHeight;
				}
			}
		}

		Bitmap scaledBmp = createScaledBitmap(bmp, width.intValue(), height.intValue(), false);
		File file =
				createImageOnExternalDirectory("/scaled_" + outputImageName, scaledBmp, imageQuality);
		return file;
	}

	private File createFile(File externalFilesDirectory, String child) {
		File image = new File(externalFilesDirectory, child);
		if (!image.getParentFile().exists()) {
			image.getParentFile().mkdirs();
		}
		return image;
	}

	private FileOutputStream createOutputStream(File imageFile) throws IOException {
		return new FileOutputStream(imageFile);
	}

	private void copyExif(String filePathOri, String filePathDest) {
		exifDataCopier.copyExif(filePathOri, filePathDest);
	}

	private Bitmap decodeFile(String path) {
		return BitmapFactory.decodeFile(path);
	}

	private Bitmap createScaledBitmap(Bitmap bmp, int width, int height, boolean filter) {
		return Bitmap.createScaledBitmap(bmp, width, height, filter);
	}

	private boolean isImageQualityValid(Integer imageQuality) {
		return imageQuality != null && imageQuality > 0 && imageQuality < 100;
	}

	private File createImageOnExternalDirectory(String name, Bitmap bitmap, int imageQuality)
			throws IOException {
		ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
		boolean saveAsPNG = bitmap.hasAlpha();
		if (saveAsPNG) {
			Log.d(
					"ImageResizer",
					"media_picker: compressing is not supported for type PNG. Returning the image with original quality");
		}
		bitmap.compress(
				saveAsPNG ? Bitmap.CompressFormat.PNG : Bitmap.CompressFormat.JPEG,
				imageQuality,
				outputStream);
		File imageFile = createFile(externalFilesDirectory, name);
		FileOutputStream fileOutput = createOutputStream(imageFile);
		fileOutput.write(outputStream.toByteArray());
		fileOutput.close();
		return imageFile;
	}
}
