// testing
#ifndef __WXBOUNDINGBOX_H__
#define __WXBOUNDINGBOX_H__

#ifndef WX_PRECOMP
#include "wx/wx.h"
#endif

#include "wx/matrix.h"
#include "wx/geometry.h"

enum OVERLAP { _IN, _ON, _OUT };

// Purpose   The wxBoundingBox class stores one wxBoundingBox.
// The wxBoundingBox is defined by two coordiates,
// a upperleft coordinate and a lowerright coordinate.
class wxBoundingBox {
public:
  wxBoundingBox();
  wxBoundingBox(const wxBoundingBox&);
  wxBoundingBox(const wxPoint2DDouble&);
  wxBoundingBox(double xmin, double ymin, double xmax, double ymax);
  virtual ~wxBoundingBox();

  bool And(wxBoundingBox*, double Marge = 0);

  void EnLarge(const double Marge);
  void Shrink(const double Marge);

  void Expand(const wxPoint2DDouble&, const wxPoint2DDouble&);
  void Expand(const wxPoint2DDouble&);
  void Expand(double x, double y);
  void Expand(const wxBoundingBox& bbox);

  OVERLAP Intersect(const wxBoundingBox&, double Marge = 0) const;

  /* this routine is used very heavily, so this is a lightweight
     version for when we only care if the other box is out */
  virtual inline bool IntersectOut(const wxBoundingBox& other) const {
    return (m_minx > other.m_maxx) || (m_maxx < other.m_minx) ||
           (m_maxy < other.m_miny) || (m_miny > other.m_maxy);
  }

  bool LineIntersect(const wxPoint2DDouble& begin,
                     const wxPoint2DDouble& end) const;
  bool PointInBox(const wxPoint2DDouble&, double Marge = 0) const;
  virtual bool PointInBox(double, double, double Marge = 0) const;

  void Reset();

  void Translate(wxPoint2DDouble&);
  void MapBbox(const wxTransformMatrix& matrix);

  double GetWidth() const { return m_maxx - m_minx; };
  double GetHeight() const { return m_maxy - m_miny; };
  bool GetValid() const;
  void SetValid(bool);

  void SetBoundingBox(const wxPoint2DDouble& a_point);

  void SetMin(double, double);
  void SetMax(double, double);
  inline wxPoint2DDouble GetMin() const;
  inline wxPoint2DDouble GetMax() const;
  inline double GetMinX() const { return m_minx; };
  inline double GetMinY() const { return m_miny; };
  inline double GetMaxX() const { return m_maxx; };
  inline double GetMaxY() const { return m_maxy; };

  wxBoundingBox& operator+(wxBoundingBox&);
  wxBoundingBox& operator=(const wxBoundingBox&);

protected:
  // bounding box in world
  double m_minx;
  double m_miny;
  double m_maxx;
  double m_maxy;
  bool m_validbbox;
};

//    A class derived from wxBoundingBox
//    that is assummed to be a geographic area, with coordinates
//    expressed in Lat/Lon.
//    This class understands the International Date Line (E/W Longitude)

class LLBBox {
public:
  LLBBox() : m_valid(FALSE) {}
  void Set(double minlat, double minlon, double maxlat, double maxlon);
  void SetFromSegment(double lat1, double lon1, double lat2, double lon2);
  void Expand(const LLBBox& bbox);
  bool Contains(double Lat, double Lon) const;
  bool ContainsMarge(double Lat, double Lon, double Marge) const;
  bool GetValid() const { return m_valid; }
  void Invalidate() { m_valid = false; }

  bool IntersectIn(const LLBBox& other) const;

  //  Add some epsilon to the equality measurement
  //  This class is concerned with lat/lon, in degrees
  //  So here we can arbitrarily establish epsilon as 1e-6 degrees,
  //  which is about 300 mm at the equator.
  //  This is judged close enough for geometric region calculations
  inline bool IntersectOut(const LLBBox& other) const {
    // allow -180 to 180 or 0 to 360
    if (!GetValid() || !other.GetValid()) return true;

    if ((m_maxlat + 1e-6 < other.m_minlat) ||
        (m_minlat - 1e-6 > other.m_maxlat))
      return true;

    double minlon = m_minlon, maxlon = m_maxlon;
    if (m_maxlon < other.m_minlon)
      minlon += 360, maxlon += 360;
    else if (m_minlon > other.m_maxlon)
      minlon -= 360, maxlon -= 360;

    return (minlon - 1e-6 > other.m_maxlon) || (maxlon + 1e-6 < other.m_minlon);
  }
  bool IntersectOutGetBias(const LLBBox& other, double bias) const;

  double GetMinLat() const { return m_minlat; };
  double GetMinLon() const { return m_minlon; };
  double GetMaxLat() const { return m_maxlat; };
  double GetMaxLon() const { return m_maxlon; };

  void EnLarge(const double Marge);
  double GetLonRange() const { return m_maxlon - m_minlon; }
  double GetLatRange() const { return m_maxlat - m_minlat; };

private:
  double m_minlat;
  double m_minlon;
  double m_maxlat;
  double m_maxlon;
  bool m_valid;
};

#endif
