/***********************************************************************
*
*       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_inputIges.cpp
Language:   C++
Date:       01.10.98
Version:    1.00
Author(s):  Martti Verho
Revisions:

Abstract:   Implementation

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

#include "ecif_body.h"
#include "ecif_body2D.h"
#include "ecif_body3D.h"
#include "ecif_bodyElement.h"
#include "ecif_bodyElement1D.h"
#include "ecif_bodyElement2D.h"
#include "ecif_bodyElement3D.h"
#include "ecif_geometry.h"
#include "ecif_inputIges.h"
#include "ecif_model.h"

const int IGES_LINE_LEN = 80;
const int SEC_ID_POS = 72;
const int DIR_FLD_LEN = 8;
const int DATA_PART_LEN = 64;
char DATA_END = ';';
char DATA_SEP = ',';

// Global buffers
char fieldBuffer[1+DIR_FLD_LEN];
char lineBuffer[1+IGES_LINE_LEN];
char dataBuffer[1+DATA_PART_LEN];
char dataFieldBuffer[1+DATA_PART_LEN];

int  statusNbrBuffer[4];
char statusFldBuffer[3];


// Constructor
IgesStatusField::IgesStatusField()
{
  blankFlag = 0;
  subordFlag  = 0;
  useFlag   = 0;
  hrchFlag    = 0;
}


// Split character format data-field into status number values
void
IgesStatusField::setValues(char* data_str)
{
  for(int i=0; i<4; i++) {
    statusFldBuffer[0] = data_str[2*i];
    statusFldBuffer[1] = data_str[2*i + 1];
    statusNbrBuffer[i] = atoi(statusFldBuffer);
  }
  blankFlag = statusNbrBuffer[0];
  subordFlag  = statusNbrBuffer[1];
  useFlag   = statusNbrBuffer[2];
  hrchFlag    = statusNbrBuffer[3];
}


// Constructor
IgesDirectoryEntry::IgesDirectoryEntry()
{
  id = 0;
  entNbr = 0;
  startLine = 0;
  nofLines = 0;
  colorNbr = DEFAULT_COLOR_INDEX;
  formNbr = 0;
  transfId = 0;
  canBeBody = false;
  referingId = 0;
  body = NULL;
}


// Constructor
InputIges::InputIges(enum ecif_modelDimension m_dim,
                     ifstream& in_file, char* in_filename):
Input(m_dim, in_file, in_filename)
{
  modelDimension = inputDimension;

  directory = new IgesDirectory;
  paramSecLine  = 0;
  paramSecStart = 0;
}


// Method adds new directory entry to the container "directory"
int
InputIges::addToDirectory(IgesDirectoryEntry* dir_entry)
{
  int dir_id = dir_entry->id;
  (*directory)[dir_id] = dir_entry;

  return dir_id;
}


// Method checks if argument strin stream buffer is 'empty', it
// means if there are no data values in the line.
// NOTE: we suppouse that there are no blanks among data values !!!***!!!
bool
InputIges::dataLineStrmIsEmpty(istrstream& data_line)
{
  char c = data_line.peek();
  if (c == EOF || c == ' ')
    return true;
  return false;
}


bool
InputIges::checkEntryReferences()
{
  //--Go to the start position of the parameter (= data) section.
  infile.seekg(paramSecStart - infile.tellg(), ios::cur);
  paramSecLine = 1;

  bool check_only_status = true;

  //--Loop through stored dircetory entities and update references to
  //  other directory entries
  //  This is needed when we decide which entries defines bodies!
  IgesDirectory::iterator pos = directory->begin();

  bool ok;

  while (pos != directory->end()) {

    IgesDirectoryEntry* de = (*pos++).second;

    switch (de->entNbr) {

    case 100: ok = read_100(de, check_only_status); break;
    case 102: ok = read_102(de, check_only_status); break;
    case 106: ok = read_106(de, check_only_status); break;
    case 141: ok = read_141(de, check_only_status); break;
    case 142: ok = read_142(de, check_only_status); break;
    case 143: ok = read_143(de, check_only_status); break;
    case 144: ok = read_144(de, check_only_status); break;
    default: ok = true; break;
    }

    if (!ok) {
      return false;
    }
  }

  return true;
}


bool
InputIges::createBodies()
{
  IgesDirectory::iterator pos;

  // First create bodies
  pos = directory->begin();
  while (pos != directory->end()) {

    IgesDirectoryEntry* de = (*pos++).second;

    if ( !de->canBeBody || de->referingId > 0 ) {
      continue;
    }

    Body* body;

    if ( modelDimension == ECIF_2D ) {
      body = new Body2D();
    } else {
      body = new Body3D();
    }

    model->addBody(body);
    de->body = body;
  }

  // Next set parent bodies for the component elements
  pos = directory->begin();
  while (pos != directory->end()) {

    IgesDirectoryEntry* de = (*pos++).second;

    if ( de->referingId <= 0 ) {
      continue;
    }

    IgesDirectoryEntry* ref_de = getDirectoryEntry(de->referingId);

    de->body = ref_de->body;
  }

  return true;
}


// Method finds out the dimension of a Iges format Cad-model.
// One way to do this is to check if one of the (x,y,z) values
// is constant for all vertices. In this case model would be 2D.
// Otherwise it is 3D
//
enum ecif_modelDimension
InputIges::findCadModelDimension() {

  // Currently only 2D
  return ECIF_2D;

  //return ECIF_3D;
}


// Method reads one data field from string stream argument "data-line"
// into buffer "buffer".
void
InputIges::getDataField(istrstream*& data_line, char* buffer)
{
  // If there is no more data in the data-buffer, read next data-line
  if (dataLineStrmIsEmpty(*data_line)) {
    delete data_line;
    readDataLine(lineBuffer, dataBuffer);
    data_line = new istrstream(dataBuffer);
  }

  data_line->getline(buffer, DATA_PART_LEN, DATA_SEP);
}


// Methods retrieves directory entry from the container "directory"
// using the argument "entry-id" as teh key.
IgesDirectoryEntry*
InputIges::getDirectoryEntry(int entry_id)
{
  IgesDirectoryEntry* de =  (*directory)[entry_id];
  return de;
}


// Method reads directory field number "fld_nbr" into buffer "fld_buffer"
// Data in in the buffer "line_buffer"
void
InputIges::getDirectoryField(int fld_nbr, char* line_buffer, char* fld_buffer)
{
  strncpy(fld_buffer, line_buffer + (fld_nbr-1) * DIR_FLD_LEN, DIR_FLD_LEN);
}


// Method locates the starting position of the paramter lines
// (data lines) where the argument directory entry is pointing to.
// First line is read int global buffer "lineBuffer"
void
InputIges::locateParamEntry(IgesDirectoryEntry* de)
{
  int delta = de->startLine - paramSecLine;
  if (delta  == 0)
    return;
  else if (delta > 0) {
    paramSecLine += delta;
    readFileLine(infile, lineBuffer, delta);
    return;
  }
  else {
    infile.seekg(paramSecStart, ios::beg);
    paramSecLine = de->startLine;
    readFileLine(infile, lineBuffer, paramSecLine - 1);
  }
}


// Read those entries which define body elements
bool
InputIges::readBodyElements()
{
  //-Go to the start position of the parameter (= data) section.
  infile.seekg(paramSecStart - infile.tellg(), ios::cur);
  paramSecLine = 1;

  // Loop through directory entries
  IgesDirectory::iterator pos = directory->begin();

  while (pos != directory->end()) {

    IgesDirectoryEntry* de = (*pos++).second;

    // If this entry is not in the model at all!
    if ( de->body == NULL ) {
      continue;
    }

    bool ok;

    switch(de->entNbr) {
    case 100: ok = read_100(de); break;
    case 106: ok = read_106(de); break;
    case 110: ok = read_110(de); break;
    case 126: ok = read_126(de); break;
    case 128: ok = read_128(de); break;
    default: ok = true; break;
    }

    if (!ok) {
      return false;
    }
  }

  return true;
}




// Method reads all bodies in the Iges input file.
// Returns the number of the bodies read.
bool
InputIges::readCadGeometry()
{
  bool result;

  //--Read directory section and find the start position of the
  //  of parameters-section in the file.
  //  Paramter-section line-counter is set to 0.
  //  NOTE: This counter is updated by: readDataLine and
  //  locateParamEntry functions.
  paramSecStart = readDirectory();

  //--If model type is unknown
  if (modelDimension == ECIF_ND) {
    modelDimension = findCadModelDimension();
  }

  //--Update entry references ans states
  checkEntryReferences();

  //--Create bodies in each entry which defines a body
  createBodies();

  //--Read body elements
  readBodyElements();


  // After reading all body-information, data can be checked!
  //result = this->model->checkBodies();

  //  if (!modelIsOk)
  //    exit(1);
  return true;
}


// Method reads header information from the input file.
// (this is model level information (units etc.))
bool
InputIges::readCadHeader()
{
  return true;
}


// Method reads one data-line into buffer "data_buf" from Iges Cad-file.
// NOTE: end character (like ;) for the group of data lines
// is not read from the file.
void
InputIges::readDataLine(char* line_buf, char* data_buf)
{
  readFileLine(infile, line_buf);

  char* tmp = line_buf;
  int i = 0;
  // Copy data-part into data_buf. Don't copy data-end
  // separator character (normally ';')
  while (i < DATA_PART_LEN && *tmp != DATA_END)
    data_buf[i++] = *tmp++;

  data_buf[i] = '\0';

  // If we working om parameters-section, increase the
  // line counter.
  if (paramSecLine > 0)
    paramSecLine++;
}


// Method reads all (interesting)directory entries from Iges Cad-file
// and stores them into attribute "directory" (container)
int
InputIges::readDirectory()
{
  fieldBuffer[1+DIR_FLD_LEN] = '\0';
  lineBuffer[SEC_ID_POS] = ' ';

  while (!infile.eof() && lineBuffer[SEC_ID_POS] != 'D')
    readFileLine(infile, lineBuffer);

  if (infile.eof())
    return 0;

  IgesDirectoryEntry* de;
  int cur_file_pos = infile.tellg();

  while (!infile.eof() && lineBuffer[SEC_ID_POS] == 'D') {
    de = readDirectoryEntry(lineBuffer);

    //-Add geometry-use types to directory
    //if (de->status.useFlag == 0)
    //  addToDirectory(de);

    // Currently all entries are added to directory.
    // The problem is: what entries could be excluded? !!!
    addToDirectory(de);

    //-Read the first line of next entry
    cur_file_pos = infile.tellg();
    readFileLine(infile, lineBuffer);
  }
  return cur_file_pos;
}


IgesDirectoryEntry*
InputIges::readDirectoryEntry(char* dir_line)
{
  char* tmp;
  IgesDirectoryEntry* dir_entry = new IgesDirectoryEntry;

  // First directory-entry line
  // ==========================

  //--Entity type number
  getDirectoryField(1, dir_line, fieldBuffer);
  dir_entry->entNbr = atoi(fieldBuffer);

  //--Id
  getDirectoryField(10, dir_line, fieldBuffer);
  tmp = fieldBuffer;
  dir_entry->id = atoi(++tmp);

  //--Start-line in par-section
  getDirectoryField(2, dir_line, fieldBuffer);
  dir_entry->startLine = atol(fieldBuffer);

  //--Tranformation matrix id
  getDirectoryField(7, dir_line, fieldBuffer);
  dir_entry->transfId = atol(fieldBuffer);

  //--Status field
  getDirectoryField(9, dir_line, fieldBuffer);
  dir_entry->status.setValues(fieldBuffer);


  // Second directory-entry line
  // ===========================
  readFileLine(infile, dir_line);

  //--Color number
  getDirectoryField(3, dir_line, fieldBuffer);
  dir_entry->colorNbr = (colorIndices)atoi(fieldBuffer);

  //--Number of lines in par-section
  getDirectoryField(4, dir_line, fieldBuffer);
  dir_entry->nofLines = atoi(fieldBuffer);

  //--Form number
  getDirectoryField(5, dir_line, fieldBuffer);
  dir_entry->formNbr = atoi(fieldBuffer);

  //--Check if this entry could be a body
  switch (dir_entry->entNbr) {
  case 100:
  case 102:
  case 106:
  case 141:
  case 142:
  case 143:
  case 144:
    dir_entry->canBeBody = true;
    break;
  }

  return dir_entry;
}


int
InputIges::readDoubleFields(istrstream*& data_line, int nof_fields, double* buffer)
{
  //NOTE: we read data-lines until necessary number of
  //data fields is read from the file
  int counter;
  for(counter = 0; counter < nof_fields; counter++) {
    getDataField(data_line, dataFieldBuffer);
    buffer[counter] = atof(dataFieldBuffer);
  }

  return counter;
}


int
InputIges::readIntFields(istrstream*& data_line, int nof_fields, int* buffer)
{
  //NOTE: we read data-lines until necessary number of
  //data fields is read from the file
  int counter;
  for(counter = 0; counter < nof_fields; counter++) {
    getDataField(data_line, dataFieldBuffer);
    buffer[counter] = atol(dataFieldBuffer);
  }

  return counter;
}


// Method reads line body-element.
bool
InputIges::readLine(Body* body)
{
  return false;
}


// Method reads a nurbs-spline formed body-element.
bool
InputIges::readNurbs(Body* body)
{
  return false;
}


// Method reads one point from input-string *s*.
bool
InputIges::readPoint(istrstream*& data_line, Point3& p)
{
  for(int i = 0; i < 3; i++) {

    getDataField(data_line, dataFieldBuffer);

    p[i] = atof(dataFieldBuffer);

    p[i] *= model->unit.conv[i];
  }

  return true;
}


// Method reads one vertex from input-string *s*.
BodyElement*
InputIges::readVertex(istrstream*& data_line)
{
  static Point3 point;
  static GcPoint gpoint;

  if ( !readPoint(data_line, point) )
    return NULL;

  gpoint.setPosit(point[0], point[1], point[2]);

  // Create vertex from the point in the input file.
  BodyElement* vertex = new BodyElement1D(&gpoint);

  return vertex;
}


//******************************************
//****** IGES ENTITY READING ROUTINES ******
//******************************************

// Circular arc entity (Type 100)
bool
InputIges::read_100(IgesDirectoryEntry* de, bool check_only_status)
{
  bool result = true;

  static ecif_EdgeGeometry_X edge;
  init_trx_data(edge);

  edge.type = ECIF_CIRCLE;

  int i,j;
  int int_fld;
  double dbl_fldb[1];
  char chr_fld;

  bool is_closed;

  static Point3 points[3];

  locateParamEntry(de);

  //-Read one data-line
  readDataLine(lineBuffer, dataBuffer);
  istrstream* data_line = new istrstream(dataBuffer);

  //-Read center and two points
  *data_line >> int_fld >> chr_fld; // Entity-id
  readDoubleFields(data_line, 1, dbl_fldb); // z-inclination

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

    points[i][2] = 0.0;

    for (j = 0; j < 2; j++) {
      readDoubleFields(data_line, 1, dbl_fldb);
      points[i][j] = dbl_fldb[0];
    }
  }

  if ( isZero(dist3(points[1], points[2])) ) {
    is_closed = true;
  } else {
    is_closed = false;
  }

  if ( modelDimension == ECIF_2D && is_closed ) {
    de->canBeBody = true;
  }

  if ( check_only_status ) {
    return result;
  }

  // Create body element
  // ===================

  edge.location = new Point3[1];
  edge.start = new Point3[1];
  edge.end = new Point3[1];

  for (i = 0; i < 3; i++) {
    edge.location[0][i] = points[0][i];
    edge.start[0][i] = points[1][i];
    edge.end[0][i] = points[2][i];
  }

  int body_layer = 0;
  createBodyElement2D(de->body, body_layer, edge);

  reset_trx_data(edge);

  return result;
}


// Composite curve entity (Type 102)
bool
InputIges::read_102(IgesDirectoryEntry* de, bool check_only_status)
{
  bool result = true;

  locateParamEntry(de);

  int i;
  int int_fld, int_fldb[1];
  char chr_fld;

  istrstream* data_line;

  readDataLine(lineBuffer, dataBuffer);
  data_line = new istrstream(dataBuffer);


  //-Read number of curves in the composite
  int nof_crvs;
  *data_line >> int_fld >> chr_fld; // Entry-id
  *data_line >> nof_crvs >> chr_fld;

  int* crv_id_table = new int[nof_crvs];

  IgesDirectoryEntry* comp_de;

  //-Read component dir-ids and update the reference
  for(i = 0; i < nof_crvs; i++) {
    readIntFields(data_line, 1, int_fldb);
    crv_id_table[i] = int_fldb[0];

    comp_de = getDirectoryEntry(int_fldb[0]);
    comp_de->referingId = de->id;
  }

  delete [] crv_id_table;

  return result;
}


// Copious data entity (Type 106)
// Path of data points
bool
InputIges::read_106(IgesDirectoryEntry* de, bool check_only_status)
{
  bool result = true;

  locateParamEntry(de);

  bool is_closed = ( de->formNbr == 63 );

  if ( modelDimension == ECIF_2D && is_closed ) {
    de->canBeBody = true;
  }

  if ( check_only_status ) {
    return result;
  }

  // Read geometry data
  // ==================
  int i,j;
  int int_fld;
  char chr_fld;
  double dbl_fld[1];

  istrstream* data_line;

  readDataLine(lineBuffer, dataBuffer);
  data_line = new istrstream(dataBuffer);

  //-Read format of points
  int read_size;
  int point_size;
  int point_type;
  int nof_points;
  double common_z;

  *data_line >> int_fld >> chr_fld;         // Entry-id
  *data_line >> point_type >> chr_fld;      // Point type
  *data_line >> nof_points >> chr_fld;      // Nof tuples
  readDoubleFields(data_line, 1, dbl_fld);  // Common z coordinate (for type 1 points)

  if ( point_type == 1 ) {
    point_size = 2;
    read_size = 2;
  } else if ( point_type == 2 ) {
    point_size = 3;
    read_size = 3;
  } else if ( point_type == 3 ) {
    point_size = 6;
    read_size = 3;
  }

  Point3* points = new Point3[nof_points];

  //-Read points
  int read_count = 0;
  for(i = 0; i < nof_points; i++) {

    points[i][0] = points[i][1] = points[i][2] = 0.0;

    for (j = 0; j < read_size; j++) {

      readDoubleFields(data_line, 1, dbl_fld);
      read_count++;

      // If we are reading relevant point data
      if ( read_count <= read_size ) {
        points[i][read_count - 1] = dbl_fld[0];
      }

      // Reset counter if all point data read
      read_count = read_count % point_size;
    }
  }

  IgesDirectoryEntry* comp_de;

  //-We are reading a surface into a 3D model
  if ( modelDimension == ECIF_3D ) {
    // body->addElement(from this face-element etc....);
    result = false;
  }

  //-We are reading a body into a 2D model!
  else if ( modelDimension == ECIF_2D ) {
    int body_layer = 0;
    createBodyElement2D(de->body, body_layer, nof_points, points);
  }

  delete[] points;

  return result;
}


// Line entity (Type 110)
bool
InputIges::read_110(IgesDirectoryEntry* de, bool check_only_status)
{
  bool result = true;

  int i,j;
  int int_fld;
  char chr_fld;

  static Point3 points[2];

  locateParamEntry(de);

  //-Read one data-line
  readDataLine(lineBuffer, dataBuffer);
  istrstream* data_line = new istrstream(dataBuffer);

  //-Read two points
  *data_line >> int_fld >> chr_fld; // Entity-id

  for(i = 0; i < 2; i++) {
    readPoint(data_line, points[i]);
  }

  int body_layer = 0;
  createBodyElement2D(de->body, body_layer, points[0], points[1]);

  return result;
}


// Rational B-spline curve entity (Type 126)
bool
InputIges::read_126(IgesDirectoryEntry* de, bool check_only_status)
{
  bool result = true;

  static ecif_EdgeGeometry_X edge;
  init_trx_data(edge);

  locateParamEntry(de);
  int i,j, int_fld;
  char chr_fld;

  //-Read one data-line
  readDataLine(lineBuffer, dataBuffer);
  istrstream* data_line = new istrstream(dataBuffer);

  int nof_cp, nof_kp, degree;
   int planar_flg, open_flg, polyn_flg, periodic_flg;

  *data_line >> int_fld >> chr_fld;   //-Entry-id
  *data_line >> nof_cp >> chr_fld;      //-Nof control-points - 1
  nof_cp += 1;
  *data_line >> degree >> chr_fld;      //-Degree of basis functions
  nof_kp = nof_cp - degree + 1;       //-Number of knot-points
  nof_kp += 2 * degree;           // multiplicity at ends added!!
  *data_line >> planar_flg >> chr_fld;  //-0= non-planar
  *data_line >> open_flg >> chr_fld;    //-0= open curve
  *data_line >> polyn_flg >> chr_fld;   //-0=rational, 1=polynomial
  *data_line >> periodic_flg >> chr_fld;  //-0=non-periodic

  //-Read data into a temporary data-vector
  //Data length: (control-points (x,y,z,w)+ knots
  int data_len = 4 * nof_cp + nof_kp;
  double* crv_data = new double[data_len];
   int nof_values = readDoubleFields(data_line, data_len, crv_data);

  //-Copy data from crv_data into proper components
  double* knots = new double[nof_kp];
  Point4* ct_points = new Point4[nof_cp];

  int pos = 0;

  //-Knot points are first
  for(i=0; i < nof_kp; i++)
    knots[i] = crv_data[pos++];

  //-Then come weights
  for(i=0; i < nof_cp; i++) {
    ct_points[i][3] = crv_data[pos++];
  }

  //-Then come control points

  edge.start = new Point3[1];
  edge.end = new Point3[1];

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

    for(j=0; j < 3; j++) {

      //-First control point is the start-point!
      if ( i == 0 ) {
        edge.start[0][j] = crv_data[pos];
      }

      //-Last control point is the end-point!
      if ( i == nof_cp - 1 ) {
        edge.end[0][j] = crv_data[pos];
      }

      //NOTE: In Iges points are not in homogenous form
      // We must transform them into (xw,yw,zw,w) format
      ct_points[i][j] = ct_points[i][3] * crv_data[pos++];
    }
  }

  edge.isRational = (polyn_flg == 0);
  edge.degree = degree;
  edge.nofKnots = nof_kp;
  edge.knots = knots;
  edge.nofCpoints = nof_cp;
  edge.cpoints = ct_points;

  int body_layer = 0;
  createBodyElement2D(de->body, body_layer, edge);

  delete[] crv_data;

  reset_trx_data(edge);

  return result;
}


// Rational B-spline surface entity (Type 128)
bool
InputIges::read_128(IgesDirectoryEntry* de, bool check_only_status)
{
  bool result = true;

  locateParamEntry(de);

  int i,j;
  int int_fld;
  char chr_fld;

  //-Read one data-line
  readDataLine(lineBuffer, dataBuffer);
  istrstream* data_line = new istrstream(dataBuffer);

  int nof_cp, polyn_flg;
  int nof_cp_u, nof_kp_u, degree_u;
  int open_flg_u, periodic_flg_u;
  int nof_cp_v, nof_kp_v, degree_v;
  int open_flg_v, periodic_flg_v;

  *data_line >> int_fld >> chr_fld;   //-Entry-id

   //---u-parameter
  *data_line >> nof_cp_u >> chr_fld;    //-Nof control-points - 1
  nof_cp_u += 1;
  *data_line >> degree_u >> chr_fld;    //-Degree of basis functions
  nof_kp_u = nof_cp_u - degree_u + 1;   //-Number of knot-points
  nof_kp_u += 2 * degree_u;         // multiplicity at ends added!!

   //---v-parameter
  *data_line >> nof_cp_v >> chr_fld;    //-Nof control-points - 1
  nof_cp_v += 1;
  *data_line >> degree_v >> chr_fld;    //-Degree of basis functions
  nof_kp_v = nof_cp_v - degree_v + 1;   //-Number of knot-points
  nof_kp_v += 2 * degree_v;         // multiplicity at ends added!!

   //---other parameters
  *data_line >> open_flg_u >> chr_fld;    //-0= open curve
  *data_line >> open_flg_v >> chr_fld;    //-0= open curve
  *data_line >> polyn_flg >> chr_fld;      //-0=rational, 1=polynomial
  *data_line >> periodic_flg_u >> chr_fld;  //-0=non-periodic
  *data_line >> periodic_flg_v >> chr_fld;  //-0=non-periodic

  //-Read data into a temporary data-vector
  //Data length: (control-points (x,y,z,w)+ knots
   // total number of control-point (cp_u * cp_v array)
  nof_cp = nof_cp_u * nof_cp_v;
  int data_len =  nof_kp_u + nof_kp_v + 4 * nof_cp ;
  double* crv_data = new double[data_len];
  int nof_values = readDoubleFields(data_line, data_len, crv_data);

  //-Copy data from crv_data into proper components
  double* knots_u = new double[nof_kp_u];
  double* knots_v = new double[nof_kp_v];
  Point4* cpoints = new Point4[nof_cp];

  int pos = 0;

  //-Knot-u points are first
  for(i=0; i < nof_kp_u; i++)
    knots_u[i] = crv_data[pos++];

  //-Knot-u points are next
  for(i=0; i < nof_kp_v; i++)
    knots_v[i] = crv_data[pos++];

  //-Weights are next
  for(i=0; i < nof_cp; i++) {
    cpoints[i][3] = crv_data[pos++];
  }

  //-Control points are next
  //NOTE: In Iges points are not in homogenous form
  // We must transform them into (xw,yw,zw,w) format
  for(i=0; i < nof_cp; i++) {
    for(j=0; j < 3; j++)
      cpoints[i][j] = cpoints[i][3] * crv_data[pos++];
  }

  ecif_FaceGeometry_X sP;
  sP.isRational = (polyn_flg == 0);
  sP.degree_u = degree_u;
  sP.degree_v = degree_v;
  sP.nofKnots_u = nof_kp_u;
  sP.nofKnots_v = nof_kp_v;
  sP.knots_u = knots_v;
  sP.knots_v = knots_v;
  sP.nofCpoints_u = nof_cp_u;
  sP.nofCpoints_v = nof_cp_v;
  sP.nofCpoints = nof_cp;
  sP.cpoints = cpoints;

  delete [] crv_data;

  //-Create nurbs-curve element
  //--Corner points
  BodyElement* vertices[4];
  int  vertex_ids[4];

  static GcPoint point;

  int corner_ids[4];
  corner_ids[0] = 0;
  corner_ids[1] = nof_cp_u - 1;
  corner_ids[2] = (nof_cp_u - 1) * nof_cp_v;
  corner_ids[3] = nof_cp_u * nof_cp_v - 1;

  // Create model vertex elements from nurbs corner points
  for (i = 0; i < 4; i++) {
    int c_id = corner_ids[i];
    double w = sP.cpoints[c_id][3];

    point.setPos(X, sP.cpoints[c_id][0]/w);
    point.setPos(Y, sP.cpoints[c_id][1]/w);
    point.setPos(Z, sP.cpoints[c_id][2]/w);

    vertices[i] = new BodyElement1D(&point);
    vertex_ids[i] = vertices[i]->Id();

    model->addBodyElement(vertices[i]);
  }

  // Create a new element into the 3D-body
  BodyElement* body_elm = new BodyElement3D(4, vertex_ids, &sP);
  de->body->addElement(0, body_elm);
  model->addBodyElement(body_elm);

  return result;
}


// Boundary entity (Type 141)
// This entry point to a untrimmed surface and to all curve elements
// which define this surface as boundaries.
bool
InputIges::read_141(IgesDirectoryEntry* de, bool check_only_status)
{
  bool result = true;

  locateParamEntry(de);

  int i, j;
  int int_fld, int_fldb[1];
  char chr_fld;

  istrstream* data_line;

  readDataLine(lineBuffer, dataBuffer);
  data_line = new istrstream(dataBuffer);

   //-Skip 4 (nbr sep) pairs:
   // Entry id
   // Type of srf representation (0=in trimmimg space, 1=in model space)
   // Preferred representation (0=unspec., 1=model space, 2=param. spcace, 3=equal)
   // Pointer to the DE of the untrimmed surface
  for(i = 0; i < 4; i++) {
    *data_line >> int_fld >> chr_fld;
  }

  //-Read number of curves in the boundary
  int nof_crvs;
  *data_line >> nof_crvs >> chr_fld;
  int* crv_id_table = new int[nof_crvs];

  IgesDirectoryEntry* comp_de;

  //-Read component ids and update references
  for(i = 0; i < nof_crvs; i++) {
    for(j = 0; j < 4; j++) {

      readIntFields(data_line, 1, int_fldb);

      if (j == 0) {
        crv_id_table[i] = int_fldb[0];
        comp_de = getDirectoryEntry(int_fldb[0]);
        comp_de->referingId = de->id;
      }
    }
  }

  delete [] crv_id_table;
  return result;
}


// Curve on a parametric surface entity (Type 142)
bool
InputIges::read_142(IgesDirectoryEntry* de, bool check_only_status)
{
  bool result = true;

  locateParamEntry(de);

  int int_fld;
  char chr_fld;
  istrstream* data_line;

  readDataLine(lineBuffer, dataBuffer);
  data_line = new istrstream(dataBuffer);

  int prmc_entry;
  int srf_entry;
  int srfc_entry;
  *data_line >> int_fld >> chr_fld;     //-Entry-number
  *data_line >> int_fld >> chr_fld;     //-Type of the curve
  *data_line >> srf_entry >> chr_fld;   //-Pointer to surface
  *data_line >> prmc_entry >> chr_fld;  //-Pointer to crv in param space
  *data_line >> srfc_entry >> chr_fld;  //-Pointer to crv on the surface
  *data_line >> int_fld >> chr_fld;     //-Preferred format

  // Update reference to the component
  IgesDirectoryEntry* comp_de = getDirectoryEntry(srfc_entry);
  comp_de->referingId = de->id;

  return result;
}


// This entry defines a bounded surface entity (Type 143)
// It points to the surface and to the entry which defines the
// bounding curves (fex. to an entry 141)
bool
InputIges::read_143(IgesDirectoryEntry* de, bool check_only_status)
{
  bool result = true;

  locateParamEntry(de);

  int i;
  int int_fld;
  char chr_fld;
  istrstream* data_line;

  readDataLine(lineBuffer, dataBuffer);
  data_line = new istrstream(dataBuffer);

  int nof_srfs, nof_bnds, nof_values;

  *data_line >> int_fld >> chr_fld;   // entry-number

  *data_line >> nof_srfs >> chr_fld;    // Nof surface entities
  int* srf_id_table = new int[nof_srfs];
  nof_values = readIntFields(data_line, nof_srfs, srf_id_table);

  *data_line >> nof_bnds >> chr_fld;     // Nof boundary entities
  int* bnd_id_table = new int[nof_bnds];
  nof_values = readIntFields(data_line, nof_bnds, bnd_id_table);

  IgesDirectoryEntry* comp_de;
  int nof_refs;
  int* refs_table;

  // In 2D boundaries
  if ( modelDimension == ECIF_2D ) {
    nof_refs = nof_bnds;
    refs_table = bnd_id_table;

  // In 3D surfaces
  } else {
    nof_refs = nof_srfs;
    refs_table = srf_id_table;
  }

  for(i = 0; i < nof_refs; i++) {
    comp_de = getDirectoryEntry(refs_table[i]);
    comp_de->referingId = de->id;
  }

  delete [] bnd_id_table;
  delete [] srf_id_table;

  return result;
}


// Trimmed (parametric) surface entity (Type 144)
bool
InputIges::read_144(IgesDirectoryEntry* de, bool check_only_status)
{
  bool result = true;

  locateParamEntry(de);

  int i;
  int int_fld, int_fldb[1];
  char chr_fld;

  istrstream* data_line;

  readDataLine(lineBuffer, dataBuffer);
  data_line = new istrstream(dataBuffer);

  int srf_entry;
  int obnd_entry;
  int nof_ibnds;

  *data_line >> int_fld >> chr_fld;     //-Entry-number
  *data_line >> srf_entry >> chr_fld;   //-Pointer to surface
  *data_line >> int_fld >> chr_fld;     //-Type of bounded surface
                                          // 0=the outer bndr is the bndr of D
                                          // 1=otherwise
  *data_line >> nof_ibnds >> chr_fld;   //-Nof inner bndrs defined by simple closed curves
  *data_line >> obnd_entry >> chr_fld;  //-Pointer to outer bndr of the surface
                                        // defining the inner bndr of the srf.

  IgesDirectoryEntry* comp_de;

  //-Read outer boundary on the surface (if defined!)
  if (obnd_entry > 0) {
    comp_de = getDirectoryEntry(obnd_entry);
    comp_de->referingId = de->id;
  }

  //-Read inner boundary defining curves (if defined!)
  if (nof_ibnds > 0) {

    int* bnd_id_table = new int[nof_ibnds];
    int ibnd_entry;

    //-Read component row-ids and corresponding data-rows
    for(i = 0; i < nof_ibnds; i++) {
      readIntFields(data_line, 1, int_fldb);
      bnd_id_table[i] = int_fldb[0];

      comp_de = getDirectoryEntry(int_fldb[0]);
      comp_de->referingId = de->id;
    }

    delete [] bnd_id_table;
  }

  return result;
}
