// Cutmesh - Copyright (C) 2010-2018 T. Mouton, E. Bechet 
//
// See the LICENSE file for license information and contributions.
// bugs and problems to <thibaud.mouton@gmail.com>.

#include "lsquery.h"

void Topology_query::set_expression ( std::vector<char*> expr )
{
  if ( result )
  {
    delete result;
    result = NULL;
  }

  rpn_expr.clear();
  for ( int i=0; i < expr.size(); i++ )
  {
    expr_element new_elem;

//         printf("--> %s\n", expr[i]);

    if ( strcmp ( expr[i], "union" ) == 0 )
    {
      new_elem.op = 0;
    }
    else if ( strcmp ( expr[i], "inter" ) == 0 )
    {
      new_elem.op = 1;
    }
    else if ( strcmp ( expr[i], "sub" ) == 0 )
    {
      new_elem.op = 2;
    }
    else
    {
      // get the surf
      int surf = atoi ( expr[i++] );
      int local_id = topo_object->get_entity_manager()->get_lookup_face_id ( surf );
//             printf("surf %d --> local %d\n", surf, local_id);
      if ( local_id == -1 )
      {
        printf ( "bad levelset number --> abort\n" );
        rpn_expr.clear();
        return;
      }
      // get the side
      int side;
      if ( strcmp ( expr[i], "NEG" ) == 0 )
        side = NEGATIVE;
      else if ( strcmp ( expr[i], "ZERO" ) == 0 )
        side = ZERO;
      else if ( strcmp ( expr[i], "POS" ) == 0 )
        side = POSITIVE;
      else
      {
        printf ( "expression error : no position given --> abort\n" );
        rpn_expr.clear();
        return;
      }

      new_elem.qr = query ( local_id, side );
//             printf("operand dump :\n");
//             dump(new_elem.qr);
    }
    rpn_expr.push_back ( new_elem );
  }
}

void Topology_query::compute_expression()
{
  if ( rpn_expr.empty() )
    return;

  for ( int i=0; i < rpn_expr.size(); i++ )
  {
    expr_element elem = rpn_expr[i];
    if ( elem.qr != NULL )
    {
      rpn_stack.push ( elem.qr );
    }
    else
    {
      assert(rpn_stack.size()>=2); // two operands are required
      query_result *qr1 = rpn_stack.top();
      rpn_stack.pop();
      query_result *qr0 = rpn_stack.top();
      rpn_stack.pop();
      int op_id = elem.op;
      switch ( op_id )
      {
        case 0:
          qr0->union_result ( qr1 );
          rpn_stack.push ( qr0 );
          break;
        case 1:
          qr0->inter_result ( qr1 );
          rpn_stack.push ( qr0 );
          break;
        case 2:
          qr0->sub_result ( qr1 );
          rpn_stack.push ( qr0 );
          break;
      }
      delete qr1;
    }
  }
  result = rpn_stack.top();
  rpn_stack.pop();
  if ( !rpn_stack.empty() )
  {
    printf ( "too many parameters in expression --> abort\n" );
    while ( !rpn_stack.empty() )
    {
      delete rpn_stack.top();
      rpn_stack.pop();
    }
    delete result;
    result = NULL;
  }
}

void query_result::union_result ( query_result *qr )
{
  for ( qr->itv=qr->vols.begin(); qr->itv != qr->vols.end(); ++ ( qr->itv ) )
    vols.insert ( * ( qr->itv ) );
  for ( qr->itf=qr->faces.begin(); qr->itf != qr->faces.end(); ++ ( qr->itf ) )
    faces.insert ( * ( qr->itf ) );
  for ( qr->ite=qr->edges.begin(); qr->ite != qr->edges.end(); ++ ( qr->ite ) )
    edges.insert ( * ( qr->ite ) );
  for ( qr->itx=qr->verts.begin(); qr->itx != qr->verts.end(); ++ ( qr->itx ) )
    verts.insert ( * ( qr->itx ) );
}

void query_result::inter_result ( query_result *qr )
{
  std::vector<TopoVolume *> rtv;
  for ( itv=vols.begin(); itv != vols.end(); ++itv )
    if ( qr->vols.find ( *itv ) == qr->vols.end() )
      rtv.push_back ( *itv );
  for ( int i = 0; i < rtv.size(); i++ )
    vols.erase ( rtv[i] );

  std::vector<TopoFace *> rtf;
  for ( itf=faces.begin(); itf != faces.end(); ++itf )
    if ( qr->faces.find ( *itf ) == qr->faces.end() )
      rtf.push_back ( *itf );
  for ( int i = 0; i < rtf.size(); i++ )
    faces.erase ( rtf[i] );

  std::vector<TopoEdge *> rte;
  for ( ite=edges.begin(); ite != edges.end(); ++ite )
    if ( qr->edges.find ( *ite ) == qr->edges.end() )
      rte.push_back ( *ite );
  for ( int i = 0; i < rte.size(); i++ )
    edges.erase ( rte[i] );

  std::vector<TopoVertex *> rtx;
  for ( itx=verts.begin(); itx != verts.end(); ++itx )
    if ( qr->verts.find ( *itx ) == qr->verts.end() )
      rtx.push_back ( *itx );
  for ( int i = 0; i < rtx.size(); i++ )
    verts.erase ( rtx[i] );
}

