// $Id$
//
//  Copyright (C) 2012 Greg Landrum
//
//   @@ All Rights Reserved @@
//  This file is part of the RDKit.
//  The contents are covered by the terms of the BSD license
//  which is included in the file license.txt, found at the root
//  of the RDKit source tree.
//

/*
  To use include this in your CPPFLAGS:
      $(shell pkg-config --cflags cairo)
  and this in your LDFLAGS:
      $(shell pkg-config --libs cairo)  
*/ 

#ifndef _RD_DRAWING_TO_CAIRO_H_
#define _RD_DRAWING_TO_CAIRO_H_

#include <GraphMol/MolDrawing/MolDrawing.h>
#include <cairo.h>

namespace RDKit {
  namespace Drawing {
    namespace {
      void setColorCairo(int atNum,cairo_t *cr){
        PRECONDITION(cr,"no context");
        switch(atNum){
        case 7:
          cairo_set_source_rgb (cr, 0.0, 0.0, 1.0);
          break;
        case 8:
          cairo_set_source_rgb (cr, 1.0, 0.0, 0.0);
          break;
        case 9:
          cairo_set_source_rgb (cr, 0.2, 0.8, 0.8);
          break;
        case 15:
          cairo_set_source_rgb (cr, 1.0, 0.5, 0.0);
          break;
        case 16:
          cairo_set_source_rgb (cr, 0.8, 0.8, 0.0);
          break;
        case 17:
          cairo_set_source_rgb (cr, 0.0, 0.8, 0.0);
          break;
        case 35:
          cairo_set_source_rgb (cr, 0.5, 0.3, 0.1);
          break;
        case 0:
          cairo_set_source_rgb (cr, 0.5, 0.5, 0.5);
          break;
        default:
          cairo_set_source_rgb (cr, 0.0, 0.0, 0.0);
        }
      }
      void drawLineCairo(std::vector<int>::const_iterator &pos,
                         cairo_t *cr){
        PRECONDITION(cr,"no context");
        int width=*pos++;
        cairo_set_line_width(cr,width*10);
        int dashed=*pos++;
        double dashes[2];
        switch(dashed){
        case 0:
          cairo_set_dash(cr,0,0,0);
          break;
        case 2:
          dashes[0]=5.0;dashes[1]=20.0;
          cairo_set_dash(cr,dashes,sizeof(dashes)/sizeof(dashes[0]),0);    
          break;
        default:
          dashes[0]=20.0;dashes[1]=20.0;
          cairo_set_dash(cr,dashes,sizeof(dashes)/sizeof(dashes[0]),0);    
        }
        int an1=*pos++;
        int an2=*pos++;
        int xp1 = *pos++;
        int yp1 = *pos++;
        int xp2 = *pos++;
        int yp2 = *pos++;
        if(an1==an2){
          setColorCairo(an1,cr);
          cairo_move_to(cr,xp1,yp1);
          cairo_line_to(cr,xp2,yp2);
          cairo_stroke(cr);
        } else {
          int mx = xp1+(xp2-xp1)/2;
          int my = yp1+(yp2-yp1)/2;

          setColorCairo(an1,cr);
          cairo_move_to(cr,xp1,yp1);
          cairo_line_to(cr,mx,my);
          cairo_stroke(cr);
          setColorCairo(an2,cr);
          cairo_move_to(cr,mx,my);
          cairo_line_to(cr,xp2,yp2);
          cairo_stroke(cr);
        }
      }
      void drawAtomCairo(std::vector<int>::const_iterator &pos,
                         cairo_t *cr){
        PRECONDITION(cr,"no context");
        int fontSz=50;
        int atNum=*pos++;
        double xp=static_cast<double>(*pos++);
        double yp=static_cast<double>(*pos++);
        int slen=*pos++;
        std::string label="";
        for(unsigned int i=0;i<slen;++i){
          label+=(char)*pos++;
        }
        RDKit::Drawing::OrientType orient=static_cast<RDKit::Drawing::OrientType>(*pos++);
        if(label.length()){
          cairo_text_extents_t extents;
          cairo_text_extents(cr,label.c_str(),&extents);
          double twidth=extents.width,theight=extents.height;

          switch(orient){
          case RDKit::Drawing::W:
            xp -= twidth;
            yp += theight/2;
            break;
          case RDKit::Drawing::E:
            yp += theight/2;
            break;
          case RDKit::Drawing::S:
            xp -= twidth/2;
            yp += theight;
            break;
          case RDKit::Drawing::N:
            xp -= twidth/2;
            yp -= theight/2;
            break;
          default:
            xp -= twidth/2;
            yp += theight/2;
          }
          cairo_set_source_rgb (cr, 1.0, 1., 1.);
          cairo_rectangle(cr,
                          xp-10,yp-theight-10,
                          twidth+20,theight+20);
          cairo_fill(cr);
                  

          cairo_move_to(cr,xp,yp);
          setColorCairo(atNum,cr);
          cairo_show_text(cr,label.c_str());
          cairo_stroke(cr);
        }
      }
    } // end of anonymous namespace 


