/*
    Copyright (C) 1998  Dennis Roddeman
    email: dennis.roddeman@uibk.ac.at

    This program is free software; you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
    the Free Software Foundation; either version 2 of the License, or
    (at your option) any later version.

    This program is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU General Public License for more details.

    You should have received a copy of the GNU General Public License
    along with this program; if not, write to the Free Software Foundation 
    59 Temple Place, Suite 330, Boston, MA, 02111-1307, USA
*/

#include "tochnog.h"

void merge( void )

{
  long int element=0, merge=0, max_element=0, max_node=0, tmp_max_node=-1,
    tmp_node_number=0, swit=0, inol=0, inod=0, nnol=0, length=0, 
    icontrol=0, ldum=0, idum[1], el[MNOL+1], nodes[MNOL], 
    *old_node_numbers=NULL, *ordered_nodes=NULL, *new_node_numbers=NULL;
  double ddum[1], node[MDIM];

  db( ICONTROL, 0, &icontrol, ddum, ldum, VERSION_NORMAL, GET );
  if ( db_active_index( CONTROL_MESH_MERGE, icontrol, VERSION_NORMAL ) ) {
    db( CONTROL_MESH_MERGE, icontrol, &merge, ddum, ldum, VERSION_NORMAL, GET );
    if ( merge==-YES ) {
      swit = set_swit(-1,-1,"merge");
      if ( swit ) pri( "In routine MERGE" );

      db_max_index( ELEMENT, max_element, VERSION_NORMAL, GET ); 
      db_max_index( NODE, max_node, VERSION_NORMAL, GET ); 

      ordered_nodes = get_new_int(1+max_node);
      old_node_numbers = get_new_int(1+max_node);
      new_node_numbers = get_new_int(1+max_node);

      array_set( new_node_numbers, -1, 1+max_node);
      for ( inod=0; inod<=max_node; inod++ ) {
        if ( db_active_index( NODE, inod, VERSION_NORMAL ) ) {
          db( NODE, inod, idum, node, ldum, VERSION_NORMAL, GET );
          tmp_node_number = check_new_node( ordered_nodes, 
            tmp_max_node, node, ADD );
          if ( tmp_node_number<0 ) {
            tmp_max_node++;
            old_node_numbers[tmp_max_node] = inod;
            db( NODE, tmp_max_node, idum, node, ndim, VERSION_TMP, PUT );
          }
          else {
            new_node_numbers[inod] = old_node_numbers[tmp_node_number];
            delete_node( inod, VERSION_NORMAL );
          }
        }
      }

      for ( element=0; element<=max_element; element++ ) {
        if ( db_active_index( ELEMENT, element, VERSION_NORMAL ) ) {
          db( ELEMENT, element, el, ddum, length, VERSION_NORMAL, GET );
          nnol = length - 1; array_move( &el[1], nodes, nnol );
          for ( inol=0; inol<nnol; inol++ ) {
            inod = nodes[inol];
            if ( new_node_numbers[inod]>=0 )
             nodes[inol] = new_node_numbers[inod];
          }
          array_move( nodes, &el[1], nnol );
          db( ELEMENT, element, el, ddum, length, VERSION_NORMAL, PUT );
        }
      }

      delete[] ordered_nodes;
      delete[] old_node_numbers;
      delete[] new_node_numbers;

      db_version_delete( VERSION_TMP );

      mesh_has_changed( VERSION_NORMAL );

      if ( swit ) pri( "Out routine MERGE" );
    }
  }
}

long int check_new_node( long int ordered_nodes[], long int tmp_max_node, 
  double tmp_node[], long int task ) 

{
  long int inod=0, jnod=0, ready=0, lower=0, higher=0, tmp_node_number=-1;
  double tmp=0., radius=0., tmp_radius=0., diff_coord[MDIM], *tmp_coord=NULL;

    // order by radius
  if ( tmp_max_node==-1 )
    ordered_nodes[0] = 0; 
  else {
    tmp_radius = array_size( tmp_node, ndim );
    ready = 0; lower = -1; higher = tmp_max_node + 1;
      /* lower becomes the last node with a smaller radius.
         higher becomes the first node with larger 
         or equal radius */
    while ( !ready ) {
      inod = ( lower + higher ) / 2;
      jnod = ordered_nodes[inod];
      tmp_coord = db_dbl( NODE, jnod, VERSION_TMP );
      radius = array_size( tmp_coord, ndim );
      if      ( lower==higher-1 )
        ready = 1;
      else if ( tmp_radius>radius+EPS_COORD )
        lower = inod;
      else if ( tmp_radius<radius+EPS_COORD )
        higher = inod;
      else
        ready = 1;
    }
      // check if coordinate already exists
    ready = 0;
    for ( inod=lower+1; !ready; inod++ ) {
      if ( inod>tmp_max_node )
        ready = 1;
      else {
        jnod = ordered_nodes[inod];
        tmp_coord = db_dbl( NODE, jnod, VERSION_TMP );
        array_subtract( tmp_coord, tmp_node, diff_coord, ndim );
        tmp = array_inproduct( diff_coord, diff_coord, ndim );
        if ( tmp<EPS_COORD ) tmp_node_number = jnod;
        radius = array_size( tmp_coord, ndim );
        ready = 
          tmp_node_number>0 || scalar_dabs(radius-tmp_radius)>EPS_COORD;
      }
    }
    if ( task==ADD ) {
      if ( tmp_node_number<0 ) {
        for ( inod=tmp_max_node; inod>lower; inod-- )
          ordered_nodes[inod+1] = ordered_nodes[inod];
        ordered_nodes[lower+1] = tmp_max_node + 1;
      }
    }
    else 
      assert( task==CHECK );
  }

  return tmp_node_number;
}
