/***********************************************************************
*
*       ELMER, A Computational Fluid Dynamics Program.
*
*       Copyright 1st April 1995 - , Center for Scientific Computing,
*                                    Finland.
*
*       All rights reserved. No part of this program may be used,
*       reproduced or transmitted in any form or by any means
*       without the written permission of CSC.
*
*                Address: Center for Scientific Computing
*                         Tietotie 6, P.O. BOX 405
*                         02101 Espoo, Finland
*                         Tel.     +358 0 457 2001
*                         Telefax: +358 0 457 2302
*                         EMail:   Jari.Jarvinen@csc.fi
************************************************************************/

/***********************************************************************
Program:    ELMER Front
Module:     ecif_body2D.cpp
Language:   C++
Date:       01.10.98
Version:    1.00
Author(s):  Martti Verho
Revisions:

Abstract:   Implementation

************************************************************************/

#include "ecif_body2D.h"
#include "ecif_bodyElement.h"
#include "ecif_bodyElement2D.h"
#include "ecif_bodyElementLoop.h"
#include "ecif_model.h"
#include "ecif_renderer.h"
#include "ecif_userinterface.h"


// Constructors.

Body2D::Body2D()
  : Body()
{
}

//--Body-elements etc are not known
Body2D::Body2D(bodyGmtrType body_type, int ext_tag, char* name, colorIndices color)
  : Body(body_type, ext_tag, name, color)
{
}

//--Component ids are known, but not more.
Body2D::Body2D(ecif_Body_X& trx_body, bool add_default_layer)
  : Body(trx_body, add_default_layer)
{
}


Body2D::Body2D(bodyGmtrType body_type, int int_tag, int ext_tag,
               int nof_mesh_elements, int* mesh_element_ids)
               :Body(body_type, int_tag, ext_tag, NULL)
{
  maxNofMeshElements = nof_mesh_elements;
  nofMeshElements = nof_mesh_elements;
  meshElementIds = mesh_element_ids;
}


bool
Body2D::acceptsStructuredMesh(int layer)
{
  if (!checkLayerIndex(layer)) return false;

  // If more than one loops, no grid!
  if ( nofElementLoops[layer] == 0 || nofElementLoops[layer] > 1 ) {
    return false;
  }

  // If more or less than four elements, n grid!
  if ( nofElements[layer] != 4 ) {
    return false;
  }

  // Ok, gridable
  return true;
}


// Add all pending vertices as elements
// Returns nof elements added
int
Body2D::addAllPendingVertices(int layer)
{
  if (!checkLayerIndex(layer)) return 0;

  int nof_vertex_tags = pendingVertexTags[layer]->size();

  if ( nof_vertex_tags == 0 )
    return 0;

  // Vertex ids are stored in this table
  // Allocate space also for the possible closing
  // vetex
  int* vertex_groups = new int[1 + nof_vertex_tags];
  int* vertex_ids = new int[1 + nof_vertex_tags];

  int nof_vertex_ids = 0;

  int i;

  for (i = 0; i < nof_vertex_tags; i++) {

    // Read and remove last element
    int v_group = (*pendingVertexGroups[layer])[i];
    int v_tag = (*pendingVertexTags[layer])[i];

    // Find vertex by tag and get id
    BodyElement* v = model->getVertexByTag(v_tag);

    if (v != NULL) {
      vertex_groups[nof_vertex_ids] = v_group;
      vertex_ids[nof_vertex_ids] = v->Id();
      nof_vertex_ids++;
    }
  }

  int nof_elements = 0;

  int groupd_id = vertex_groups[0];

  for (i = 1; i < nof_vertex_ids; i++) {

    int v1 = vertex_ids[i-1];
    int v2 = vertex_ids[i];

    // Inside group, create new element
    if ( groupd_id == vertex_groups[i] ) {

      BodyElement* be = new BodyElement2D(v1, v2);
      addElement(layer, be);
      model->addBodyElement(be, false);
      nof_elements++;
    }

    groupd_id = vertex_groups[i];
  }

  pendingVertexGroups[layer]->clear();
  pendingVertexTags[layer]->clear();

  return nof_elements;
}