void query_result::sub_result ( query_result *qr )
{
  std::vector<TopoVolume *> rtv;
  for ( itv=vols.begin(); itv != vols.end(); ++itv )
    if ( qr->vols.find ( *itv ) != qr->vols.end() )
      rtv.push_back ( *itv );
  for ( int i = 0; i < rtv.size(); i++ )
    vols.erase ( rtv[i] );

  std::vector<TopoFace *> rtf;
  for ( itf=faces.begin(); itf != faces.end(); ++itf )
    if ( qr->faces.find ( *itf ) != qr->faces.end() )
      rtf.push_back ( *itf );
  for ( int i = 0; i < rtf.size(); i++ )
    faces.erase ( rtf[i] );

  std::vector<TopoEdge *> rte;
  for ( ite=edges.begin(); ite != edges.end(); ++ite )
    if ( qr->edges.find ( *ite ) != qr->edges.end() )
      rte.push_back ( *ite );
  for ( int i = 0; i < rte.size(); i++ )
    edges.erase ( rte[i] );

  std::vector<TopoVertex *> rtx;
  for ( itx=verts.begin(); itx != verts.end(); ++itx )
    if ( qr->verts.find ( *itx ) != qr->verts.end() )
      rtx.push_back ( *itx );
  for ( int i = 0; i < rtx.size(); i++ )
    verts.erase ( rtx[i] );

}

Topology_query::Topology_query ( Topo *topo )
{
  topo_object = topo;

  // first of all --> list the levelsets/faces
  for ( int i = 0; i < topo_object->topoface_size(); i++ )
  {
    TopoFace *tf = topo_object->get_topoface ( i );
    if ( !tf->get_parent() )
    {
      levelset_map.insert ( std::pair<int, TopoFace*> ( tf->get_physical ( 0 ), tf ) );
    }
    else
    {
      TopoFace *root = tf->get_parent();
      while ( root->get_parent() )
        root = root->get_parent();
      levelset_map.insert ( std::pair<int, TopoFace*> ( root->get_physical ( 0 ), root ) );
    }
  }
  result = NULL;
//     printf("levelset map --> %d\n", levelset_map.size());
}

query_result *Topology_query::query ( int surf, int position )
{
//     printf("query for levelset %d\n", surf);
  query_result *ret = new query_result;
  std::vector<TopoFace *> final_list;
  std::vector<TopoFace *> inspect_list;

  TopoFace *tf = levelset_map.find ( surf )->second;
//     printf("found levelset --> %d\n", tf->get_physical(0));
  inspect_list.push_back ( tf );
  for ( int i = 0; i < inspect_list.size(); i++ )
  {
    TopoFace *ttf = inspect_list[i];
    if ( ttf->child_size() )
      for ( int j = 0; j < ttf->child_size(); j++ )
        inspect_list.push_back ( ttf->get_child ( j ) );
    else
      final_list.push_back ( ttf );
  }

//     printf("final list size --> %d\n", final_list.size());

  // populate volumes
  for ( int i = 0; i < final_list.size(); i++ )
  {
    TopoFace *ttf = final_list[i];
    ret->add_face ( ttf );
    if ( position == NEGATIVE )
    {
      if ( ttf->get_inside_topovolume() )
      {
        TopoVolume *ttv = ttf->get_inside_topovolume();
        ret->add_volume ( ttv );
      }
    }
    if ( position == POSITIVE )
    {
      if ( ttf->get_outside_topovolume() )
      {
        TopoVolume *ttv = ttf->get_outside_topovolume();
        ret->add_volume ( ttv );
      }
    }
  }

  for ( ret->init_read_volumes(); !ret->end_volume(); ret->next_volume() )
  {
    TopoVolume *ttv = ret->current_volume();
//         printf("volume : nb face --> %d\n", ttv->topoface_size());
    for ( int i = 0; i < ttv->topoface_size(); i++ )
    {
      ret->add_face ( ttv->get_topoface ( i ) );
    }
  }
  for ( ret->init_read_faces(); !ret->end_face(); ret->next_face() )
  {
    TopoFace *ttf = ret->current_face();
//         printf("face : nb edge --> %d\n", ttf->topoedge_size());
    for ( int i = 0; i < ttf->topoedge_size(); i++ )
    {
      ret->add_edge ( ttf->get_topoedge ( i ) );
    }
  }
  for ( ret->init_read_edges(); !ret->end_edge(); ret->next_edge() )
  {
    TopoEdge *tte = ret->current_edge();
//         printf("edge : nb vertex --> %d\n", 2);
    for ( int i = 0; i < 2; i++ )
    {
      ret->add_vertex ( tte->get_topovertex ( i ) );
    }
  }
  return ret;
}

