use nalgebra::Matrix4;
use anyhow::bail;
use crate::data_types::intrinsic_info::IntrinsicInfo;
use opencv::prelude::*;
use opencv::objdetect::{ArucoDetector, PredefinedDictionaryType, get_predefined_dictionary, DetectorParameters, RefineParameters};
use opencv::imgcodecs::{imread, IMREAD_GRAYSCALE};
use opencv::core::{Point2f, Point3f, Vector, Mat, MatTrait};
use opencv::calib3d::{solve_pnp, rodrigues};
pub fn get_extrinsic_inv_from_aruco(filepath : &str, marker_ids : Vec<i32>,marker_coords : Vec<[f32; 3]>, marker_type : PredefinedDictionaryType, intrinsic_info: &IntrinsicInfo) -> Result<Matrix4<f32>, anyhow::Error>{
let (rvec, tvec) = estimate_pose_from_aruco(filepath, marker_ids, marker_coords, marker_type, intrinsic_info)?;
let extrinsic = calc_extrinsic(rvec, tvec)?;
Ok(extrinsic.try_inverse().unwrap())
}
pub fn estimate_pose_from_aruco(filepath : &str, marker_ids : Vec<i32>,marker_coords : Vec<[f32; 3]>, marker_type : PredefinedDictionaryType, intrinsic_info: &IntrinsicInfo) -> Result<(Vector::<f32>, Vector::<f32>), anyhow::Error> {
let aruco_dict = get_predefined_dictionary(marker_type)?;
let aruco_detector = ArucoDetector::new(&aruco_dict, &DetectorParameters::default()?, RefineParameters::new_def()?)?;
let image = imread(filepath, IMREAD_GRAYSCALE)?;
let mut corners = Vector::<Vector<Point2f>>::new();
let mut ids = Vector::<i32>::new();
aruco_detector.detect_markers_def(&image, &mut corners, &mut ids)?;
if ids.len() < marker_ids.len(){
bail!("Not enough ids spotted")
}
let mut object_points = Vector::<Point3f>::new();
let mut image_points = Vector::<Point2f>::new();
for (i, id) in ids.iter().enumerate(){
if marker_ids.contains(&id){
println!("Marker {:?} detected", id);
let marker_index = marker_ids.iter().position(|&x| x == id).unwrap();
object_points.push(Point3f::new(marker_coords[marker_index][0], marker_coords[marker_index][1], marker_coords[marker_index][2]));
let detected_center = corners.get(i)?.get(0)?;
image_points.push(detected_center)
}
}
let mut rvec = Vector::<f32>::new();
let mut tvec = Vector::<f32>::new();
solve_pnp(&object_points, &image_points, &intrinsic_info.as_opencv_mat(), &Vector::<f32>::new(), &mut rvec, &mut tvec, false, 2)?;
Ok((rvec, tvec))
}
pub fn calc_extrinsic(rvec : Vector::<f32>, tvec : Vector::<f32>) -> Result<Matrix4<f32>, anyhow::Error>{
let mut rot_mat = Mat::default();
let mut jacobian = Mat::default();
rodrigues(&rvec, &mut rot_mat, &mut jacobian)?;
println!("{:?}", rot_mat);
let mut ext_matrix = Matrix4::new(0.0, 0.0, 0.0, 0.0,0.0, 0.0, 0.0, 0.0,0.0, 0.0, 0.0, 0.0,0.0, 0.0, 0.0, 0.0);
ext_matrix.m11 = *rot_mat.at_2d_mut::<f32>(0,0).unwrap();
ext_matrix.m12 = *rot_mat.at_2d_mut::<f32>(0,1).unwrap();
ext_matrix.m13 = *rot_mat.at_2d_mut::<f32>(0,2).unwrap();
ext_matrix.m21 = *rot_mat.at_2d_mut::<f32>(1,0).unwrap();
ext_matrix.m22 = *rot_mat.at_2d_mut::<f32>(1,1).unwrap();
ext_matrix.m23 = *rot_mat.at_2d_mut::<f32>(1,2).unwrap();
ext_matrix.m31 = *rot_mat.at_2d_mut::<f32>(2,0).unwrap();
ext_matrix.m32 = *rot_mat.at_2d_mut::<f32>(2,1).unwrap();
ext_matrix.m33 = *rot_mat.at_2d_mut::<f32>(2,2).unwrap();
ext_matrix.m14 = tvec.get(0).unwrap();
ext_matrix.m24 = tvec.get(1).unwrap();
ext_matrix.m34 = tvec.get(2).unwrap();
ext_matrix.m44 = 1.0;
Ok(ext_matrix)
}
pub fn get_extrinsic_inv_from_aruco_4x4_250(filepath : &str, marker_ids : Vec<i32>,marker_coords : Vec<[f32; 3]>, intrinsic_info: &IntrinsicInfo) -> Result<Matrix4<f32>, anyhow::Error>{
get_extrinsic_inv_from_aruco(filepath, marker_ids ,marker_coords, PredefinedDictionaryType::DICT_4X4_250, intrinsic_info)
}