#include <cstring>
#include "stdafx.h"

#include <string.h>

#ifndef  _WIN32
#include <sys/time.h>
#include <time.h>
#else
#include <cairo-win32.h>
#endif

#include <cairo-ps.h>
#include <cairo-pdf.h>

#include "mdc_common.h"
/**
 * @file  mdc_common.cpp
 * @brief 
 */

using namespace mdc;



typedef struct
{
  const char* name;
  unsigned char color[4];
} ColorName;

static ColorName colors[] =
{
  {"aliceblue",            {240, 248, 255, 255}},
  {"antiquewhite",         {250, 235, 215, 255}},
  {"aqua",                 {  0, 255, 255, 255}},
  {"aquamarine",           {127, 255, 212, 255}},
  {"azure",                {240, 255, 255, 255}},
  {"beige",                {245, 245, 220, 255}},
  {"bisque",               {255, 228, 196, 255}},
  {"black",                {  0,   0,   0, 255}},
  {"blanchedalmond",       {255, 235, 205, 255}},
  {"blue",                 {  0,   0, 255, 255}},
  {"blueviolet",           {138,  43, 226, 255}},
  {"brown",                {165,  42,  42, 255}},
  {"burlywood",            {222, 184, 135, 255}},
  {"cadetblue",            { 95, 158, 160, 255}},
  {"chartreuse",           {127, 255,   0, 255}},
  {"chocolate",            {210, 105,  30, 255}},
  {"coral",                {255, 127,  80, 255}},
  {"cornflowerblue",       {100, 149, 237, 255}},
  {"cornsilk",             {255, 248, 220, 255}},
  {"crimson",              {220,  20,  60, 255}},
  {"cyan",                 {  0, 255, 255, 255}},
  {"darkblue",             {  0,   0, 139, 255}},
  {"darkcyan",             {  0, 139, 139, 255}},
  {"darkgoldenrod",        {184, 134,  11, 255}},
  {"darkgray",             {169, 169, 169, 255}},
  {"darkgreen",            {  0, 100,   0, 255}},
  {"darkgrey",             {169, 169, 169, 255}},
  {"darkkhaki",            {189, 183, 107, 255}},
  {"darkmagenta",          {139,   0, 139, 255}},
  {"darkolivegreen",       { 85, 107,  47, 255}},
  {"darkorange",           {255, 140,   0, 255}},
  {"darkorchid",           {153,  50, 204, 255}},
  {"darkred",              {139,   0,   0, 255}},
  {"darksalmon",           {233, 150, 122, 255}},
  {"darkseagreen",         {143, 188, 143, 255}},
  {"darkslateblue",        { 72,  61, 139, 255}},
  {"darkslategray",        { 47,  79,  79, 255}},
  {"darkslategrey",        { 47,  79,  79, 255}},
  {"darkturquoise",        {  0, 206, 209, 255}},
  {"darkviolet",           {148,   0, 211, 255}},
  {"deeppink",             {255,  20, 147, 255}},
  {"deepskyblue",          {  0, 191, 255, 255}},
  {"dimgray",              {105, 105, 105, 255}},
  {"dimgrey",              {105, 105, 105, 255}},
  {"dodgerblue",           { 30, 144, 255, 255}},
  {"firebrick",            {178,  34,  34, 255}},
  {"floralwhite",          {255, 250, 240, 255}},
  {"forestgreen",          { 34, 139,  34, 255}},
  {"fuchsia",              {255,   0, 255, 255}},
  {"gainsboro",            {220, 220, 220, 255}},
  {"ghostwhite",           {248, 248, 255, 255}},
  {"gold",                 {255, 215,   0, 255}},
  {"goldenrod",            {218, 165,  32, 255}},
  {"gray",                 {128, 128, 128, 255}},
  {"grey",                 {128, 128, 128, 255}},
  {"green",                {  0, 128,   0, 255}},
  {"greenyellow",          {173, 255,  47, 255}},
  {"honeydew",             {240, 255, 240, 255}},
  {"hotpink",              {255, 105, 180, 255}},
  {"indianred",            {205,  92,  92, 255}},
  {"indigo",               { 75,   0, 130, 255}},
  {"ivory",                {255, 255, 240, 255}},
  {"khaki",                {240, 230, 140, 255}},
  {"lavender",             {230, 230, 250, 255}},
  {"lavenderblush",        {255, 240, 245, 255}},
  {"lawngreen",            {124, 252,   0, 255}},
  {"lemonchiffon",         {255, 250, 205, 255}},
  {"lightblue",            {173, 216, 230, 255}},
  {"lightcoral",           {240, 128, 128, 255}},
  {"lightcyan",            {224, 255, 255, 255}},
  {"lightgoldenrodyellow", {250, 250, 210, 255}},
  {"lightgray",            {211, 211, 211, 255}},
  {"lightgreen",           {144, 238, 144, 255}},
  {"lightgrey",            {211, 211, 211, 255}},
  {"lightpink",            {255, 182, 193, 255}},
  {"lightsalmon",          {255, 160, 122, 255}},
  {"lightseagreen",        { 32, 178, 170, 255}},
  {"lightskyblue",         {135, 206, 250, 255}},
  {"lightslategray",       {119, 136, 153, 255}},
  {"lightslategrey",       {119, 136, 153, 255}},
  {"lightsteelblue",       {176, 196, 222, 255}},
  {"lightyellow",          {255, 255, 224, 255}},
  {"lime",                 {  0, 255,   0, 255}},
  {"limegreen",            { 50, 205,  50, 255}},
  {"linen",                {250, 240, 230, 255}},
  {"magenta",              {255,   0, 255, 255}},
  {"maroon",               {128,   0,   0, 255}},
  {"mediumaquamarine",     {102, 205, 170, 255}},
  {"mediumblue",           {  0,   0, 205, 255}},
  {"mediumorchid",         {186,  85, 211, 255}},
  {"mediumpurple",         {147, 112, 219, 255}},
  {"mediumseagreen",       { 60, 179, 113, 255}},
  {"mediumslateblue",      {123, 104, 238, 255}},
  {"mediumspringgreen",    {  0, 250, 154, 255}},
  {"mediumturquoise",      { 72, 209, 204, 255}},
  {"mediumvioletred",      {199,  21, 133, 255}},
  {"midnightblue",         { 25,  25, 112, 255}},
  {"mintcream",            {245, 255, 250, 255}},
  {"mistyrose",            {255, 228, 225, 255}},
  {"moccasin",             {255, 228, 181, 255}},
  {"navajowhite",          {255, 222, 173, 255}},
  {"navy",                 {  0,   0, 128, 255}},
  {"oldlace",              {253, 245, 230, 255}},
  {"olive",                {128, 128,   0, 255}},
  {"olivedrab",            {107, 142,  35, 255}},
  {"orange",               {255, 165,   0, 255}},
  {"orangered",            {255,  69  , 0, 255}},
  {"orchid",               {218, 112, 214, 255}},
  {"palegoldenrod",        {238, 232, 170, 255}},
  {"palegreen",            {152, 251, 152, 255}},
  {"paleturquoise",        {175, 238, 238, 255}},
  {"palevioletred",        {219, 112, 147, 255}},
  {"papayawhip",           {255, 239, 213, 255}},
  {"peachpuff",            {255, 218, 185, 255}},
  {"peru",                 {205, 133,  63, 255}},
  {"pink",                 {255, 192, 203, 255}},
  {"plum",                 {221, 160, 221, 255}},
  {"powderblue",           {176, 224, 230, 255}},
  {"purple",               {128,   0, 128, 255}},
  {"red",                  {255,   0,   0, 255}},
  {"rosybrown",            {188, 143, 143, 255}},
  {"royalblue",            { 65, 105, 225, 255}},
  {"saddlebrown",          {139,  69,  19, 255}},
  {"salmon",               {250, 128, 114, 255}},
  {"sandybrown",           {244, 164,  96, 255}},
  {"seagreen",             { 46, 139,  87, 255}},
  {"seashell",             {255, 245, 238, 255}},
  {"sienna",               {160,  82,  45, 255}},
  {"silver",               {192, 192, 192, 255}},
  {"skyblue",              {135, 206, 235, 255}},
  {"slateblue",            {106,  90, 205, 255}},
  {"slategray",            {112, 128, 144, 255}},
  {"slategrey",            {112, 128, 144, 255}},
  {"snow",                 {255, 250, 250, 255}},
  {"springgreen",          {  0, 255, 127, 255}},
  {"steelblue",            { 70, 130, 180, 255}},
  {"tan",                  {210, 180, 140, 255}},
  {"teal",                 {  0, 128, 128, 255}},
  {"thistle",              {216, 191, 216, 255}},
  {"tomato",               {255,  99,  71, 255}},
  {"turquoise",            { 64, 224, 208, 255}},
  {"violet",               {238, 130, 238, 255}},
  {"wheat",                {245, 222, 179, 255}},
  {"white",                {255, 255, 255, 255}},
  {"whitesmoke",           {245, 245, 245, 255}},
  {"yellow",               {255, 255,   0, 255}},
  {"yellowgreen",          {154, 205,  50, 255}}
};



