// Copyright (c) 2018-2025  Robert J. Hijmans
//
// This file is part of the "spat" library.
//
// spat is free software: you can redistribute it and/or modify it
// under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 2 of the License, or
// (at your option) any later version.
//
// spat is distributed in the hope that it will be useful, but
// WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with spat. If not, see <http://www.gnu.org/licenses/>.


//double distance_cos(const double &lon1, const double &lat1, const double &lon2, const double &lat2, const double &r = 6378137);

inline void deg2rad(std::vector<double> &x) {
	const double f = 0.0174532925199433;
	for (double& d : x) d *= f;
}

inline void deg2rad(double &x) {
	const double f = 0.0174532925199433;
	x *= f;
}


inline double distance_cos(double lon1, double lat1, double lon2, double lat2) {
	const double r = 6378137.;
	return r * acos((sin(lat1) * sin(lat2) + cos(lat1) * cos(lat2) * cos(lon1-lon2)));
}



inline double distance_hav(double lon1, double lat1, double lon2, double lat2) {
	const double r = 6378137.;
	double dLat = lat2-lat1;
	double dLon = lon2-lon1;
	double a = sin(dLat/2.) * sin(dLat/2.) + cos(lat1) * cos(lat2) * sin(dLon/2.) * sin(dLon/2.);
	return 2. * atan2(sqrt(a), sqrt(1. - a)) * r;
}

double distance_geo(double lon1, double lat1, double lon2, double lat2);


double direction_cos(double& lon1, double& lat1, double& lon2, double& lat2);

double dist2segment_hav(double plon, double plat, double lon1, double lat1, double lon2, double lat2, double r=6378137.);
double dist2segment_cos(double plon, double plat, double lon1, double lat1, double lon2, double lat2, double r=6378137.);
double dist2segment_geo(double plon, double plat, double lon1, double lat1, double lon2, double lat2, double notused=0.);
double dist2segment_geo_ell(double plon, double plat, double lon1, double lat1, double lon2, double lat2, double r, const struct geod_geodesic &g_ell);



bool pip_geo_in_ring(double plon, double plat, const std::vector<double> &rx, const std::vector<double> &ry);

// Prepared ring for spherical point-in-polygon. All per-edge work that
// does not depend on the test point is cached here so that querying many
// test points against the same ring is much cheaper.
//
//   lon, lat   : closed ring vertices (degrees), last == first
//   Nx, Ny, Nz : per-edge cross product V1 x V2 on the unit sphere
//   max_arc_lat: upper bound (degrees) on the latitude reached by any
//                edge's great-circle arc, including the bulge toward the
//                nearer pole on long arcs. Used for the cheap "ray going
//                north passes above all edges" prune.
//   wraps_pole : true iff the ring topologically encircles a pole (the
//                signed sum of its edge dlons is approximately +/- 360).
//   flip_north : true iff the north pole is inside the ring; the
//                ray-cast crossing parity is XOR'd with this to handle
//                the pole-inside case correctly.
struct PipGeoRing {
	std::vector<double> lon;
	std::vector<double> lat;
	std::vector<double> Nx, Ny, Nz;
	double max_arc_lat;
	bool wraps_pole;
	bool flip_north;
};

void prepare_pip_geo_ring(const std::vector<double> &rx,
                          const std::vector<double> &ry,
                          PipGeoRing &out);

// Test point given in degrees (plon_deg, plat_deg). The caller supplies
// cos/sin(plon in radians) and plat_rad to amortize the per-point trig
// across all edges (and across all rings of a multi-ring polygon).
bool pip_geo_in_ring_prepared(double plon_deg, double plat_deg,
                              double cos_plon_rad, double sin_plon_rad,
                              double plat_rad,
                              const PipGeoRing &ring);