#if 0
// Method checks that edges create a closed loop and it stores the
// edge-loop as table of consecutive edge-ids. A negative id-number
// in the  table means that edge is in the reversed order.
// Return true if body is ok, otherwise false.
bool
Body2D::check()
{
  checked = true;
  bool result = false;

  nofElements = belements->size();
  if (nofElements == 0)
    return false;
  // A table where row is: (edge-id, vertex1-id, vertex2-id)
  int (*vertex_table)[3] = new int[nofElements][3];
  BodyElement* be;
  BodyElement* vertex;
  int e_id, v1_id, v2_id;
  BodyElementTable::iterator pos = belements->begin();
  // Now the (edge,vertex1,vertex2)-table is filled.
  int row = 0;
  while (pos != belements->end()) {
    be = (*pos++).second;
    e_id = be->ID();
    vertex = be->getSubElement(1);
    v1_id = vertex->ID();
    vertex = be->getSubElement(2);
    v2_id = vertex->ID();
    vertex_table[row][0] = e_id;
    vertex_table[row][1] = v1_id;
    vertex_table[row][2] = v2_id;
    row++;
  }
  // Next we create a edgeLoop-table which is simply a list of edge-id
  // numbers. A negative id-number in the table means that edge's
  // vertices are in opposite order compared to whole loop order.
  IdList* edgeLoop = new IdList;

  // Starting edge is selcted.
  int table_row = 0;//we take the first edge as a staring edge.
  int vrtx_nbr = 1;
  // These are needed to check at the end that loop is closed.
  int start_vertex = vertex_table[0][1];
  int end_vertex = vertex_table[0][2];
  // OUTER LOOP: once for each edge in the resulting edge-loop table.
  for (int i = 0; i < nofElements; i++) {
    // order of vertices compared to whole loop order
    int sign = (vrtx_nbr == 1)? 1: -1;
    edgeLoop->push_back(sign * vertex_table[table_row][0]);
    // id for the other vertex in the edge's vertex pair.
    //  Next edge is
    int v_id = vertex_table[table_row][(vrtx_nbr==1)?2:1];
    end_vertex = v_id;
    // we don't want to search for this table-row any more, lets mark it off!
    vertex_table[table_row][0] *= -1;
    // now next row in *vertex_table* is searhced.
    // Note: updating of *vrtx_nbr* is based on short-circuit property
    // of the logical operators in C++!!
    // INNER LOOP: we search for the 'next' edge in the (edge,vrtx1,vrtx2)-table
    for (int j = 0; j < nofElements; j++) {
      vrtx_nbr = 1;
      if ( (vertex_table[j][0] > 0)
           &&
          // here short-circuit is used in logical-or (||) when
          // vtrx_nbr is selcted based on first matching column.
          (vertex_table[j][vrtx_nbr] == v_id ||
           vertex_table[j][++vrtx_nbr] == v_id )) {
        table_row = j;
        break;
      }
    }
  }

  if (start_vertex == end_vertex)
    result = true;
  else
    return false;

  // If we have a closed loop of edges, we can create the final
  // element-loop to be stored in the Model.
  // We also ccheck loops ccw/cw orientation
  int loopDirection;
  BodyElementLoop* bel;
  if (result == true) {
    bel = new BodyElementLoop(edgeLoop);
    model->addBodyElementLoop(bel);
    elementLoopIds->push_back(bel->ID());
    loopDirection = calcDirection();
  }
  // If for some reason edge-loop orientation couldn't
  // be calulated, status is not ok.
  if (loopDirection == 0)
    result = false;
  // If loop direction was clock-wise, we have to
  // change the loop-ids sign to indicate this
  else if (loopDirection < 0) {
    elementLoopIds->pop_back();
    elementLoopIds->push_back(-1 * bel->ID());
  }

  return result;
}
#endif


