/**
 * @file
 *
 * @date Created  on Oct 13, 2025
 * @author Attila Kovacs
 */

#include <iostream>

#include "TestUtil.hpp"



int main() {
  TestUtil test = TestUtil("HorizontalTrack");

  int n = 0;

  ScalarEvolution xe(NAN);
  ScalarEvolution lon(1.0, 2.0), lat(-1.1, 2.1), r(1.2, -2.2);

  if(!test.check("invalid(time)", !HorizontalTrack(Time::undefined(), Interval(1.0), lon, lat, r).is_valid())) n++;
  if(!test.check("invalid(range)", !HorizontalTrack(Time::j2000(), Interval(NAN), lon, lat, r).is_valid())) n++;
  if(!test.check("invalid(lon)", !HorizontalTrack(Time::j2000(), Interval(1.0), xe, lat, r).is_valid())) n++;
  if(!test.check("invalid(lat)", !HorizontalTrack(Time::j2000(), Interval(1.0), lon, xe, r).is_valid())) n++;
  if(!test.check("invalid(r)", !HorizontalTrack(Time::j2000(), Interval(1.0), lon, lat, xe).is_valid())) n++;

  Interval ip(1.0 / Constant::pi);
  Time t1 = Time::j2000() + ip;

  HorizontalTrack hx = HorizontalTrack(Time::j2000(), Interval(1.0), xe, xe, xe);
  if(!test.check("invalid is_valid()", !hx.is_valid())) n++;
  if(!test.check("invalid longitude_at()", !hx.longitude_at(t1).is_valid())) n++;
  if(!test.check("invalid latitude_at()", !hx.latitude_at(t1).is_valid())) n++;
  if(!test.check("invalid distance_at()", !hx.distance_at(t1).is_valid())) n++;
  if(!test.check("invalid raidal_velocity_at()", !hx.radial_velocity_at(t1).is_valid())) n++;
  if(!test.check("invalid projected_at()", !hx.projected_at(t1).is_valid())) n++;


  HorizontalTrack h(Time::j2000(), Interval(1.0), lon, lat, r);
  if(!test.check("is_valid()", h.is_valid())) n++;
  if(!test.check("reference_time()", h.reference_time() == Time::j2000())) n++;
  if(!test.check("range()", h.range() == Interval(1.0))) n++;
  if(!test.check("is_valid_at(OK)", h.is_valid_at(t1))) n++;
  if(!test.check("is_valid_at(!)", !h.is_valid_at(t1 + 1.0))) n++;

  if(!test.equals("longitude_evolution()", h.longitude_evolution().value(ip), lon.value(ip), 1e-15)) n++;
  if(!test.equals("latitude_evolution()", h.latitude_evolution().value(ip), lat.value(ip), 1e-15)) n++;
  if(!test.equals("distance_evolution()", h.distance_evolution().value(ip), r.value(ip), 1e-15)) n++;

  Angle oa = h.longitude_at(t1);
  if(!test.check("longitude_at()", oa.is_valid())) n++;
  if(!test.equals("longitude_at().value()", oa.rad(), lon.value(ip), 1e-15)) n++;

  oa = h.latitude_at(t1);
  if(!test.check("latitude_at()", oa.is_valid())) n++;
  if(!test.equals("latitude_at().value()", oa.rad(), lat.value(ip), 1e-15)) n++;

  Coordinate oc = h.distance_at(t1);
  if(!test.check("distance_at()", oc.is_valid())) n++;
  if(!test.equals("distance_at().value()", oc.m(), r.value(ip), 1e-15)) n++;

  ScalarVelocity ov = h.radial_velocity_at(t1);
  if(!test.check("radial_velocity_at()", ov.is_valid())) n++;
  if(!test.equals("radial_velocity_at().value()", ov.m_per_s(), r.rate(ip), 1e-6)) n++;

  if(!test.check("redshift_at()", isfinite(h.redshift_at(t1)))) n++;
  if(!test.equals("redshift_at().value()", h.redshift_at(t1), novas_v2z(r.rate(ip) / (Unit::km_per_s)), 1e-15)) n++;

  Horizontal oh = h.projected_at(t1);
  if(!test.check("projected_at()", oh.is_valid())) n++;
  if(!test.equals("projected_at().azimuth()", oh.azimuth().rad(), lon.value(ip), 1e-15)) n++;
  if(!test.equals("projected_at().elevation()", oh.elevation().rad(), lat.value(ip), 1e-15)) n++;

  ScalarEvolution z(0.1, 0.001);
  h = HorizontalTrack(Time::j2000(), Interval(1.0), lon, lat, r, z);
  if(!test.check("is_valid(z)", h.is_valid())) n++;
  if(!test.equals("redshift_evolution()", h.redshift_evolution().value(ip), z.value(ip), 1e-15)) n++;

  novas_track tr0 = {};
  novas_set_time(NOVAS_TT, NOVAS_JD_J2000, 32.0, 0.0, &tr0.time);

  tr0.pos.lon = lon.value(Interval::zero()) / Unit::deg;
  tr0.rate.lon = lon.rate(Interval::zero()) / Unit::deg;
  tr0.accel.lon = lon.acceleration() / Unit::deg;

  tr0.pos.lat = lat.value(Interval::zero()) / Unit::deg;
  tr0.rate.lat = lat.rate(Interval::zero()) / Unit::deg;
  tr0.accel.lat = lat.acceleration() / Unit::deg;

  tr0.pos.dist = r.value(Interval::zero()) / Unit::AU;
  tr0.rate.dist = r.rate(Interval::zero()) / Unit::AU;
  tr0.accel.dist = r.acceleration() / Unit::AU;

  tr0.pos.z = NAN;

  novas_track trx = tr0; trx.pos.lon = NAN;
  if(!test.check("from_novas_track(pos0 = NAN)", !HorizontalTrack::from_novas_track(&trx, Interval(1.0)).is_valid())) n++;

  trx = tr0; trx.rate.lon = NAN;
  if(!test.check("from_novas_track(pos1 = NAN)", !HorizontalTrack::from_novas_track(&trx, Interval(1.0)).is_valid())) n++;

  trx = tr0; trx.accel.lon = NAN;
  if(!test.check("from_novas_track(pos2 = NAN)", !HorizontalTrack::from_novas_track(&trx, Interval(1.0)).is_valid())) n++;

  h = HorizontalTrack::from_novas_track(NULL, Interval(1.0));
  if(!test.check("from_novas_track(NULL)", !h.is_valid())) n++;

  h = HorizontalTrack::from_novas_track(&tr0, Interval(1.0));
  if(!test.check("from_novas_track()", h.is_valid())) n++;

  oa = h.longitude_at(t1);
  if(!test.check("longitude_at(tr)", oa.is_valid())) n++;
  if(!test.equals("longitude_at(tr).value()", oa.rad(), lon.value(ip), 1e-15)) n++;

  oa = h.latitude_at(t1);
  if(!test.check("latitude_at(tr)", oa.is_valid())) n++;
  if(!test.equals("latitude_at(tr).value()", oa.rad(), lat.value(ip), 1e-15)) n++;

  oc = h.distance_at(t1);
  if(!test.check("distance_at(tr)", oc.is_valid())) n++;
  if(!test.equals("distance_at(tr).value()", oc.m(), r.value(ip), 1e-15)) n++;

  ov = h.radial_velocity_at(t1);
  if(!test.check("radial_velocity_at(tr)", ov.is_valid())) n++;
  if(!test.equals("radial_velocity_at(tr).value()", ov.m_per_s(), r.rate(ip), 1e-6)) n++;

  Time t2 = Time::j2000() - Interval(1.1);
  if(!test.check("longitude_at(range)", !h.longitude_at(t2).is_valid())) n++;
  if(!test.check("latitude_at(range)", !h.latitude_at(t2).is_valid())) n++;
  if(!test.check("distance_at(range)", !h.distance_at(t2).is_valid())) n++;
  if(!test.check("radial_velocity_at(range)", !h.radial_velocity_at(t2).is_valid())) n++;
  if(!test.check("redshift_at(range)", !isfinite(h.redshift_at(t2)))) n++;
  if(!test.check("projected_at(range)", !h.projected_at(t2).is_valid())) n++;


  // =========================================================================

  test = TestUtil("EquatorialTrack");

  EquatorialTrack ex = EquatorialTrack(Equinox::icrs(), Time::j2000(), Interval(1.0), xe, xe, xe);
  if(!test.check("invalid is_valid()", !ex.is_valid())) n++;
  if(!test.check("invalid longitude_at()", !ex.longitude_at(t1).is_valid())) n++;
  if(!test.check("invalid latitude_at()", !ex.latitude_at(t1).is_valid())) n++;
  if(!test.check("invalid distance_at()", !ex.distance_at(t1).is_valid())) n++;
  if(!test.check("invalid raidal_velocity_at()", !ex.radial_velocity_at(t1).is_valid())) n++;
  if(!test.check("invalid projected_at()", !ex.projected_at(t1).is_valid())) n++;

  EquatorialTrack et(Equinox::icrs(), Time::j2000(), Interval(1.0), lon, lat, r);
  if(!test.check("is_valid()", et.is_valid())) n++;
  if(!test.check("reference_time()", et.reference_time() == Time::j2000())) n++;
  if(!test.check("range()", et.range() == Interval(1.0))) n++;
  if(!test.check("is_valid_at(OK)", et.is_valid_at(t1))) n++;
  if(!test.check("is_valid_at(!)", !et.is_valid_at(t1 + 1.0))) n++;

  if(!test.equals("longitude_evolution()", et.longitude_evolution().value(ip), lon.value(ip), 1e-15)) n++;
  if(!test.equals("latitude_evolution()", et.latitude_evolution().value(ip), lat.value(ip), 1e-15)) n++;
  if(!test.equals("distance_evolution()", et.distance_evolution().value(ip), r.value(ip), 1e-15)) n++;

  oa = et.longitude_at(t1);
  if(!test.check("longitude_at()", oa.is_valid())) n++;
  if(!test.equals("longitude_at().value()", oa.rad(), lon.value(ip), 1e-15)) n++;

  oa = et.latitude_at(t1);
  if(!test.check("latitude_at()", oa.is_valid())) n++;
  if(!test.equals("latitude_at().value()", oa.rad(), lat.value(ip), 1e-15)) n++;

  oc = et.distance_at(t1);
  if(!test.check("distance_at()", oc.is_valid())) n++;
  if(!test.equals("distance_at().value()", oc.m(), r.value(ip), 1e-15)) n++;

  ov = et.radial_velocity_at(t1);
  if(!test.check("radial_velocity_at()", ov.is_valid())) n++;
  if(!test.equals("radial_velocity_at().value()", ov.m_per_s(), r.rate(ip), 1e-6)) n++;

  if(!test.check("redshift_at()", isfinite(et.redshift_at(t1)))) n++;
  if(!test.equals("redshift_at().value()", et.redshift_at(t1), novas_v2z(r.rate(ip) / (Unit::km_per_s)), 1e-15)) n++;

  Equatorial oe = et.projected_at(t1);
  if(!test.check("projected_at()", oe.is_valid())) n++;
  if(!test.equals("projected_at().azimuth()", oe.ra().rad(), lon.value(ip), 1e-15)) n++;
  if(!test.equals("projected_at().elevation()", oe.dec().rad(), lat.value(ip), 1e-15)) n++;

  et = EquatorialTrack(Equinox::undefined(), Time::j2000(), Interval(1.0), lon, lat, r, z);
  if(!test.check("is_valid(equinox invalid)", !et.is_valid())) n++;

  et = EquatorialTrack(Equinox::icrs(), Time::undefined(), Interval(1.0), lon, lat, r, z);
  if(!test.check("is_valid(time invalid)", !et.is_valid())) n++;

  et = EquatorialTrack(Equinox::icrs(), Time::j2000(), Interval(1.0), lon, lat, r, z);
  if(!test.check("is_valid(z)", et.is_valid())) n++;
  if(!test.equals("redshift_evolution()", et.redshift_evolution().value(ip), z.value(ip), 1e-15)) n++;

  oe = et.projected_at(t1);
  if(!test.check("projected_at()", oe.is_valid())) n++;
  if(!test.equals("projected_at().azimuth()", oe.ra().rad(), lon.value(ip), 1e-15)) n++;
  if(!test.equals("projected_at().elevation()", oe.dec().rad(), lat.value(ip), 1e-15)) n++;

  if(!test.check("longitude_at(range)", !et.longitude_at(t2).is_valid())) n++;
  if(!test.check("latitude_at(range)", !et.latitude_at(t2).is_valid())) n++;
  if(!test.check("distance_at(range)", !et.distance_at(t2).is_valid())) n++;
  if(!test.check("radial_velocity_at(range)", !et.radial_velocity_at(t2).is_valid())) n++;
  if(!test.check("redshift_at(range)", !isfinite(et.redshift_at(t2)))) n++;
  if(!test.check("projected_at(range)", !et.projected_at(t2).is_valid())) n++;

  et = EquatorialTrack::from_novas_track(Equinox::icrs(), NULL, Interval(1.0));
  if(!test.check("from_novas_track(NULL)", !et.is_valid())) n++;

  et = EquatorialTrack::from_novas_track(Equinox::undefined(), &tr0, Interval(1.0));
  if(!test.check("from_novas_track(invalid system)", !et.is_valid())) n++;

  trx = tr0; trx.pos.lon = NAN;
  et = EquatorialTrack::from_novas_track(Equinox::icrs(), &trx, Interval(1.0));
  if(!test.check("from_novas_track(pos0 = NAN)", !et.is_valid())) n++;

  et = EquatorialTrack::from_novas_track(Equinox::icrs(), &tr0, Interval(1.0));
  if(!test.check("from_novas_track(pos0 = NAN)", et.is_valid())) n++;


  std::cout << "Track.cpp: " << (n > 0 ? "FAILED" : "OK") << "\n";
  return n;
}
