#ifndef _MDC_COMMON_H_
#define _MDC_COMMON_H_

#ifndef _WIN32
#include <glib.h>
#include <list>
#include <vector>
#include <string>
#include <map>
#include <set>
#include <stdexcept>
#include <assert.h>
#include <algorithm>
#include <typeinfo>
#include <stdio.h>

#include <sigc++/sigc++.h>

#define _USE_MATH_DEFINES
#include <math.h>

#include <cairo/cairo.h>

#ifdef __APPLE__
#include <OpenGL/gl.h>
#else
#include <GL/gl.h>
#endif
#endif


#include "mdc_canvas_public.h"

#define BEGIN_MDC_DECLS namespace mdc {
#define END_MDC_DECLS };


#ifdef _WIN32
#define DEFAULT_FONT_FACE "Arial"
#elif defined(__APPLE__)
#define DEFAULT_FONT_FACE "Lucida Grande"
#else
#define DEFAULT_FONT_FACE "Helvetica"
#endif


#define MM_TO_PT(m) ((m)/(25.4/72.0))


BEGIN_MDC_DECLS

typedef unsigned int Count;
typedef double Timestamp;

//class Variable;

struct MYSQLCANVAS_PUBLIC_FUNC Size {
  double width;
  double height;

  Size() : width(0), height(0) {};
  Size(double w, double h) : width(w), height(h) {};

  inline Size round() const { Size s; s.width= ceil(width); s.height= ceil(height); return s; };
  inline std::string str() const { char buf[20]; snprintf(buf, sizeof(buf), "{%.2fx%.2f}", width, height); return buf; };
  
  inline bool operator==(const Size &s) const { return s.width == width && s.height == height; };
  inline bool operator!=(const Size &s) const { return s.width != width || s.height != height; };  
};


struct MYSQLCANVAS_PUBLIC_FUNC Point {
  double x;
  double y;
  
  Point() : x(0), y(0) {};
  Point(double xx, double yy) : x(xx), y(yy) {};

  inline Point operator+(const Point &p) const { return Point(p.x+x, p.y+y); };
  inline Point operator-(const Point &p) const { return Point(x-p.x, y-p.y); };
  inline Point operator-() const { return Point(-x, -y); };
  inline bool operator==(const Point &p) const { return p.x == x && p.y == y; };
  inline bool operator!=(const Point &p) const { return p.x != x || p.y != y; };
  inline Point round() const { Point p; p.x= ceil(x); p.y= ceil(y); return p; };
  inline std::string str() const { char buf[20]; snprintf(buf, sizeof(buf), "{%.2f,%.2f}", x, y); return buf; };
};


struct MYSQLCANVAS_PUBLIC_FUNC Rect {
  Point pos;
  Size size;
  Rect() {};
  Rect(const Point &tl, const Point &br) : pos(tl), size(br.x-tl.x, br.y-tl.y) {};
  Rect(const Point &apos, const Size &asize) : pos(apos), size(asize) {};
  Rect(double x, double y, double w, double h) : pos(Point(x,y)), size(Size(w,h)) {};
  
  inline double xmax() const { return pos.x + size.width; };
  inline double ymax() const { return pos.y + size.height; };
  inline double xmin() const { return pos.x; };
  inline double ymin() const { return pos.y; };
  inline double width() const { return size.width; };
  inline double height() const { return size.height; };

  inline double xcenter() const { return pos.x + size.width/2; }
  inline double ycenter() const { return pos.y + size.height/2; }
  
  inline void set_xmin(double x) { size.width+= x - pos.x; pos.x= x; };
  inline void set_ymin(double y) { size.height+= y - pos.y; pos.y= y; };
  inline void set_xmax(double x) { size.width= x - pos.x; };
  inline void set_ymax(double y) { size.height= y - pos.y; };

  inline Point center() const { return Point(xcenter(), ycenter()); }
  
  inline Point top_left() const { return Point(xmin(), ymin()); }
  inline Point top_right() const { return Point(xmax(), ymin()); }
  inline Point bottom_left() const { return Point(xmin(), ymax()); }
  inline Point bottom_right() const { return Point(xmax(), ymax()); }
  
  inline bool operator==(const Rect &r) const { return r.pos == pos && r.size == size; };
  inline bool operator!=(const Rect &r) const { return r.pos != pos || r.size != size; };

  //inline Rect round() const { Rect r; r.pos= pos.round(); r.size= size.round(); return r; };
  
  inline std::string str() const { char buf[40]; snprintf(buf, sizeof(buf), "{%.2f,%.2f  %.2fx%.2f}", pos.x, pos.y, size.width, size.height); return buf; };
};


