use crate::{lazy::*, lib::*, prefetch::*, FS, GL};
use GL::{atlas, tex::*, uImage};

#[derive(Debug)]
pub struct VTex2d<S, F> {
	pub region: Vec4,
	pub tex: Rc<Tex2d<S, F>>,
}
impl<S, F> PartialEq for VTex2d<S, F> {
	fn eq(&self, r: &Self) -> bool {
		let &Self { region, ref tex } = self;
		Rc::ptr_eq(tex, &r.tex) && region == r.region
	}
}
impl<S, F> Eq for VTex2d<S, F> {}
impl<S, F> Clone for VTex2d<S, F> {
	fn clone(&self) -> Self {
		let &Self { region, ref tex } = self;
		let (region, tex) = (region, tex.clone());
		Self { region, tex }
	}
}

pub type VTex2dEntry<'a, S> = Prefetched<'a, u32, VTex2d<S, u8>, TexAtlas<S>>;

pub struct TexAtlas<S> {
	t: UnsafeCell<State<S>>,
}
impl<S: TexSize> TexAtlas<S> {
	pub fn new() -> Self {
		Def()
	}
	pub fn load(&self, name: &str) -> VTex2dEntry<S> {
		match unsafe { &mut *self.t.get() } {
			Baked(_) => ERROR!("Trying to load into finalized atals"),
			Fresh(reqs) => {
				let k = u32(reqs.len());
				let name: Astr = format!("res/{name}").into();
				reqs.push((name.clone(), Lazy::new(FS::Lazy::File(name))));
				Prefetched::new(k, self)
			}
		}
	}
	fn finalise(state: &mut State<S>) -> &mut VTexMap<S> {
		let mut s = Fresh(Def());
		mem::swap(&mut s, state);

		let t = match s {
			Baked(_) => unreachable!(),
			Fresh(mut reqs) => {
				let reqs = reqs
					.iter_mut()
					.enumerate()
					.map(|(n, (name, r))| (u32(n), EXPECT!(uImage::<S>::load(r.get()).map_err(|e| format!("{e}, image {name:?}")))))
					.collect_vec();
				let max_side = GL::MAX_TEXTURE_SIZE();
				let (atlas, mut tail) = atlas::pack_into_atlas(reqs, max_side, max_side);
				let mut textures: VTexMap<S> = atlas.into_iter().collect();
				while !tail.is_empty() {
					let last_l = tail.len();
					let (a, t) = atlas::pack_into_atlas(tail, max_side, max_side);
					if last_l == t.len() {
						ERROR!("Graphics card can't fit textures: {t:?}");
					}
					textures.extend(a.into_iter());
					tail = t;
				}
				textures
			}
		};

		*state = Baked(t);
		match state {
			Fresh(_) => unreachable!(),
			Baked(t) => t,
		}
	}
}
impl<S: TexSize> Default for TexAtlas<S> {
	fn default() -> Self {
		Self { t: UnsafeCell::new(Fresh(Def())) }
	}
}

impl<S: TexSize> Fetcher<u32, VTex2d<S, u8>> for TexAtlas<S> {
	fn get(&self, k: u32) -> &VTex2d<S, u8> {
		let s = unsafe { &mut *self.t.get() };
		let textures = match s {
			Fresh(_) => Self::finalise(s),
			Baked(t) => t,
		};
		textures.get(&k).valid()
	}
	fn take(&self, k: u32) -> VTex2d<S, u8> {
		let s = unsafe { &mut *self.t.get() };
		let textures = match s {
			Fresh(_) => Self::finalise(s),
			Baked(t) => t,
		};
		textures.remove(&k).take().valid()
	}
}

enum State<S> {
	Fresh(Vec<(Astr, Lazy<Vec<u8>>)>),
	Baked(VTexMap<S>),
}
use State::*;
type VTexMap<S> = HashMap<u32, VTex2d<S, u8>>;
