//
//  Copyright (C) 2015 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.
//
// derived from Dave Cosgrove's MolDraw2D
//

#include "MolDraw2DSVG.h"
#include <GraphMol/MolDraw2D/MolDraw2DDetails.h>
#include <sstream>

namespace RDKit {
  namespace {
    std::string DrawColourToSVG(const DrawColour &col){
      const char *convert="0123456789ABCDEF";
      std::string res(7,' ');
      res[0]='#';
      unsigned int v;
      unsigned int i=1;
      v=int(255 * col.get<0>());
      res[i++] = convert[v/16];
      res[i++] = convert[v%16];
      v=int(255 * col.get<1>());
      res[i++] = convert[v/16];
      res[i++] = convert[v%16];
      v=int(255 * col.get<2>());
      res[i++] = convert[v/16];
      res[i++] = convert[v%16];
      return res;
    }
  }
  
  void MolDraw2DSVG::initDrawing() {
    d_os<<"<?xml version='1.0' encoding='iso-8859-1'?>\n";
    d_os << "<svg:svg version='1.1' baseProfile='full'\n      \
        xmlns:svg='http://www.w3.org/2000/svg'\n              \
        xmlns:rdkit='http://www.rdkit.org/xml'\n              \
        xmlns:xlink='http://www.w3.org/1999/xlink'\n          \
        xml:space='preserve'\n";
    d_os<<"width='"<<width()<<"px' height='"<<height()<<"px' >\n";
    //d_os<<"<svg:g transform='translate("<<width()*.05<<","<<height()*.05<<") scale(.85,.85)'>";
  }

  // ****************************************************************************
  void MolDraw2DSVG::finishDrawing() {
    //d_os << "</svg:g>";
    d_os << "</svg:svg>\n";
  }

  // ****************************************************************************
  void MolDraw2DSVG::setColour( const DrawColour &col ) {
    MolDraw2D::setColour( col );
  }

  // ****************************************************************************
  void MolDraw2DSVG::drawLine( const Point2D &cds1 ,
                               const Point2D &cds2 ) {

    Point2D c1 = getDrawCoords( cds1 );
    Point2D c2 = getDrawCoords( cds2 );
    std::string col=DrawColourToSVG(colour());
    unsigned int width=lineWidth();
    std::string dashString="";
    const DashPattern &dashes=dash();
    if(dashes.size()){
      std::stringstream dss;
      dss<<";stroke-dasharray:";
      std::copy(dashes.begin(),dashes.end()-1,std::ostream_iterator<unsigned int>(dss,","));
      dss<<dashes.back();
      dashString = dss.str();
    }
    d_os<<"<svg:path ";
    d_os<< "d='M " << c1.x << "," << c1.y << " " << c2.x << "," << c2.y << "' ";
    d_os<<"style='fill:none;fill-rule:evenodd;stroke:"<<col<<";stroke-width:"<<width<<"px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"<<dashString<<"'";
    d_os<<" />\n";
  }

  // ****************************************************************************
  // draw the char, with the bottom left hand corner at cds
  void MolDraw2DSVG::drawChar( char c , const Point2D &cds ) {
    unsigned int fontSz=scale()*fontSize();
    std::string col = DrawColourToSVG(colour());

    d_os<<"<svg:text";
    d_os<<" x='" << cds.x;
    // doesn't seem like the inclusion of the fontSz should be necessary, but vertical text alignment seems impossible
    // The 0.9 is an empirical factor to account for the descender on the font.
    d_os<< "' y='" << cds.y + 0.9*fontSz <<"'"; 
    d_os<<" style='font-size:"<<fontSz<<"px;font-style:normal;font-weight:normal;fill-opacity:1;stroke:none;font-family:sans-serif;text-anchor:start;"<<"fill:"<<col<<"'";
    d_os<<" >";
    d_os<<c;
    d_os<<"</svg:text>";
  }

  // ****************************************************************************
  void MolDraw2DSVG::drawPolygon( const std::vector< Point2D > &cds ){
    PRECONDITION(cds.size()>=3,"must have at least three points");

    std::string col=DrawColourToSVG(colour());
    unsigned int width=lineWidth();
    std::string dashString="";
    d_os<<"<svg:path ";
    d_os<<"d='M";
    Point2D c0 = getDrawCoords( cds[0] );
    d_os << " " << c0.x << "," << c0.y;
    for(unsigned int i=1;i<cds.size();++i){
      Point2D ci = getDrawCoords( cds[i] );
      d_os << " " << ci.x << "," << ci.y;
    }
    d_os << " " << c0.x << "," << c0.y;
    d_os<<"' style='";
    if(fillPolys())
      d_os<<"fill:"<<col<<";fill-rule:evenodd";
    else
      d_os<<"fill:none;";

    d_os<<"stroke:"<<col<<";stroke-width:"<<width<<"px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"<<dashString<<"'";
    d_os<<" />\n";

  }

  void MolDraw2DSVG::drawEllipse( const Point2D &cds1 ,
                                  const Point2D &cds2 ) {
    Point2D c1 = getDrawCoords( cds1 );
    Point2D c2 = getDrawCoords( cds2 );
    double w = c2.x - c1.x;
    double h = c2.y - c1.y;
    double cx=c1.x + w/2;
    double cy=c1.y + h/2;
    w = w>0 ? w : -1*w;
    h = h>0 ? h : -1*h;
    
    std::string col=DrawColourToSVG(colour());
    unsigned int width=lineWidth();
    std::string dashString="";
    d_os<<"<svg:ellipse" <<" cx='"<<cx<<"'" <<" cy='"<<cy<<"'" <<" rx='"<<w/2<<"'" <<" ry='"<<h/2<<"'";

    d_os<<" style='";
    if(fillPolys())
      d_os<<"fill:"<<col<<";fill-rule:evenodd";
    else
      d_os<<"fill:none;";

    d_os<<"stroke:"<<col<<";stroke-width:"<<width<<"px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"<<dashString<<"'";
    d_os<<" />\n";
  }
  