struct HSVColor;

struct MYSQLCANVAS_PUBLIC_FUNC Color {
  double r, g, b, a;

  Color() : r(0), g(0), b(0), a(1) {};
  Color(double ar, double ag, double ab, double aa=1.0) : r(ar), g(ag), b(ab), a(aa) {};
  Color(const HSVColor &hsv);

  bool operator !=(const Color &other) { return r != other.r || g != other.g || b != other.b || a != other.a; }

  static Color parse(const std::string &color);
  
  static inline Color Black() { return Color(0,0,0); }
  static inline Color White() { return Color(1,1,1); }  
};


struct MYSQLCANVAS_PUBLIC_FUNC HSVColor {
  int h; // 0 ~ 360
  double s, v, a; // 0 ~ 1.0

  HSVColor() : h(0), s(0), v(0), a(1) {};
  HSVColor(int ah, double as, double av, double aa=1.0) : h(ah), s(as), v(av), a(aa) {};
  HSVColor(const Color &rgb);
};



enum FontSlant {
  SNormal= CAIRO_FONT_SLANT_NORMAL,
  SOblique= CAIRO_FONT_SLANT_OBLIQUE,
  SItalic= CAIRO_FONT_SLANT_ITALIC
};

enum FontWeight {
  WNormal= CAIRO_FONT_WEIGHT_NORMAL,
  WBold= CAIRO_FONT_WEIGHT_BOLD
};

struct MYSQLCANVAS_PUBLIC_FUNC FontSpec {
  std::string family;
  FontSlant slant;
  FontWeight weight;
  float size;

  inline FontSpec &operator= (const FontSpec &font) 
  {
    family= font.family;
    slant= font.slant;
    weight= font.weight;
    size= font.size;
    
    return *this;
  }

  inline bool operator != (const FontSpec &font) const
  {
    return (family != font.family || slant != font.slant || weight != font.weight) || size != font.size;
  }

  inline bool operator == (const FontSpec &font) const
  {
    return (family == font.family && slant == font.slant && weight == font.weight && size == font.size);
  }

  FontSpec(const FontSpec &other)
    : family(other.family), slant(other.slant), weight(other.weight), size(other.size) {}

  FontSpec() : family(DEFAULT_FONT_FACE), slant(SNormal), weight(WNormal), size(12) {}

  FontSpec(const std::string &afamily, FontSlant aslant=SNormal, FontWeight aweight=WNormal, float asize= 12.0)
    : family(afamily), slant(aslant), weight(aweight), size(asize) {}

  void toggle_bold(bool flag) { weight= flag ? WBold : WNormal; }
  void toggle_italic(bool flag) { slant= flag ? SItalic : SNormal; }
};


class canvas_error : public std::runtime_error
{
public:
  canvas_error(const std::string &msg) : std::runtime_error(msg) {};
};




class MYSQLCANVAS_PUBLIC_FUNC Surface
{
protected:
  cairo_surface_t *surface;

  Surface() : surface(0) {}

public:
  Surface(const Surface &other);
  Surface(cairo_surface_t *surface);

  virtual ~Surface();

  Surface &operator= (const Surface &s)
  {
    Surface tmp(s);

    std::swap(*this, tmp);

    return *this;
  }

  cairo_surface_t *get_surface() const { return surface; }
};


class MYSQLCANVAS_PUBLIC_FUNC PDFSurface : public Surface
{
public:
  PDFSurface(const std::string &path, double width, double height);
};


class MYSQLCANVAS_PUBLIC_FUNC PSSurface : public Surface
{
public:
  PSSurface(const std::string &path, double width, double height);
};


class MYSQLCANVAS_PUBLIC_FUNC ImageSurface : public Surface
{
public:
  ImageSurface(double width, double height, cairo_format_t format);
};


#ifdef _WIN32
class MYSQLCANVAS_PUBLIC_FUNC Win32Surface : public Surface
{
public:
  Win32Surface(HDC hdc, bool printing= false);
};
#endif


class FontManager;

struct MYSQLCANVAS_PUBLIC_FUNC CairoCtx 
{
private:
  cairo_t *cr;

  FontManager *fm;
  
public:
  CairoCtx(cairo_surface_t *surf);
  CairoCtx(const Surface &surf);
  ~CairoCtx();

  void check_state() const;


  inline cairo_t *get_cr() { return cr; }

  inline void save() const { cairo_save(cr); check_state(); }
  inline void restore() const { cairo_restore(cr); check_state(); }
  inline void show_page() { cairo_show_page(cr); }

