//! Shapes file bitmap type.

use crate::{err::*, image::Image};
use super::clut;

/// Reads a `Bitmap`.
pub fn read(b: &[u8]) -> ResultS<Bitmap>
{
	read_data! {
		endian: BIG, buf: b, size: 26, start: 0, data {
			let width  = u16[0] usize;
			let height = u16[2] usize;
			let compr  = u16[4];
			let flags  = u16[6] flag BmpFlags;
			let depth  = u16[8];
		}
	}

	let compr = compr == u16::max_value();
	let alpha = flags.contains(BmpFlags::TRANSPARENT);
	let cmajr = flags.contains(BmpFlags::COLUMN_MAJOR);

	if depth != 8 {
		bail!("invalid bit depth (should always be 8)");
	}

	let mut bmp = Bitmap::new(width, height, alpha, cmajr);
	let mut p   = 30 + if cmajr {4 * width} else {4 * height};

	let scanlines = if cmajr {width} else {height};
	let pitch     = if cmajr {height} else {width};

	if compr {
		// compressed scanlines (transparency RLE)
		for _ in 0..scanlines {
			read_data! {
				endian: BIG, buf: b, size: 4, start: p, data {
					let fst = u16[0] usize;
					let lst = u16[2] usize;
				}
			}

			let end = lst - fst;

			p += 4;

			if lst < fst || fst > pitch || lst > pitch {
				bail!("invalid compressed scanline");
			}

			for _ in 0..fst {
				bmp.cr.push(0);
			}

			bmp.cr.extend(ok!(b.get(p..p + end), "not enough data")?);

			for _ in lst..pitch {
				bmp.cr.push(0);
			}

			p += end;
		}
	} else {
		// simple copy
		bmp.cr.extend(ok!(b.get(p..p + width * height), "not enough data")?);
	}

	Ok(bmp)
}

impl Bitmap
{
	/// Creates an empty bitmap.
	pub fn new(w: usize, h: usize, alpha: bool, cmajr: bool) -> Self
	{
		Self{w, h, alpha, cmajr, cr: Vec::with_capacity(w * h)}
	}
}

impl<'a, 'b> ImageShp<'a, 'b>
{
	/// Creates an `ImageShp` with the given bitmap.
	#[inline]
	pub const fn new(bmp: &'a Bitmap, clut: &'b [clut::ColorShp]) -> Self
	{
		Self{bmp, clut}
	}
}

impl Image for ImageShp<'_, '_>
{
	type Output = clut::ColorShp;

	fn w(&self) -> usize {self.bmp.w}
	fn h(&self) -> usize {self.bmp.h}

	fn index(&self, x: usize, y: usize) -> &Self::Output
	{
		static TRANSLUCENT_COLOR: clut::ColorShp = clut::ColorShp::Translucent;

		let cr = usize::from(if self.bmp.cmajr {
			self.bmp.cr[y + x * self.bmp.h]
		} else {
			self.bmp.cr[x + y * self.bmp.w]
		});

		if self.bmp.alpha && cr == 0 {
			&TRANSLUCENT_COLOR
		} else {
			self.clut.get(cr).unwrap_or(&TRANSLUCENT_COLOR)
		}
	}
}

/// An unpacked Shape bitmap.
#[derive(Clone, Debug, Eq, PartialEq)]
pub struct Bitmap {
	w:     usize,
	h:     usize,
	cr:    Vec<u8>,
	alpha: bool,
	cmajr: bool,
}

/// An image from a Shape. This mainly just exists so that `Bitmap` can use the
/// `Image` trait.
#[derive(Debug, Eq, PartialEq)]
pub struct ImageShp<'a, 'b> {
	bmp:  &'a Bitmap,
	clut: &'b [clut::ColorShp],
}

c_bitfield! {
	pub struct BmpFlags: u16 {
		TRANSPARENT  = 14,
		COLUMN_MAJOR = 15,
	}
}

// EOF