  // ****************************************************************************
  void MolDraw2DSVG::clearDrawing() {
    d_os << "<svg:rect";
    d_os << " style='opacity:1.0;fill:#ffffff;stroke:none'";
    d_os << " width='" << width() << "' height='" << height() << "'";
    d_os << " x='0' y='0'";
    d_os << "> </svg:rect>\n";
  }


  
  // ****************************************************************************
  void MolDraw2DSVG::setFontSize( double new_size ) {
    MolDraw2D::setFontSize( new_size );
    double font_size_in_points = fontSize() * scale();
  }

  // ****************************************************************************
  // using the current scale, work out the size of the label in molecule coordinates
  void MolDraw2DSVG::getStringSize( const std::string &label , double &label_width ,
                                    double &label_height ) const {

    label_width = 0.0;
    label_height = 0.0;

    int draw_mode = 0; // 0 for normal, 1 for superscript, 2 for subscript

    bool had_a_super = false;

    for( int i = 0 , is = label.length() ; i < is ; ++i ) {

      // setStringDrawMode moves i along to the end of any <sub> or <sup>
      // markup
      if( '<' == label[i] && setStringDrawMode( label , draw_mode , i ) ) {
        continue;
      }

      label_height = fontSize();
      double char_width = fontSize() * static_cast<double>(MolDraw2D_detail::char_widths[label[i]]) / MolDraw2D_detail::char_widths['M'];
      if( 2 == draw_mode ) {
        char_width *= 0.75;
      } else if( 1 == draw_mode ) {
        char_width *= 0.75;
        had_a_super = true;
      }
      label_width += char_width;

    }

    // subscript keeps its bottom in line with the bottom of the bit chars,
    // superscript goes above the original char top by a bit (empirical)
    if( had_a_super ) {
      label_height *= 1.1;
    }
  }

  // ****************************************************************************
  // draws the string centred on cds
  void MolDraw2DSVG::drawString( const std::string &str ,
                                 const Point2D &cds ) {

    unsigned int fontSz=scale()*fontSize();
    std::string col = DrawColourToSVG(colour());

    double string_width , string_height;
    getStringSize( str , string_width , string_height );

    double draw_x = cds.x - string_width / 2.0;
    double draw_y = cds.y - string_height / 2.0;
    Point2D draw_coords = getDrawCoords(Point2D(draw_x,draw_y));

    d_os<<"<svg:text";
    d_os<<" x='" << draw_coords.x;
    
    // doesn't seem like the inclusion of the fontSz should be necessary, but vertical text alignment seems impossible
    // The 0.9 is an empirical factor to account for the descender on the font.
    d_os<< "' y='" << draw_coords.y + 0.9*fontSz <<"'"; 

    d_os<<" style='font-size:"<<fontSz<<"px;font-style:normal;font-weight:normal;fill-opacity:1;stroke:none;font-family:sans-serif;text-anchor:start;"<<"fill:"<<col<<"'";
    d_os<<" >";

    int draw_mode = 0; // 0 for normal, 1 for superscript, 2 for subscript
    std::string span;
    bool first_span=true;
    for( int i = 0 , is = str.length() ; i < is ; ++i ) {
      // setStringDrawMode moves i along to the end of any <sub> or <sup>
      // markup
      if( '<' == str[i] && setStringDrawMode( str , draw_mode , i ) ) {
        if(!first_span){
          d_os<<span<<"</svg:tspan>";
          span="";
        }
        first_span=false;
        d_os<<"<svg:tspan";
        switch(draw_mode){
        case 1:
          d_os<<" style='baseline-shift:super;font-size:"<<fontSz*0.75<<"px;"<< "'"; break;
        case 2:
          d_os<<" style='baseline-shift:sub;font-size:"<<fontSz*0.75<<"px;"<< "'"; break;
        default:
          break;
        }
        d_os<<">";
        continue;
      }
      if(first_span){
        first_span=false;
        d_os<<"<svg:tspan>";
        span="";
      }
      span += str[i];
    }
    d_os<<span<<"</svg:tspan>";
    d_os<<"</svg:text>\n";
  }

  void MolDraw2DSVG::tagAtoms( const ROMol &mol ){
    PRECONDITION(d_os,"no output stream");
    ROMol::VERTEX_ITER this_at , end_at;
    boost::tie( this_at , end_at ) = mol.getVertices();
    while( this_at != end_at ) {
      int this_idx = mol[*this_at]->getIdx();
      ++this_at;
      Point2D pos=getDrawCoords(atomCoords()[this_idx]);
      std::string lbl=atomSyms()[this_idx].first;
      
      d_os << "<rdkit:atom"<<" idx=\""<<this_idx+1<<"\"";
      if(lbl!=""){
        d_os<< " label=\"" << lbl<< "\"";
      }
      d_os << " x=\""<<pos.x<<"\"" <<" y=\""<<pos.y<<"\""<< " />"<<std::endl;
    }    
  }
} // EO namespace RDKit
