// 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 "sweepobject.h"
#include "frontline.h"
#include "generator.h"

using namespace vorosweep;

// static member shared by all basic_type
Container<Point> *basic_type::pcontainer = new Container<Point>;

const char *SweepObject::objectname[] = {"EDGE","BORDEREDGE","FACET"};
const char *SweepObject::statusname[] = {"STARTING","RUNNING","STOPPED"};

// void SweepObject::disable_futur_event ( SweepObject *s0, SweepObject *s1 )
// {
//   dbgprintf ( 2, "removing event involving %p and %p \n", s0, s1 );
//   std::set<Event*>::iterator it;
//   for ( it = eventqueue.begin(); it != eventqueue.end(); it++ )
//   {
//     Event *ev = *it;
//     if ( ev->get_sweepobject ( 0 ) == s0 && ev->get_sweepobject ( 1 ) == s1 )
//     {
//       ev->print();
//       ev->disable();
//       return;
//     }
//   }
// }

// we should add there something to check if the event has been found !
void SweepObject::remove_earliest_event ( Event *ev )
{
  dbgprintf ( 2, "removing earliest events from %d\n", ( int ) eventqueue.size() );
  Event *cur = NULL;
  while ( cur != ev && eventqueue.size() )
  {
    std::multiset<Event*>::iterator it = eventqueue.begin();
    cur = *it;
    if ( cur->get_time() > ev->get_time() )
      return;
    dbgprintf ( 1, "erasing from queue : " );
    cur->print();
    eventqueue.erase ( it );
  }
}

Event *SweepObject::get_earliest_event()
{
  std::multiset<Event*>::iterator it = eventqueue.begin();
  if ( *it )
  {
    dbgprintf ( 2, "earliest : " );
    ( *it )->print();
  }
  return ( *it );
}

int SweepEdge::compatible_event ( SweepFacet *sf )
{
  // we check the close fronts of edge to avoid adding neighbor facets
  SweepFacet *fsf0 = get_neighbor_sweepfacet ( 0, 2 );
  SweepFacet *fsf1 = get_neighbor_sweepfacet ( 1, 2 );

  if ( sf == fsf0 || sf == fsf1 )
  {
    dbgprintf ( 5, "--> not compatible\n" );
    return 0;
  }

  for ( int i = 0; i < 2; i++ )
  {
    SweepFacet *esf = get_sweepfacet ( i );
//     printf("sf %d --> %p\n", i, esf);
    if ( esf )
    {
//       if (esf == sf)
//       {
//         dbgprintf ( 5, "--> not compatible\n" );
//         return 0;
//       }

      double ndir[3];
      if ( i == 0 )
        geom::cross_product3d ( esf->get_plane(), sf->get_plane(), ndir );
      else
        geom::cross_product3d ( sf->get_plane(), esf->get_plane(), ndir );
//       printf("ndir --> %lf %lf %lf\n", ndir[0], ndir[1], ndir[2]);
      if ( ndir[2] < 0.0 )
      {
        dbgprintf ( 5, "--> not compatible\n" );
        return 0;
      }
    }
  }
  dbgprintf ( 5, "--> compatible\n" );
  return 1;
}

void SweepEdge::set_front ( int i, Front *f )
{
  fr[i] = f;
  fr[i]->cse[ Front::next[i] ] = this;
}

// get adjacent sweepfacet at level 'level' --> level 2 means the neighbor of neighbor. And so on ...
SweepFacet *SweepEdge::get_neighbor_sweepfacet ( int side, int level )
{
  SweepEdge *se = this;
  Front *fr = NULL;
  for ( int i = 0; i < level; i++ )
  {
    fr = se->get_front ( side );
    if ( fr )
      se = fr->get_sweepedge ( side );
    else
      return NULL;
  }
  return fr->get_owner();
}

// get adjacent sweepfacet
SweepFacet *SweepEdge::get_sweepfacet ( int i )
{
  if ( fr[i] )
    return fr[i]->get_owner();
  else
    return NULL;
}

int SweepEdge::self_check ()
{
  // check adjacencies consistency
  for ( int i = 0; i < 2; i++ )
  {
    if ( fr[i] && fr[i]->get_sweepedge ( ( i+1 ) %2 ) != this )
    {
      printf ( "SELF CHECK failed --> sweepedge %p\n", this );
      printf ( "front %d edge %d : %p != sweepedge %p\n", i, ( i+1 ) %2, fr[i]->get_sweepedge ( ( i+1 ) %2 ), this );
      return 1;
    }
  }
  return 0;
}

// beaaaaaaark !
SweepEdge *SweepFacet::get_original_sweepedge ( int i )
{
  return cv->get_sweepedge ( ( gpos+cv->initial_face_size()-i ) %cv->initial_face_size() );
}

int SweepFacet::self_check ()
{
  // check owner consistency
  if ( get_frontline()->get_owner() != this )
  {
    printf ( "SELF CHECK failed --> sweepfacet %p\n", this );
    return 1;
  }
  return 0;
}

void SweepEdge::update_speeds ( SweepFacet *sf )
{
  if ( get_dir3d() [2] == 0.0 )
  {
    set_xyspeed ( INFINITY );
  }
  else
  {
    double xys = sf->compute_squared_speeds ( get_dir2d() );
    set_xyspeed ( sqrt ( xys ) );
  }
}

// void SweepObject::print_event_queue()
// {
//   std::set<Event*>::iterator it = eventqueue.begin();
//   for ( it = eventqueue.begin(); it != eventqueue.end(); it++ )
//   {
//     ( *it )->print();
//   }
// }

void SweepObject::print_event_queue ()
{
  dbgprintf ( 2, "--> printing %d event queue from %s %p\n", ( int ) eventqueue.size(), objectname[type], this );
  for ( std::multiset<Event*>::iterator it = eventqueue.begin(); it != eventqueue.end(); it++ )
    ( *it )->print();
}

void SweepObject::print()
{
  dbgprintf ( 1, "## %s %p --> status %s, start %lf %lf, current %lf %lf, dir --> %lf %lf %lf, speed %lf ##\n", objectname[type], this, statusname[state], start->p[0], start->p[1], current->p[0], current->p[1], dir3d[0], dir3d[1], dir3d[2], xyspeed );
}

void SweepFacet::print()
{
  SweepObject::print();
  dbgprintf ( 2, "## plane : %lf %lf %lf %lf\n", get_plane() [0], get_plane() [1], get_plane() [2], get_plane() [3] );
}

void SweepEdge::print()
{
  SweepObject::print();
  if ( get_sweepfacet ( 0 ) )
    dbgprintf ( 2, "## sf0 %p --> dir %lf %lf\n", get_sweepfacet ( 0 ), get_sweepfacet ( 0 )->get_dir2d() [0], get_sweepfacet ( 0 )->get_dir2d() [1] );
  if ( get_sweepfacet ( 1 ) )
    dbgprintf ( 2, "## sf1 %p --> dir %lf %lf\n", get_sweepfacet ( 1 ), get_sweepfacet ( 1 )->get_dir2d() [0], get_sweepfacet ( 1 )->get_dir2d() [1] );
  if ( get_front ( 0 ) )
    get_front ( 0 )->print();
  if ( get_front ( 1 ) )
    get_front ( 1 )->print();
  dbgprintf ( 2, "## ##\n" );
}