    void DrawingToCairo(const std::vector<int> &drawing,cairo_t *cr,
                        int width,int height,
                        int fontSize=12,int maxDotsPerAngstrom=60){
      PRECONDITION(cr,"no context");
      PRECONDITION(width>0 && height>0,"bad dimensions");

      cairo_set_source_rgb (cr, 1.0, 1.0, 1.0);
      cairo_rectangle(cr,0,0,width,height);
      cairo_fill(cr);

      cairo_select_font_face (cr, "sans", CAIRO_FONT_SLANT_NORMAL, CAIRO_FONT_WEIGHT_NORMAL);

      std::vector<int>::const_iterator pos=drawing.begin();
      int resolution=0;
      if(*pos!=Drawing::RESOLUTION){
        std::cerr<<"no RESOLUTION token found"<<std::endl;
        return;
      }
      resolution=*(pos+1);
      PRECONDITION(resolution>0,"bad resolution");
      pos+=2;
      if(*pos!=Drawing::BOUNDS){
        std::cerr<<"no bounds token found"<<std::endl;
        return;
      }
      pos+=3;
      int dwidth,dheight;
      dwidth = *pos++;
      dheight = *pos++;
      PRECONDITION(dwidth>0 && dheight>0,"bad dimensions");

      // size of the image in angstroms:
      double xSize,ySize;
      xSize=static_cast<double>(dwidth)/resolution;
      ySize=static_cast<double>(dheight)/resolution;
      double scale=1.0;

      if(dwidth>width || dheight>height){
        if(static_cast<double>(dwidth)/width > static_cast<double>(dheight)/height){
          scale = static_cast<double>(width)/dwidth;
        } else {
          scale = static_cast<double>(height)/dheight;
        }
      } else {
        if(width/xSize > height/ySize){
          if( width/xSize > maxDotsPerAngstrom ){
            scale = maxDotsPerAngstrom*xSize/width;
          }
        } else {
          if( height/ySize > maxDotsPerAngstrom ){
            scale = maxDotsPerAngstrom*ySize/height;
          }
        }
      }
  
      scale*=0.80;
      cairo_translate(cr,
                      .5*(width-dwidth*scale),
                      .5*(height-dheight*scale));
      cairo_scale(cr,scale,scale);

      // scaling factors here are empirically determined
      double dFontSize=1.5*maxDotsPerAngstrom*fontSize/14;
      cairo_set_font_size (cr, dFontSize);

      while(pos!=drawing.end()){
        int token=*pos++;
        switch(token){
        case Drawing::LINE:
          drawLineCairo(pos,cr);
          break;
        case Drawing::ATOM:
          drawAtomCairo(pos,cr);
          break;
        default:
          std::cerr<<"unrecognized token: "<<token<<std::endl;
        }
      }
    }
  } // end of namespace Drawing
} // end of namespace RDKit
#endif
