use either::Either;
use fixed::types::I16F16 as Ftype;
use serde_json;

use math_utils::*;

#[expect(unused_macros)]
macro_rules! show {
  ($e:expr) => { println!("{}:\n{:?}", stringify!($e), $e); }
}

macro_rules! display {
  ($e:expr) => { println!("{}:\n{}", stringify!($e), $e); }
}

#[derive(Clone, Copy, Debug)]
struct WorldSpace;
type WorldPoint       = coordinate::Position3 <Ftype, WorldSpace>;
type WorldVector      = <WorldPoint as AffineSpace <Ftype>>::Translation;
type WorldEndo        = <WorldVector as Module <Ftype>>::LinearEndo;
type WorldProjective  = coordinate::Position4 <Ftype, WorldSpace>;

#[derive(Clone, Copy, Debug)]
struct LocalSpace;
type LocalPoint       = coordinate::Position3 <Ftype, LocalSpace>;
type LocalVector      = <LocalPoint as AffineSpace <Ftype>>::Translation;

type WorldToLocal     = coordinate::Transform3 <Ftype, WorldSpace, LocalSpace>;

fn main() {
  use num::One;
  println!("i32...");
  let p1 : Point2 <i32> = [1, -1].into();
  let p2 : Point2 <i32> = [-1, 1].into();
  display!(p1);
  display!(p2);
  display!(p2 - p1);
  println!("...i32");

  println!("f32...");
  display!(1.0f32.exp());
  let v : Vector2 <f32> = [0.0, 1.0].into();
  let m : Matrix2 <f32> = Matrix2::from_col_arrays ([
    [ 0.0, -1.0],
    [ 1.0,  0.0]
  ]);
  display!(v);
  display!(m);
  display!(m * v);
  println!("...f32");

  println!("I16F16...");
  display!(Ftype::from_num (1).exp());

  let v = Vector2::<Ftype>::from_num::<i32> ([0, 1].into());
  println!("v[0]: {}", v[0]);
  let m = Matrix2::<Ftype>::from_num::<i32> (Matrix2::from_col_arrays ([
    [ 0, -1],
    [ 1,  0]
  ]));
  display!(v);
  display!(Vector3::<Ftype>::from (v));
  display!(v.with_z (Ftype::one()));
  display!(m);
  display!(Matrix3::from (m));
  display!(m * v);
  println!("...I16F16");

  println!("coordinates...");
  let t : AffineMap <Ftype, WorldPoint, LocalPoint, WorldToLocal> = {
    let m = WorldToLocal::from_num::<i32> (Matrix3::from_col_arrays ([
      [ 0, -1, 0 ],
      [ 1,  0, 0 ],
      [ 0,  0, 1 ]
    ]));
    let x = LocalVector::from_num::<i32> ([-1, -1, 0].into());
    display!(x);
    display!(x.norm());
    display!(x.normalize());
    AffineMap::new (m, x)
  };
  let p = WorldPoint::from (Point3::<Ftype>::from_num::<i32> ([-1, -1, 0].into()));
  display!(p);
  display!(t);
  display!(t.map (p));

  let a : Affinity <Ftype, WorldPoint, WorldPoint, WorldEndo> = {
    let m = WorldEndo::from_num::<i32> (Matrix3::from_col_arrays ([
      [ 0, -1, 0 ],
      [ 1,  0, 0 ],
      [ 0,  0, 1 ]
    ]));
    let x = WorldVector::from_num::<i32> ([-1, -1, 0].into());
    Affinity::new (LinearIso::new (m).unwrap(), x)
  };
  let h = WorldProjective::homography (a);
  display!(a);
  display!(h);
  display!(h * WorldProjective::homogeneous (Either::Left (p)));
  display!(serde_json::to_string (&a).unwrap());
  println!("...coordinates");
}
