// Copyright Marcus Del Favero 2025
// Licensed under the GNU AGPLv3 with an exception, see `README.md` for details
use std::fs::File;
use std::io::{BufReader, BufWriter};

use png::{Encoder, Decoder, ColorType, BitDepth};

pub struct Image {
	pub width: usize,
	pub height: usize,
	pub pixels: Box<[u8]>,
}

impl Image {
	pub fn new(width: usize, height: usize, channels: usize) -> Image {
		Image {
			width, height,
			pixels: vec![0; width * height * channels].into_boxed_slice(),
		}
	}

	// Did some copy-pasting from a previous project which was copy-pasted from the documentation, each time some modifications made
	pub fn load(path: &str, format: ColorType) -> Image {
		let decoder = Decoder::new(BufReader::new(File::open(path).unwrap()));

		let mut reader = decoder.read_info().unwrap();
		let mut pixels = vec![0; reader.output_buffer_size().unwrap()];
		let info = reader.next_frame(&mut pixels).unwrap();
		pixels.truncate(info.buffer_size());

		assert!(info.bit_depth == BitDepth::Eight);
		assert!(info.color_type == format);

		Image {
			width: info.width as usize,
			height: info.height as usize,
			pixels: pixels.into_boxed_slice(),
		}
	}

	pub fn save(&self, path: &str, colour_type: ColorType) {
		let mut encoder = Encoder::new(BufWriter::new(File::create(path).unwrap()), self.width.try_into().unwrap(), self.height.try_into().unwrap());
		encoder.set_color(colour_type);
		encoder.set_depth(BitDepth::Eight);
		encoder.write_header().unwrap().write_image_data(&self.pixels).unwrap();
	}

	pub fn flip_vertical(mut self, colour_type: ColorType) -> Image {
		let channels = match colour_type {
			ColorType::Grayscale => 1,
			ColorType::Rgba => 4,
			_ => unimplemented!(),
		};

		for y in 0..self.height / 2 {
			for x in 0..self.width {
				for ch in 0..channels {
					let flipped_y = self.height - 1 - y;
					self.pixels.swap((x + y * self.width) * channels + ch, (x + flipped_y * self.width) * channels + ch);
				}
			}
		}
		self
	}
}