Color Color::parse(const std::string &color)
{
  if (!color.empty())
  {
    if (color[0] == '#')
    {
      int r, g, b;

      // Check first with 3 single values (and only 3), then with 3 double values.
      if (color.size() == 4 && sscanf(color.c_str(), "#%01x%01x%01x", &r, &g, &b) == 3)
        return Color(r * 16 / 255.0, g * 16 / 255.0,  b * 16 / 255.0);
      else
        if (sscanf(color.c_str(), "#%02x%02x%02x", &r, &g, &b) == 3)
          return Color(r/255.0, g/255.0,  b/255.0);
    }
    else
    {
      for (unsigned int i= 0; i < sizeof(colors)/sizeof(ColorName); i++)
      {
        if (strcmp(colors[i].name, color.c_str())==0)
          return Color(colors[i].color[0]/255.0, colors[i].color[1]/255.0,  colors[i].color[2]/255.0,  colors[i].color[3]/255.0);
      }
    }
  }
  return Black();
}



struct ScaledFont
{
  FontSpec spec;
  cairo_scaled_font_t *font;
  cairo_font_face_t *face;
  cairo_font_options_t *options;

  ScaledFont(const FontSpec &aspec, cairo_scaled_font_t *sf, cairo_font_face_t *fc, cairo_font_options_t *opt) 
    : spec(aspec), font(sf), face(fc), options(opt)
  {}