  inline void translate(const Point &p) { cairo_translate(cr, p.x, p.y); }
  inline void translate(double x, double y) { cairo_translate(cr, x, y); }
  inline void scale(const Point &p) { cairo_scale(cr, p.x, p.y); }
  inline void scale(double x, double y) { cairo_scale(cr, x, y); }
  inline void rotate(double rad) { cairo_rotate(cr, rad); }

  inline void set_line_width(double width) { cairo_set_line_width(cr, width); }
  inline void set_line_cap(cairo_line_cap_t t) { cairo_set_line_cap(cr, t); }
  inline void set_line_join(cairo_line_join_t t) { cairo_set_line_join(cr, t); }
  inline void set_miter_limit(double l) { cairo_set_miter_limit(cr, l); }

  inline void set_dash(double dashes[], int ndashes, double offset) { cairo_set_dash(cr, dashes, ndashes, offset); }
  inline void set_operator(cairo_operator_t oper) { cairo_set_operator(cr, oper); }

  inline void set_color(const Color &color) const
  {
    if (color.a==1.0) 
      cairo_set_source_rgb(cr, color.r, color.g, color.b);
    else 
      cairo_set_source_rgba(cr, color.r, color.g, color.b, color.a);
  }
  
  inline void set_color(const Color &color, double alpha) const
  {
    cairo_set_source_rgba(cr, color.r, color.g, color.b, alpha);
  }

  void set_font(const FontSpec &font) const;
  void get_text_extents(const FontSpec &font, const std::string &text,
                        cairo_text_extents_t &extents);
  void get_text_extents(const FontSpec &font, const char *text,
                        cairo_text_extents_t &extents);

  inline void set_source_surface(cairo_surface_t *srf, double x, double y) {
    cairo_set_source_surface(cr, srf, x, y);
  }

  inline void set_mask(cairo_pattern_t *pat) {
    cairo_mask(cr, pat);
  }

  inline void set_mask_surface(cairo_surface_t *surf, double x, double y) {
    cairo_mask_surface(cr, surf, x, y);
  }
  
  inline void set_pattern(cairo_pattern_t *pat) { cairo_set_source(cr, pat); }

  inline void paint() { cairo_paint(cr); }
  inline void paint_with_alpha(double a) { cairo_paint_with_alpha(cr, a); }

  inline void clip() { cairo_clip(cr); }

  inline void stroke() { cairo_stroke(cr); }
  inline void fill() { cairo_fill(cr); }
  inline void stroke_preserve() { cairo_stroke_preserve(cr); }
  inline void fill_preserve() { cairo_fill_preserve(cr); }

  inline void move_to(const Point &pt) { cairo_move_to(cr, pt.x, pt.y); }
  inline void move_to(double x, double y) { cairo_move_to(cr, x, y); }

  inline void line_to(const Point &pt) { cairo_line_to(cr, pt.x, pt.y); }
  inline void line_to(double x, double y) { cairo_line_to(cr, x, y); }

  inline void arc(double cx, double cy, double r, double start, double end)
  {
    cairo_arc(cr, cx, cy, r, start, end); 
  }
  
  inline void show_text(const std::string &text) { cairo_show_text(cr, text.c_str()); }
  
  inline void new_path() { cairo_new_path(cr); }
  inline void close_path() { cairo_close_path(cr); }

  inline void rectangle(const Rect &rect) { cairo_rectangle(cr, rect.xmin(), rect.ymin(), rect.width(), rect.height()); }
  inline void rectangle(double x, double y, double w, double h) { cairo_rectangle(cr, x, y, w, h); }
};



#define DOUBLE_CLICK_DELAY 0.400

MYSQLCANVAS_PUBLIC_FUNC Timestamp get_time();

cairo_status_t write_to_surface(void *closure, const unsigned char *data, unsigned int length);

class MYSQLCANVAS_PUBLIC_FUNC FileHandle
{
  FILE *_file;
public:
  FileHandle() : _file(NULL) {}
  FileHandle(const char *filename, const char *mode, bool throw_on_fail= true) : _file(NULL) { fopen(filename, mode, throw_on_fail); }
  FileHandle(FILE *file) : _file(file) {}
  FileHandle(FileHandle &fh) : _file(NULL) { *this= fh; }
  ~FileHandle() { dispose(); }
  operator FILE *() { return _file; }
  operator bool() { return (!_file); }
  FileHandle & operator =(FileHandle &fh);
  FILE * operator ->() { return _file; }
  FILE * file() { return _file; }
  void dispose();
  FILE * fopen(const char *filename, const char *mode, bool throw_on_fail= true);
};

END_MDC_DECLS

#endif /* _MDC_COMMON_H_ */