// New version checking all element-loops defined for the body
// Method checks that edges create a closed loop and it stores the
// edge-loop as table of consecutive edge-ids. A negative id-number
// in the  table means that edge is in the reversed order.
// Return true if body is ok, otherwise false.
bool
Body2D::check()
{
  if (nofLayers == 0) return false;

  initName();

  // For a mesh, open or virtual body
  // ================================
  if ( isMeshBody() ||
       isBemBody()  ||
       isOpen()     ||
       isVirtual()
     ) {
    return Body::check();
  }

  // For a closed cad body
  // =====================
  UserInterface* gui = (UserInterface*)model->getGui();


  // Check all layers
  // ----------------
  int layer = -1;
  while (true) {

    if (!selectLayer(++layer)) break;

    int nof_pending_vertices = addAllPendingVertices(layer);

    int nof_pending_elements = addAllPendingElements(layer);

    checked = true;

    nofElements[layer] = belements[layer]->size();

    if ( nofElements[layer] == 0 ) {

      // We have elementLoops constructed already,
      // Store element ids from the loop to the belements-array
      if ( nofElementLoops[layer] > 0 ) {

        if ( !addAllLoopElements(layer) ) {
          status = false;
          return false;
        }

        nofElements[layer] = belements[layer]->size();

        // All input loops were checked (emf-file!), the body
        // shoud be ok
        if ( nof_pending_elements == 0 && status == true ) {
          continue;
        }

      // We have neither elements nor elementLoops,
      // something is really wrong!
      } else {

        strstream strm1, strm2;
        strm1 << "***ERROR in geometry for Body " << tag << ":" << ends;
        strm2 << "---No Edges or Edge Loops defined!" << ends;
        gui->showMsg(strm1.str());
        gui->showMsg(strm2.str(), 1);

        status = false;
        return false;
      }
    }

    // Construct loops from "scratch"
    // ==============================

    //--Remove existing loops
    removeElementLoops(layer);
    status = false;

    //--Create a table where rows are: (edge-id, vertex1-id, vertex2-id)
    int (*vertex_table)[3] = new int[nofElements[layer]][3];

    BodyElement* edge;
    BodyElement* vertex;
    int e_id, v1_id, v2_id;

    BodyElementTable::iterator pos = belements[layer]->begin();
    int row = 0;

    //--Fill the table.
    while ( pos != belements[layer]->end() ) {

      edge = (*pos++).second;

      e_id = edge->Id();

      // If no vertices in the element or we have a closed element like
      // a  circle
      if ( edge->isClosedU() || edge->getNofSubElements() == 0 ) {
        v1_id = 0;
        v2_id = 0;

      } else {
        v1_id = edge->getFirstSubElement()->Id();
        v2_id = edge->getLastSubElement()->Id();
      }

      vertex_table[row][0] = e_id;
      vertex_table[row][1] = v1_id;
      vertex_table[row][2] = v2_id;
      row++;
    }

    // Next we create a edgeLoop-table which is simply a list of edge-id
    // numbers. A negative id-number in the table means that edge's
    // vertices are in opposite order compared to whole loop order.

    // Starting edge for the loop is selected.
    int nof_free_elements = nofElements[layer];

    while (nof_free_elements > 0) {

      // OUTERMOST For: once for each edge in the resulting edge-loop table.
      for (int k = 0; k < nofElements[layer]; k++) {

        if (vertex_table[k][0] < 0)
          continue;

        IdList edge_loop;

        int table_row = k;//we take the first free edge as the starting edge.
        int vrtx_nbr = 1;

        // These are needed to finally check that the element loop is closed.
        int start_vertex, end_vertex;
        start_vertex = vertex_table[k][1];
        end_vertex = -1;

        while (nof_free_elements > 0 && start_vertex != end_vertex) {

          // OUTER For: once for each edge in the resulting edge-loop table.
          for (int i = 0; i < nofElements[layer]; i++) {

            if ( vertex_table[i][0] < 0 ) continue;

            // Order of vertices compared to whole loop order
            int sign = (vrtx_nbr == 1)? 1: -1;

            // Add new edge
            edge_loop.push_back(sign * vertex_table[table_row][0]);
            nof_free_elements--;

            // We don't want to search for this table-row any more, lets mark it off!
            vertex_table[table_row][0] *= -1;

            // Id for the other vertex in the edge's vertex pair.
            int v_id = vertex_table[table_row][(vrtx_nbr==1)?2:1];
            end_vertex = v_id;

            if (start_vertex == end_vertex) break;

            // Now next row in *vertex_table* is searhced.
            // Note: updating of *vrtx_nbr* is based on short-circuit property
            // of the logical operators in C!!

            // INNER For: we search for the 'next' edge in the (edge,vrtx1,vrtx2)-table
            for (int j = 0; j < nofElements[layer] ; j++) {

              if (vertex_table[j][0] < 0) continue;

              vrtx_nbr = 1;

              // NOTE: Here short-circuit is used in logical-or (||) when
              // vtrx_nbr is selcted based on first matching column.
              if (vertex_table[j][vrtx_nbr] == v_id ||
                  vertex_table[j][++vrtx_nbr] == v_id ) {
                table_row = j;
                break;
              }

            } // End INNER For

            if (nof_free_elements <= 0 ) break;

          } // End OUTER For

        } // End OUTER While

        // We could not close the loop, error!
        if (start_vertex != end_vertex) {

          strstream strm;
          strm << "***ERROR in geometry for Body " << tag << " in layer "
               << layer + 1 << ends;
          gui->showMsg(strm.str());
          gui->showMsg("---Body not closed!", 1);

          return false;
        }

        // All but the first loops are cw-directed
        int loop_direction = ( nofElementLoops[layer] > 0 )?-1:1;

        BodyElementLoop* bel = NULL;
        int bel_id;
        int direction; // Direction relative to the possible existing loop

        //-Existing loop, just pick id and relative direction
        //-NOTE All loops are stored in ccw-direction in the model, so
        // we do not use the 'direction' argument here
        //
        if ( model->getBodyElementLoopId(&edge_loop, bel_id, direction) ) {
          bel = model->getBodyElementLoopById(bel_id);
          if ( bel == NULL ) return false;

        //-Otherwise create new loop and add to the model
        //
        } else {

          bel = new BodyElementLoop(&edge_loop, false, OT_BOUNDARY);
          bel->check();
          model->addBodyElementLoop(bel);
        }

        // Add loop id to the body
        elementLoopIds[layer]->push_back(loop_direction * bel->Id());
        elementLoopTags[layer]->push_back(bel->Tag());
        nofElementLoops[layer]++;

        // No more free edges
        if (nof_free_elements <= 0) break;

      } // End OUTERMOST For

    } // End OUTERMOST While

  } // Each body grid layer

  status = true;
  return true;
}


