
#ifndef _MDC_ALGORITHMS_H_
#define _MDC_ALGORITHMS_H_

#include "mdc_common.h"

namespace mdc {

  

// top is front of list, bottom is back
template<class C>
  void restack_up(std::list<C*> &stack, C *object, C *above= 0)
{
  typename std::list<C*>::iterator iter= std::find(stack.begin(), stack.end(), object);
  if (iter == stack.end())
    return;

  stack.erase(iter);
  if (above)
    stack.insert(find(stack.begin(), stack.end(), above), object);
  else
    stack.push_front(object);
}



template<class C> 
  void restack_down(std::list<C*> &stack, C *object)
{
  typename std::list<C*>::iterator iter= std::find(stack.begin(), stack.end(), object);
  if (iter == stack.end())
    return;

  stack.erase(iter);
  stack.push_back(object);
}

//----------------------------------------------------------------------------------------------------------------------


bool MYSQLCANVAS_PUBLIC_FUNC intersect_lines(const Point &s1, const Point &e1,
                     const Point &s2, const Point &e2,
                     Point &intersection_ret);

bool MYSQLCANVAS_PUBLIC_FUNC intersect_rect_to_line(const Rect &rect,
                            const Point &s, const Point &e,
                            Point &intersection1_ret,
                            Point &intersection2_ret);



inline double angle_of_line(const Point &p1, const Point &p2)
{
  double angle;
  // figure out the angle of the line in degrees
  if (p1 == p2)
    angle= 0;
  else
  {
    if (p2.y < p1.y)
      angle= 90.0 + atan((p2.x-p1.x)/(p2.y-p1.y)) * 180.0/M_PI;
    else
      angle= 270.0 + atan((p2.x-p1.x)/(p2.y-p1.y)) * 180.0/M_PI;

    angle= angle - floor(angle/360)*360;
  }
  return angle;
}


//----------------------------------------------------------------------------------------------------------------------

/** 
 * Determines whether both bounds overlap. 
 *
 * @param bounds1 One of the bounds to compare.
 * @param bounds2 The other bounds to compare.
 * @return True if both bounds overlap each other, otherwise false.
 * @note Bounds must be sorted.
 */
inline bool bounds_intersect(const Rect& bounds1, const Rect& bounds2)
{
  return (bounds1.xmax() >= bounds2.xmin()) && (bounds1.xmin() <= bounds2.xmax()) &&
    (bounds1.ymax() >= bounds2.ymin()) && (bounds1.ymin() <= bounds2.ymax());
}

//----------------------------------------------------------------------------------------------------------------------

/** 
 * Determines whether the second bounds are completely within the first bounds. 
 *
 * @param bounds1 The outer bounds to compare.
 * @param bounds2 The inner bounds to compare.
 * @return True if the second bounds are completely within the first bounds, otherwise false.
 * @note Bounds must be sorted.
 */
inline bool bounds_contain_bounds(const Rect& bounds1, const Rect& bounds2)
{
  return (bounds1.xmin() <= bounds2.xmin()) && (bounds2.xmax() <= bounds1.xmax()) && 
    (bounds1.ymin() <= bounds2.ymin()) && (bounds2.ymax() <= bounds1.ymax());
}

//----------------------------------------------------------------------------------------------------------------------

/** 
 * Determines whether the given bounds include the given point.
 *
 * @param bounds The bounds to check the point against.
 * @param x The horizontal coordinate to check.
 * @param y The vertical coordinate to check.
 * @return True if the point is within the bounds, otherwise false.
 * @note Bounds must be sorted.
 */
inline bool bounds_contain_point(const Rect& bounds, double x, double y)                  
{
  return (bounds.xmax() >= x) && (bounds.pos.x <= x) && (bounds.ymax() >= y) && (bounds.pos.y <= y);
}

//----------------------------------------------------------------------------------------------------------------------

/**
 * Examines the given bounds and returns whether it is empty or not.
 *
 * @param bounds The bounds to examine.
 * @return True if the bounds are empty, false otherwise.
 */
inline bool bounds_are_empty(const Rect& bounds)
{
  return (bounds.size.width == 0) && (bounds.size.height == 0);
}

//----------------------------------------------------------------------------------------------------------------------

inline Rect clip_bound(const Rect &bounds, const Rect &clip)
{
  double x1= bounds.xmin();
  double x2= bounds.xmax();
  double y1= bounds.ymin();
  double y2= bounds.ymax();

  if (x1 < clip.xmin())
    x1= clip.xmin();
  if (y1 < clip.ymin())
    y1= clip.ymin();
  if (x2 > clip.xmax())
    x2= clip.xmax();
  if (y2 > clip.ymax())
    y2= clip.ymax();

  return Rect(Point(x1, y1), Size(x2-x1, y2-y1));
}

//----------------------------------------------------------------------------------------------------------------------

inline Point bounds_center(const Rect &bounds)
{
  return Point(bounds.xmin()+bounds.width()/2, bounds.ymin()+bounds.height()/2);
}


//----------------------------------------------------------------------------------------------------------------------

inline Rect expand_bound(const Rect &bounds, double dx, double dy)
{
  Rect r= bounds;
  
  r.pos.x-= dx;
  r.pos.y-= dy;
  r.size.width+= dx*2;
  r.size.height+= dy*2;
  return r;
}

//----------------------------------------------------------------------------------------------------------------------

inline double points_distance(const Point &p1, const Point &p2)
{
  return sqrt((p1.x-p2.x)*(p1.x-p2.x) + (p1.y-p2.y)*(p1.y-p2.y));
}


double MYSQLCANVAS_PUBLIC_FUNC point_line_distance(const Point &p1, const Point &p2, const Point &p);

//----------------------------------------------------------------------------------------------------------------------

inline void points_reorder(Point &topleft, Point &bottomright)
{
  double tmp;
  
  if (topleft.x > bottomright.x)
  {
    tmp= topleft.x;
    topleft.x= bottomright.x;
    bottomright.x= tmp;
  }
  if (topleft.y > bottomright.y)
  {
    tmp= topleft.y;
    topleft.y= bottomright.y;
    bottomright.y= tmp;
  }
}


};

#endif /* _MDC_ALGORITHMS_H_ */
