use core::ffi::CStr;
use std::ffi::CString;

use ndk::android::asset::*;

#[repr(transparent)]
#[derive(Debug, Clone, Copy)]
pub struct Assets {
	inner: *const AAssetManager,
}
impl Assets {
	pub fn as_sys(&self) -> *mut AAssetManager {
		self.inner as _
	}
	pub fn from_java(env: *mut jni::JNIEnv, asset_manager: jni::jobject) -> Self {
		let inner = unsafe { AAssetManager_fromJava(env, asset_manager) };
		Self { inner }
	}
	pub fn read_dir(&self, dir_name: &CStr, file_list: &mut Vec<CString>) -> Option<()> {
		let dir = unsafe { AAssetManager_openDir(self.inner as _, dir_name.as_ptr()) };
		let dir = Dir::from_sys(dir)?;
		dir.read(file_list);
		Some(())
	}
	pub fn read(&self, file_name: &CStr, bytes: &mut Vec<u8>) -> Result<(), &'static str> {
		debug_assert!(!self.inner.is_null());
		let file = unsafe { AAssetManager_open(self.inner as _, file_name.as_ptr(), AASSET_MODE_BUFFER as _) };
		let file = File::from_sys(file)?;
		file.read(bytes)?;
		core::mem::forget(file);
		Ok(())
	}
}
struct Dir(*mut AAssetDir);
impl Drop for Dir {
	fn drop(&mut self) {
		unsafe { AAssetDir_close(self.0) };
	}
}
impl Dir {
	pub fn from_sys(dir: *mut AAssetDir) -> Option<Self> {
		if !dir.is_null() {
			Some(Self(dir))
		} else {
			None
		}
	}
	pub fn read(&self, file_list: &mut Vec<CString>) {
		loop {
			let file = unsafe { AAssetDir_getNextFileName(self.0) };
			if !file.is_null() {
				let file = unsafe { CStr::from_ptr(file) };
				file_list.push(file.to_owned());
				continue;
			}
			break;
		}
	}
}
struct File(*mut AAsset);
impl Drop for File {
	fn drop(&mut self) {
		unsafe { AAsset_close(self.0) };
	}
}
impl File {
	pub fn from_sys(file: *mut AAsset) -> Result<Self, &'static str> {
		if !file.is_null() {
			Ok(Self(file))
		} else {
			Err("AAsset File handle Null")
		}
	}
	pub fn read(&self, bytes: &mut Vec<u8>) -> Result<(), &'static str> {
		let len = unsafe { AAsset_getLength64(self.0) } as usize;
		let data = unsafe { AAsset_getBuffer(self.0) } as *const u8;
		if data.is_null() {
			return Err("AAsset_getBuffer failed");
		}
		let src = unsafe { core::slice::from_raw_parts(data, len) };
		bytes.reserve(len);
		unsafe { bytes.set_len(len) };
		bytes.copy_from_slice(src);
		Ok(())
	}
}