void Topology_query::split_line ( const char *line, std::vector<char*> &exp )
{
  const std::size_t len = strlen(line);
  char* curchar = new char[len + 1];
  strncpy(curchar, line, len);
  curchar[len] = '\0';

  while ( 1 )
  {
    /* saute les espaces */
    while ( *curchar && *curchar == ' ' )
      curchar++;

    /* fin de ligne ? */
    if ( *curchar == '\0' )
      break;

    /* on se souvient du début de ce mot */
    exp.push_back ( curchar );

    /* cherche la fin du mot */
    while ( *curchar && !isspace ( *curchar ) )
    {
      curchar++; /* saute le mot */
    }

    /* termine le mot par un \0 et passe au suivant */
    if ( *curchar )
    {
      *curchar = '\0';
      curchar++;
    }
  }
}

std::vector<int> Topology_query::get_result_volumes_physicals()
{
  std::vector<int> rvp;
  if ( result )
  {
    EntityManager *em = topo_object->get_entity_manager();
    for ( result->init_read_volumes(); !result->end_volume(); result->next_volume() )
    {
      TopoVolume *ttv = result->current_volume();
      int phys = ttv->get_physical ( 0 );
      phys = em->get_vol_id ( phys );
      rvp.push_back ( phys );
    }
  }
  return rvp;
}

std::vector<int> Topology_query::get_result_faces_physicals()
{
  std::vector<int> rvf;
  if ( result )
  {
    EntityManager *em = topo_object->get_entity_manager();
    for ( result->init_read_faces(); !result->end_face(); result->next_face() )
    {
      TopoFace *ttf = result->current_face();
      int phys = ttf->get_physical ( 0 );
      phys = em->get_face_id ( phys );
      rvf.push_back ( phys );
    }
  }
  return rvf;
}

std::vector<int> Topology_query::get_result_edges_physicals()
{
  std::vector<int> rve;
  if ( result )
  {
    EntityManager *em = topo_object->get_entity_manager();
    for ( result->init_read_edges(); !result->end_edge(); result->next_edge() )
    {
      TopoEdge *tte = result->current_edge();
      int phys = tte->get_physical ( 0 );
      phys = em->get_edge_id ( phys );
      rve.push_back ( phys );
    }
  }
  return rve;
}

std::vector<int> Topology_query::get_result_vertices_physicals()
{
  std::vector<int> rvx;
  if ( result )
  {
    EntityManager *em = topo_object->get_entity_manager();
    for ( result->init_read_vertices(); !result->end_vertex(); result->next_vertex() )
    {
      TopoVertex *ttx = result->current_vertex();
      int phys = ttx->get_physical ( 0 );
//                 phys = em->get_face_id(phys);
      rvx.push_back ( phys );
    }
  }
  return rvx;
}

void Topology_query::dump ( query_result *qr )
{
  if ( !qr )
    return;

  EntityManager *em = topo_object->get_entity_manager();
  printf ( "--> %d volumes\n", qr->volume_size() );
  for ( qr->init_read_volumes(); !qr->end_volume(); qr->next_volume() )
  {
    TopoVolume *ttv = qr->current_volume();
    int phys = ttv->get_physical ( 0 );
    phys = em->get_vol_id ( phys );
    printf ( "topovolume physical --> %d\n", phys );
  }
  printf ( "--> %d faces\n", qr->face_size() );
  for ( qr->init_read_faces(); !qr->end_face(); qr->next_face() )
  {
    TopoFace *ttf = qr->current_face();
    int phys = ttf->get_physical ( 0 );
    phys = em->get_face_id ( phys );
    printf ( "topoface physical --> %d\n", phys );
  }
  printf ( "--> %d edges\n", qr->edge_size() );
  for ( qr->init_read_edges(); !qr->end_edge(); qr->next_edge() )
  {
    TopoEdge *tte = qr->current_edge();
    int phys = tte->get_physical ( 0 );
    phys = em->get_edge_id ( phys );
    printf ( "topoedge physical --> %d\n", phys );
  }
  printf ( "--> %d vertices\n", qr->vertex_size() );
  for ( qr->init_read_vertices(); !qr->end_vertex(); qr->next_vertex() )
  {
    TopoVertex *ttx = qr->current_vertex();
    int phys = ttx->get_physical ( 0 );
    printf ( "topovertex physical --> %d\n", phys );
  }
}

void Topology_query::minishell()
{
  char line[4096];

  while ( 1 )
  {
    // affiche invite
    printf ( "> " );
    fflush ( stdout );

    // get the line
    if ( !fgets ( line,sizeof ( line )-1,stdin ) )
    {
      /* ^D ou fin de fichier => on quitte */
      printf ( "\n" );
      break;
    }

    // compute and dump
    std::vector<char*> exp;

    split_line ( line, exp );

    if ( exp.size() )
    {
      set_expression ( exp );
      compute_expression();

      query_result *res = get_result();

      dump ( res );
//         break;
    }
  }
}

