// Vorosweep - Copyright (C) 2010-2014 T. Mouton
//
// See the LICENSE.txt file for license information. Please report all
// bugs and problems to <thibaud.mouton@gmail.com>.

#include "vorosweep.h"
#include "color.h"
#include "Board.h"

using namespace vorosweep;
// using namespace LibBoard;

// make use of libboard (http://libboard.sourceforge.net/) for exporting
// in SVG, EPS or FIG

class PointPtrComp
{
  public:
    bool operator() ( const Point *p0, const Point *p1 ) const
    {
      if ( p0->p[0] != p1->p[0] )
        return p0->p[0] <= p1->p[0];
      else
        return p0->p[1] <= p1->p[1];
    }
};

int potential_cross ( SweepEdge *se, double zpos, double *crashpos );
int compute_pos ( std::list<SweepEdge *>::iterator &it0, std::list<SweepEdge *>::reverse_iterator &it1, double height, double pos[2][3] );
void compute_points ( SweepFacet *sf, int start, double zsteps, SweepEdge *se, std::vector<std::set<Point *, PointPtrComp> > &list );

void Graph::export_vector_graphic ( const char *name, int draw_generator_edges, int draw_isovalues, int use_heatmap, int nbzstep )
{
  if ( ! ( int ) pointlist.size() || ! ( int ) indexmap.size() )
  {
    printf ( "--> You have to call generate_internals before exporting\n" );
    return;
  }

  char filename[100];
  static int count = 0;
  sprintf ( filename, "%s_%d.eps", name, count++ );
  LibBoard::Board board;
  double zmax = 0.0;

  for ( int i = 0; i < ( int ) borderlist.size(); i++ )
  {
    Border *bo = borderlist[i];
    Point *p0 = bo->get_point(0);
    Point *p1 = bo->get_point(1);
    board.setLineWidth ( 0.9 );
    board.setPenColor ( LibBoard::Color::Silver );
    board.drawLine ( p0->p[0], p0->p[1], p1->p[0], p1->p[1], 1 );
  }

  for ( int i = 0; i < ( int ) generatorlist.size(); i++ )
  {
    Convex_generator *cg = generatorlist[i];
    if ( cg->is_active() )
    {
      // draw center
      board.setPenColor ( LibBoard::Color::Black );
      board.fillCircle ( cg->get_apex()->p[0], cg->get_apex()->p[1], 0.002, 0 );

      // draw voronoi edges
      for ( int j = cg->face_size()-1; j >= 0; j-- )
      {
        SweepFacet *sf = cg->get_sweepfacet ( j );
        FrontLine *fl = sf->get_frontline();
        std::list<Polygon *> plist = fl->get_polygons();
        for ( std::list<Polygon *>::iterator itp = plist.begin(); itp != plist.end(); itp++ )
        {
          Polygon *pol = *itp;
          std::list<SweepEdge *> elist = pol->get_edges();
          std::list<SweepEdge *>::iterator it = elist.begin();

          for ( it = elist.begin(); it != elist.end(); it++ )
          {
            SweepEdge *se = ( *it );
            // for zmax
            if ( se->get_current()->p[2] > zmax )
              zmax = se->get_current()->p[2];

            Front *fr0 = se->get_front ( 0 );
            Front *fr1 = se->get_front ( 1 );
            SweepFacet *sf0 = NULL;
            SweepFacet *sf1 = NULL;
            if ( fr0 )
              sf0 = fr0->get_owner();
            if ( fr1 )
              sf1 = fr1->get_owner();

            if ( sf0 && sf1 && sf0->get_generator() != sf1->get_generator() )
            {
              Point *p0 = se->get_start();
              Point *p1 = se->get_current();
              board.setLineWidth ( 0.6 );
              board.setPenColor ( LibBoard::Color::Black );
              board.drawLine ( p0->p[0], p0->p[1], p1->p[0], p1->p[1], 1 );
            }
//             else if ( !sf0 || !sf1 )
//             {
//               Point *p0 = se->get_start();
//               Point *p1 = se->get_current();
//               board.setLineWidth ( 0.8 );
//               board.setPenColor ( LibBoard::Color::Silver );
//               board.drawLine ( p0->p[0], p0->p[1], p1->p[0], p1->p[1], 1 );
//             }
            else if ( draw_generator_edges )
            {
              Point *p0 = se->get_start();
              Point *p1 = se->get_current();
              board.setLineWidth ( 0.1 );
              board.setPenColor ( LibBoard::Color::Red );
              board.drawLine ( p0->p[0], p0->p[1], p1->p[0], p1->p[1], 1 );
            }

          }
        }
      }
    }
  }

  // drawing iso values
  if ( draw_isovalues )
  {
    double zsteps = zmax/ ( double ) nbzstep;

    for ( int i = 0; i < ( int ) generatorlist.size(); i++ )
    {
      Convex_generator *cg = generatorlist[i];
      if ( cg->is_active() )
      {
        for ( int j = cg->face_size()-1; j >= 0; j-- )
        {
          SweepFacet *sf = cg->get_sweepfacet ( j );

          double zstart = sf->get_start()->p[2];
          int nbstart = floor ( zstart/zsteps ) + 1;
          int rgb[3];

          FrontLine *fl = sf->get_frontline();
          std::list<Polygon *> plist = fl->get_polygons();
          for ( std::list<Polygon *>::iterator itp = plist.begin(); itp != plist.end(); itp++ )
          {
            Polygon *pol = *itp;
            std::list<SweepEdge *> elist = pol->get_edges();

//             printf ( "--> new polygon : %d edges\n", ( int ) elist.size() );
            std::list<SweepEdge *>::iterator it0;

            std::vector<std::set<Point *, PointPtrComp> > plist;
            for ( it0 = elist.begin(); it0 != elist.end(); it0++ )
            {
              SweepEdge *se = *it0;
              compute_points ( sf, nbstart, zsteps, se, plist );
            }

            for ( int k = 0; k < ( int ) plist.size(); k++ )
            {
//               printf ( "--> new line : %d points\n", ( int ) plist[k].size() );
              if ( ( int ) plist[k].size() > 1 )
              {
                ASSERT_ERROR ( ( int ) plist[k].size() %2 == 0, "number of vertices of isovalue %d is not even\n", k );
                for ( std::set<Point *>::iterator itset = plist[k].begin(); itset != plist[k].end(); itset++ )
                {
                  Point *pt0 = *itset++;
//                   printf ( "--> %lf %lf %lf\n", pt0->p[0], pt0->p[1], pt0->p[2] );
                  Point *pt1 = *itset;
//                   printf ( "--> %lf %lf %lf\n", pt1->p[0], pt1->p[1], pt1->p[2] );

                  board.setLineWidth ( 0.3 );

                  if ( use_heatmap )
                  {
                    HSVtoRGB ( ( int ) ( ( ( ( k+1 ) *zsteps ) /zmax ) *255 ), 255, 255, &rgb[0], &rgb[1], &rgb[2] );
                    board.setPenColorRGBi ( rgb[0], rgb[1], rgb[2] );
                  }
                  else
                  {
                    board.setPenColor ( LibBoard::Color::Red );
                  }

                  board.drawLine ( pt0->p[0], pt0->p[1], pt1->p[0], pt1->p[1], 2 );
                }
              }
            }
          }
        }
      }
    }
  }

  tprintf ( "Writing file %s ...\n", filename );
  board.saveEPS ( filename, LibBoard::Board::A4 /*Board::BoundingBox*//*, 100.0*/ );
//   board.saveSVG( filename, -1, -1, 10 );
  tprintf ( "Done\n" );
}

