
#include "CoordSys.h"

#include <string>
#include <vector>

#include "Gaussian.h"
#include "Wgs84.h"

namespace jf {

double GetValidZoneWidth(const Point &l, const Point &r) { return GetValidZoneWidth_impl(l, r); }

// Point::Point() : dLon(0), dLat(0), dAlt(0) {}

Point::Point(double inlon, double inlat, double inalt) {
    dLon = inlon;
    dLat = inlat;
    dAlt = inalt;
}

Point::Point(const Point &o) {
    dLon = o.dLon;
    dLat = o.dLat;
    dAlt = o.dAlt;
}

Point::Point(const std::vector<double> &d) {
    assert(d.size() >= 2);
    dLon = d[0];
    dLat = d[1];
    if (d.size() >= 3) {
        dAlt = d[2];
    }
}

Point::Point(const std::map<std::string, double> &m) {
    assert(Utils::MapValue(m, std::string("lon"), dLon));
    assert(Utils::MapValue(m, std::string("lat"), dLat));
    Utils::MapValue(m, std::string("height"), dAlt);
}

Point &Point::operator=(const Point &o) {
    dLon = o.dLon;
    dLat = o.dLat;
    dAlt = o.dAlt;
    return *this;
}

bool operator<(const Point &l, const Point &r) {
    if (l.dLon < r.dLon) return true;
    if (l.dLon == r.dLon && l.dLat < r.dLat) return true;
    if (l.dLon == r.dLon && l.dLat == r.dLat && l.dAlt < r.dAlt) return true;
    return false;
}

bool operator==(const Point &l, const Point &r) {
    if ((l.dLon == r.dLon) && (l.dLat == r.dLat) && (l.dAlt == r.dAlt)) return true;
    return false;
}

bool operator!=(const Point &l, const Point &r) {
    if ((l.dLon != r.dLon) || (l.dLat != r.dLat) || (l.dAlt != r.dAlt)) return true;
    return false;
}

bool Rect2D::intersect(const Rect2D &inr) {
    // 完全闭区间
    bool lonIntersect = (std::max(inr.rt.dLon, rt.dLon) - std::min(inr.lb.dLon, lb.dLon)) <= ((inr.rt.dLon - inr.lb.dLon) + (rt.dLon - lb.dLon));
    bool latIntersect = (std::max(inr.rt.dLat, rt.dLat) - std::min(inr.lb.dLat, lb.dLat)) <= ((inr.rt.dLat - inr.lb.dLat) + (rt.dLat - lb.dLat));
    return lonIntersect && latIntersect;
}

bool Rect2D::Contains(const Point &p) const {
    // 左下闭区间，右上开区间
    return p.dLon < rt.dLon && p.dLat < rt.dLat && p.dLon >= lb.dLon && p.dLat >= lb.dLat;
}
Rect2D Rect2D::getLt() const { return Rect2D(Point(lb.dLon, (lb.dLat + rt.dLat) / 2, 0), Point((lb.dLon + rt.dLon) / 2, rt.dLat, 0)); }
Rect2D Rect2D::getLb() const { return Rect2D(lb, Point((lb.dLon + rt.dLon) / 2, (lb.dLat + rt.dLat) / 2, 0)); }
Rect2D Rect2D::getRt() const { return Rect2D(Point(((lb.dLon + rt.dLon) / 2), (lb.dLat + rt.dLat) / 2, 0), rt); }
Rect2D Rect2D::getRb() const { return Rect2D(Point((lb.dLon + rt.dLon) / 2, lb.dLat, 0), Point(rt.dLon, (lb.dLat + rt.dLat) / 2, 0)); }
Rect2D::Rect2D(const Point &inlb, const Point &inrt) : lb(inlb), rt(inrt) {}
Rect2D::Rect2D(const Rect2D &o) : lb(o.lb), rt(o.rt) {}

Rect2D::Rect2D(const Point &p, float lon_interval_half, float lat_interval_half) {
    XYZ xyp = P2XYZ(p);
    XYZ xylb = XYZ(xyp.x - lon_interval_half, xyp.y - lat_interval_half, 0);
    XYZ xyrt = XYZ(xyp.x + lon_interval_half, xyp.y + lat_interval_half, 0);
    lb = XYZ2P(xylb);
    rt = XYZ2P(xyrt);
}

TileId::TileId(int inlonId, int inlatId) {
    nLonId = inlonId;
    nLatId = inlatId;
}

bool operator<(const TileId &l, const TileId &r) {
    if (l.nLonId < r.nLonId) return true;
    if (l.nLonId == r.nLonId && l.nLatId < r.nLatId) return true;
    return false;
}

double D2_84(const Point &l, const Point &r) { return pow((l.dLon - r.dLon), 2) + pow((l.dLat - r.dLat), 2); }

double D_84(const Point &l, const Point &r) { return sqrt(D2_84(l, r)); }

double D2XYZ(const XYZ &l, const XYZ &r, bool consider_alt) {
    if (consider_alt)
        return pow((l.x - r.x), 2) + pow((l.y - r.y), 2) + pow((l.z - r.z), 2);
    else
        return pow((l.x - r.x), 2) + pow((l.y - r.y), 2);
}

double D2XYZ(const Point &l, const Point &r, bool consider_alt) {
    XYZ ll = P2XYZ(l);
    XYZ rr = P2XYZ(r);
    return D2XYZ(ll, rr, consider_alt);
}

double DXYZ(const XYZ &l, const XYZ &r, bool consider_alt) { return sqrt(D2XYZ(l, r, consider_alt)); }

double DXYZ(const Point &l, const Point &r, bool consider_alt) { return sqrt(D2XYZ(l, r, consider_alt)); }

XYZ P2XYZ(const Point &p) {
    XYZ ret = Convert(p);
    return ret;
}

Point XYZ2P(const XYZ &in) {
    Point ret = Inverse(in);
    return ret;
}

XYZ P2XYZ(const Point &p, double zone_width) { return Convert(p, zone_width); }

Point XYZ2P(const XYZ &in, double zone_width) { return Inverse(in, zone_width); }
}  // namespace jf