  ScaledFont(const ScaledFont &other)
  {
    spec= other.spec;
    if (other.font)
      font= cairo_scaled_font_reference(other.font);
    else
      font= 0;
    if (other.face)
      face= cairo_font_face_reference(other.face);
    else
      face= 0;
    if (other.options)
      options= cairo_font_options_copy(other.options);
    else
      options= 0;
  }
  ~ScaledFont() 
  { 
    cairo_scaled_font_destroy(font); 
    cairo_font_face_destroy(face);
    cairo_font_options_destroy(options);
  }

  inline ScaledFont &operator =(const ScaledFont &other)
  {
    spec= other.spec;
    if (other.font)
      font= cairo_scaled_font_reference(other.font);
    else
      font= 0;
    if (other.face)
      face= cairo_font_face_reference(other.face);
    else
      face= 0;
    if (other.options)
      options= cairo_font_options_copy(other.options);
    else
      options= 0;    
    return *this;
  }
};


class mdc::FontManager
{
  std::map<std::string, std::list<ScaledFont> > _cache;
  CairoCtx *_cairo;

  cairo_scaled_font_t *lookup(const FontSpec &spec)
  {
    if (_cache.find(spec.family) != _cache.end())
    {
      std::list<ScaledFont> &flist(_cache[spec.family]);

      for (std::list<ScaledFont>::iterator iter= flist.begin();
           iter != flist.end(); ++iter)
      {
        if (iter->spec == spec)
          return iter->font;
      }
    }
    return 0;
  }

  cairo_scaled_font_t *create(const FontSpec &spec)
  {
    cairo_font_face_t *face;
    cairo_scaled_font_t *sfont;
    cairo_matrix_t matrix;
    cairo_matrix_t ctm;
    cairo_font_options_t *options;
    cairo_t *cr= _cairo->get_cr();

#ifdef _DEBUG
    static int i= 0;
    
    i++;
    if (i % 100 == 0)
    {
      fprintf(stderr, "create font %s %i %i %f (%i)\n", spec.family.c_str(), spec.slant, spec.weight, spec.size, i);
    }
#endif
    
#if CAIRO_VERSION_MAJOR == 1 && CAIRO_VERSION_MINOR < 8
    _cairo->save();
    cairo_select_font_face(cr,
                           spec.family.c_str(), 
                           (cairo_font_slant_t)spec.slant,
                           (cairo_font_weight_t)spec.weight);
      
    cairo_set_font_size(cr, spec.size);
    face= cairo_get_font_face(cr);
    cairo_font_face_reference(face);
#else
    face= cairo_toy_font_face_create(spec.family.c_str(), 
                                     (cairo_font_slant_t)spec.slant,
                                     (cairo_font_weight_t)spec.weight);
    cairo_set_font_size(cr, spec.size);
#endif
    if (cairo_font_face_status(face) != CAIRO_STATUS_SUCCESS)
    {
      cairo_font_face_destroy(face);
#if CAIRO_VERSION_MAJOR == 1 && CAIRO_VERSION_MINOR < 8
      _cairo->restore();
#endif
      return 0;
    }
    
    cairo_get_font_matrix(cr, &matrix);
    
    cairo_matrix_init_identity(&ctm);
    
    options= cairo_font_options_create();
    cairo_font_options_set_hint_metrics(options, CAIRO_HINT_METRICS_OFF);
    cairo_font_options_set_hint_style(options, CAIRO_HINT_STYLE_NONE);

    sfont= cairo_scaled_font_create(face, &matrix, &ctm, options);

    if (cairo_scaled_font_status(sfont) != CAIRO_STATUS_SUCCESS)
    {
      cairo_scaled_font_destroy(sfont);
      cairo_font_face_destroy(face);
#if CAIRO_VERSION_MAJOR == 1 && CAIRO_VERSION_MINOR < 8
      _cairo->restore();      
#endif
      return 0;
    }
#if CAIRO_VERSION_MAJOR == 1 && CAIRO_VERSION_MINOR < 8  
    _cairo->restore();
#endif
  
    _cache[spec.family].push_back(ScaledFont(spec, sfont, face, options));

    return sfont;
  }

public:
  FontManager(CairoCtx *cr)
    : _cairo(cr)
  {
  }
  
