// 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 <stdio.h>
#include <stdlib.h>
#include <iostream>
#include <math.h>
#include <vector>
#include <list>
#include <set>
#include <queue>
#include <map>
#include <iterator>
#include <cstring>
#include "sweepobject.h"
#include "generator.h"
#include "grid.h"
#include "statistics.h"
#include "border.h"
#include <geom.h>
#include "domain2d.h"

#ifndef _GRAPH_INCLUDE
#define _GRAPH_INCLUDE

namespace vorosweep
{

class Graph
{
  private:
    Domain *dom;
    Grid2d *bucketgrid;
    std::set<Event *, EventPtrComp> eventqueue;
    std::set<Event *, EventPtrComp> priorityeventqueue;
    std::vector<Convex_generator *> generatorlist;
    std::vector<Border *> borderlist;
    Container<Event> *econtainer;
    int gnf; // number of face of generators;
    double ctime;
    double epsilon;
            
    Statistic *stats;
    
    std::vector<Point *> pointlist;
    std::map<Point *, int> indexmap;
    std::map<Point *, int>::iterator itindex;
    int nbg;
    int nbf;
    int nbvf;
    char checksum[100]; // used to uniquely identify the result
    
    // debug purposes
    int dumpmod;

  public:
    Graph()
    {
      dom = NULL;
      bucketgrid = new Grid2d ( 0.05 );
      econtainer = new Container<Event>;
      ctime = 0.0;
      epsilon = 1e-14;
      gnf = 0;
      stats = new Statistic;
//       checksum = 0;
    }
    ~Graph()
    {
       for ( int i = 0; i < ( int ) generatorlist.size(); i++ )
         delete generatorlist[i];
       for ( int i = 0; i < ( int ) borderlist.size(); i++ )
         delete borderlist[i];
    }
    inline void add_generator ( Convex_generator *cg )
    {
      generatorlist.push_back ( cg );
      BBox *bb = bucketgrid->get_bbox();
      Point *apex = cg->get_apex();
      dbgprintf ( 2, "apex --> %lf %lf %lf\n", apex->p[0], apex->p[1], apex->p[2] );
      bb->update ( apex->p );
      gnf += cg->face_size();
    }
    inline void add_border ( Border *bo )
    {
      borderlist.push_back ( bo );
      BBox *bb = bucketgrid->get_bbox();
      for ( int i = 0; i < 2; i++ )
      {
        Point *pt = bo->get_point ( i );
        dbgprintf ( 2, "pt --> %lf %lf %lf\n", pt->p[0], pt->p[1], pt->p[2] );
        bb->update ( pt->p );
      }
    }
    inline void add_event ( Event *ev )
    {
      if ( ev->get_time() >= ctime ) // future events only
      {
        dbgprintf ( 1, "adding : " );
        ev->print();
        eventqueue.insert ( ev );
      }
    }
    inline void add_priority_event ( Event *ev )
    {
      if ( ev->get_time() >= ctime ) // future events only
      {
        dbgprintf ( 1, "adding new item : " );
        ev->print();
        priorityeventqueue.insert ( ev );
      }
    }
    inline int is_queue_empty()
    {
      return ( eventqueue.empty() && priorityeventqueue.empty() );
    }
    inline Event *get_next_event()
    {
      std::multiset<Event *>::iterator it;
      Event *ev = NULL;
      if ( priorityeventqueue.empty() )
      {
        it = eventqueue.begin();
        ev = ( *it );
        eventqueue.erase ( it );
      }
      else
      {
        it = priorityeventqueue.begin();
        ev = ( *it );
        priorityeventqueue.erase ( it );
      }
      return ev;
    }
    inline void update_time ( double time )
    {
      if ( ctime < time )
        ctime = time;
    }
    inline double get_time()
    {
      return ctime;
    }
    inline int get_pointlist_size()
    {
      return pointlist.size();
    }
//     inline unsigned long get_checksum()
//     {
//       return checksum;
//     }
    inline char *get_checksum()
    {
      return checksum;
    }
    inline void set_dumpmod ( int dm )
    {
      dumpmod = dm;
    }

    void read_input_file(const char *name );
    void read_borders(const char *name );
    void default_borders();
    void init();    
    void run();    
    void fakerun();
    void finalize();
    void export_vtk ( const char *name, bool flat = 0 );
    void export_vector_graphic ( const char *name, int draw_generator_edges = 0, int draw_isovalues = 0, int use_heatmap = 0,  int nbzstep = 10 );

  private:
    unsigned long hash ( const char *str );
    void update_all();
    void generate_internals();
    void insert_generator ( Convex_generator *cv );
    void add_linear_obstacle ( double *p0, double *p1 );
    void insert_border ( Border *bo );
    void handle ( Event *ev );
    void handle_newgenerator ( Event *ev );
    void handle_newfacetswitch ( Event *ev );
    void handle_newedgeswitch ( Event *ev );
    void handle_borderedgeswitch ( Event *ev );
    void handle_newborderedgeswitch ( Event *ev );
    void handle_edgeswitch ( Event *ev );
    void handle_simple_edgeswitch ( Event *ev );
    void handle_facetswitch ( Event *ev );
    void handle_edge_edgecrash ( Event *ev );
    void handle_edge_facetcrash ( Event *ev );
    void handle_edge_bordercrash ( Event *ev );
    void handle_facet_bordercrash ( Event *ev );
    void process_edge_events ( SweepEdge *se, double ctime, int gid );
    void process_facet_events ( SweepFacet *sf, double ctime, int gid );
    void merge_and_go_left ( SweepEdge *se0, SweepEdge *se1, SweepFacet *left );
    void merge_and_go_right ( SweepEdge *se0, SweepEdge *se1, SweepFacet *right );
    int voronoi_vertex_merge ( SweepEdge *se0, SweepEdge *se1, double *ndir );
    void new_facet ( SweepEdge *se0, SweepEdge *se1, double *ndir );
//     int new_facet ( SweepEdge *se0, SweepEdge *se1, double *ndir );
    int enclose_island ( SweepEdge *se0, SweepEdge *se1 );
    void simple_newedgeswitch ( SweepEdge *se0, SweepEdge *se1, double *ndir3d, SweepFacet *msf[2] );
    void merge ( SweepEdge *se0, SweepEdge *se1 );
    Event *next_edgeswitch ( SweepEdge *se, double *pos, int gid );
    Event *next_borderedgeswitch ( SweepEdge *se, Border *bo, double *pos, int gid );
    void next_edgecrashes ( SweepEdge *se );
    void next_facetswitch ( SweepFacet *sf, double *pos, int gid, Event **eventlist, int &nbe );
    void add_earliest_event ( SweepObject *so );
    int check_for_corner ( SweepEdge *se, Border *bo, double time );
    void switch_border ( SweepEdge *se, Border *bo, double time );
    void singular_point_rotation_ccw ( SweepEdge *se, Border *nextborder, double time );
    void singular_point_rotation_cw ( SweepEdge *se, Border *nextborder, double time );
    SweepEdge *singular_point_rotation_ccw ( SweepFacet *sf, Border *bo, Point *from, double time );
    SweepEdge *singular_point_rotation_cw ( SweepFacet *sf, Border *bo, Point *from, double time );
    void rotation_ccw ( SweepEdge *se0, SweepEdge *se1 );
    void rotation_cw ( SweepEdge *se0, SweepEdge *se1 );
};

};

#endif