int potential_cross ( SweepEdge *se, double zpos, double *crashpos )
{
  double epsilon = 1e-12;
  double plane[4] = {0.0, 0.0, 1.0, 0.0};
  plane[3] = -zpos;
  return geom::segment3d_plane_intersection ( se->get_start()->p, se->get_current()->p, plane, epsilon, crashpos );
}

void compute_points ( SweepFacet *sf, int start, double zsteps, SweepEdge *se, std::vector< std::set<Point *, PointPtrComp> > &list )
{
//   printf ( "se --> %lf %lf %lf : %lf %lf %lf\n", se->get_start()->p[0], se->get_start()->p[1], se->get_start()->p[2], se->get_current()->p[0], se->get_current()->p[1], se->get_current()->p[2] );
  int line = start;
//   printf ( "line --> %d\n", line );
  double pos[3];
  while ( line*zsteps < se->get_current()->p[2] )
  {
    if ( potential_cross ( se, line*zsteps, pos ) )
    {
      Point *pt = new Point;
//       printf ( "line --> %d : z = %lf\n", line, line*zsteps );

      pt->set ( pos[0], pos[1], pos[2] );
//       printf ( "--> %lf %lf %lf\n", pos[0], pos[1], pos[2] );
      if ( line <= ( int ) list.size() )
        list[line-1].insert ( pt );
      else if ( line > ( int ) list.size() )
      {
        list.resize ( line );
        list[line-1].insert ( pt );
      }
    }
    line++;
  }
//   getchar();
}