  cairo_scaled_font_t *get_font(const FontSpec &spec)
  {
    cairo_scaled_font_t *font;

    font= lookup(spec);
    if (!font)
      font= create(spec);

    if (!font)
      font= create(FontSpec("helvetica", SNormal, WNormal, spec.size));
    
    if (!font)
      throw canvas_error("Could not create font");

    return font;
  }
};







Surface::Surface(const Surface &other) : surface(cairo_surface_reference(other.surface)) 
{}


Surface::Surface(cairo_surface_t *surface) : surface(cairo_surface_reference(surface))
{ }

Surface::~Surface() 
{
  if (surface) cairo_surface_destroy(surface); 
}


ImageSurface::ImageSurface(double width, double height, cairo_format_t format)
{
  surface= cairo_image_surface_create(format, (int)width, (int)height);
}


PDFSurface::PDFSurface(const std::string &path, double width, double height)
{
  surface= cairo_pdf_surface_create(path.c_str(), width, height);
}


PSSurface::PSSurface(const std::string &path, double width, double height)
{
  surface= cairo_ps_surface_create(path.c_str(), width, height);
}


#ifdef _WIN32
Win32Surface::Win32Surface(HDC hdc, bool printing)
{
  if (printing)
  {
#if CAIRO_VERSION_MINOR >= 5
    surface= cairo_win32_printing_surface_create(hdc);
    return;
#endif
  }
  surface= cairo_win32_surface_create(hdc);
}
#endif





CairoCtx::CairoCtx(cairo_surface_t *surf)
{
  cairo_status_t st;
  cr= cairo_create(surf);

  if ((st= cairo_status(cr)) != CAIRO_STATUS_SUCCESS)
    throw canvas_error("Error creating cairo context: "+std::string(cairo_status_to_string(st)));

  fm= new FontManager(this);
}


CairoCtx::CairoCtx(const Surface &surf)
{
  cr= cairo_create(surf.get_surface());

  if (cairo_status(cr) != CAIRO_STATUS_SUCCESS)
    throw canvas_error("Error creating cairo context: "+std::string(cairo_status_to_string(cairo_status(cr))));

  fm= new FontManager(this);
}


CairoCtx::~CairoCtx()
{
  if (cr)
    cairo_destroy(cr);
  delete fm;
}


void CairoCtx::check_state() const
{
  cairo_status_t status= cairo_status(cr);
  if (status != CAIRO_STATUS_SUCCESS)
    throw canvas_error("cairo error: "+std::string(cairo_status_to_string(cairo_status(cr))));
}


void CairoCtx::set_font(const FontSpec &font) const
{
  cairo_set_scaled_font(cr, fm->get_font(font));
}



void CairoCtx::get_text_extents(const FontSpec &font, const std::string &text,
                                cairo_text_extents_t &extents)
{  
  cairo_scaled_font_text_extents(fm->get_font(font), text.c_str(), &extents);
}


void CairoCtx::get_text_extents(const FontSpec &font, const char *text,
                                cairo_text_extents_t &extents)
{  
  cairo_scaled_font_text_extents(fm->get_font(font), text, &extents);
}



Timestamp mdc::get_time()
{
#ifdef _WIN32
  unsigned __int64 t= 0;

  GetSystemTimeAsFileTime((FILETIME*)&t);

  return (double)t / 1e+7;
#else
  struct timeval t;

  gettimeofday(&t, NULL);

  return t.tv_sec + t.tv_usec / 1000000.0;
#endif
}


Color::Color(const HSVColor &hsv)
: a(hsv.a)
{
  int h = hsv.h % 360;
  double s = hsv.s;
  double v = hsv.v;
  int i, f;
  double p, q, t;

  if (s == 0) 
  {
    r= g= b= v;
    return;
  }
  i = h / 60;
  f = h % 60;
  p = v * (1.0 - s);
  q = v * (1.0 - s * f / 60.0);
  t = v * (1.0 - s * (60.0 - f) / 60.0);

  switch (i) {
  case 0:
    r= v;
    g= t;
    b = p;
    break;
  case 1:
    r= q;
    g= v;
    b= p;
    break;
  case 2:
    r= p;
    g= v;
    b= t;
    break;
  case 3:
    r= p;
    g= q;
    b= v;
    break;
  case 4:
    r= t;
    g= p;
    b= v;
    break;
  case 5:
    r= v;
    g= p;
    b= q;
    break;
  }
}


#ifndef MIN
#define MIN(a,b)        ((a)<(b) ? (a) : (b))
#endif
#ifndef MAX
#define MAX(a,b)        ((a)>(b) ? (a) : (b))
#endif

#define MIN3(a,b,c)     MIN(MIN(a,b), c)
#define MAX3(a,b,c)     MAX(MAX(a,b), c)

HSVColor::HSVColor(const Color &rgb)
: a(rgb.a)
{
  double max = MAX3(rgb.r, rgb.g, rgb.b);
  double min = MIN3(rgb.r, rgb.g, rgb.b);

  v = max;

  if (max == 0)
    s = 0;
  else
    s = (max - min)/ max;

  if (s == 0)
    h = 0;
  else {
    int rc, gc, bc;

    rc = (int)((max - rgb.r)/ (max - min));
    gc = (int)((max - rgb.g)/ (max - min));
    bc = (int)((max - rgb.b)/ (max - min));

    if (rgb.r == max) {
      h = ((bc - gc) * 60);
    } else if (rgb.g == max) {
      h = 2*60 + ((rc - bc) * 60);
    } else {
      h = 4*60 + ((gc - rc) * 60);
    }
    if (h < 0)
      h += 360;
  }
}


//-----------------
// mdc::fopen
//-----------------
FILE* _fopen(const char *filename, const char *mode)
{
#if defined(__WIN__) || defined(_WIN32) || defined(_WIN64)
  WCHAR wsz_filename[1024+1];
  WCHAR wsz_mode[256];

  //Covert filename from utf8 to WideChar
  MultiByteToWideChar(CP_UTF8, 0, filename,
    (int)strlen(filename)+1, wsz_filename,   
    sizeof(wsz_filename)/sizeof(wsz_filename[0]));

  //Covert mode from utf8 to WideChar
  MultiByteToWideChar(CP_UTF8, 0, mode,
    (int)strlen(mode)+1, wsz_mode,   
    sizeof(wsz_mode)/sizeof(wsz_mode[0]));

  return _wfopen(wsz_filename, wsz_mode);
#else
  FILE *file;
  char * local_filename;

  if (! (local_filename= g_filename_from_utf8(filename,-1,NULL,NULL,NULL)))
    return NULL;

  file= fopen(local_filename, mode);

  g_free(local_filename);

  return file;
#endif
}


//-------------
// mdc::FileHandle
//-------------

FILE * mdc::FileHandle::fopen(const char *filename, const char *mode, bool throw_on_fail)
{
  dispose();
  if (_file && throw_on_fail)
    throw("Can't open file - failed to close previous file.");
  _file= _fopen(filename, mode);
  if (!_file && throw_on_fail)
    throw std::runtime_error(std::string("Failed to open file \"").append(filename).append("\""));
  return _file;
}

mdc::FileHandle & mdc::FileHandle::operator =(FileHandle &fh)
{
  dispose();
  _file= fh._file;
  fh._file= NULL;
  return *this;
}

void mdc::FileHandle::dispose()
{
  if (_file)
  {
    int res= fclose(_file);
    if (EOF == res)
      ;
    else
      _file= NULL;
  }
}


//-----------------
// mdc::write_to_surface
//-----------------
cairo_status_t mdc::write_to_surface(void *closure, const unsigned char *data, unsigned int length)
{
  FILE *file= static_cast <FILE*> (closure);
  size_t res= fwrite(data, sizeof(data[0]), length, file);
  cairo_status_t ret= (res == length) ? CAIRO_STATUS_SUCCESS : CAIRO_STATUS_WRITE_ERROR;
  return ret;
}
