/*
    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 bounda( )

{
  long int in=0, ready=0, iuknwn=0, inod=0, iboun=0, max=0, found=0,
    max_bounda=0, max_bounda_time=0, max_bounda_time_file=0, max_bounda_sine=0,
    ind1=0, ipuknwn=0, iu=0, iu_start=0, iu_end=0,
    inc=0, ninc=0, length=0, range_length=0, unknown=0, force=0,
    time=0, sine=0, use_geom=0, swit=0, swit_node=0, bounda_length=0,
    use_range=0, use_all=0, ifreq=0, nfreq=0, indx=0, max_node=0,
    bounda_time_file=0, ldum=0, idum[1], 
    val[DATA_ITEM_SIZE], integer_range[MRANGE], 
    dof_label[MUKNWN], dof_type[MUKNWN], *node_bounded=NULL;
  double load=0., time0=0., time1=0., load0=0., load1=0., factor=0.,
    time_current=0., total_time=0., dtime=0., amplitude=0., frequency=0.,
    time_start=0., rdum=0., 
    ddum[MDIM], bounda_time[DATA_ITEM_SIZE], *new_node_dof=NULL, 
    *node_dof=NULL, *bounda_sine=NULL, *node_rhside=NULL;

  swit = set_swit(-1,-1,"bounda");
  if ( swit ) pri( "In routine BOUNDA" );

  db_set_int( NODE_BOUNDED, VERSION_NORMAL );

  if ( db_max_index( BOUNDA_UNKNOWN, ldum, VERSION_NORMAL, GET )<0 &&
       db_max_index( BOUNDA_FORCE, ldum, VERSION_NORMAL, GET )<0 ) 
         goto end_of_bounda;
  if ( db_max_index( BOUNDA_TIME, ldum, VERSION_NORMAL, GET )<0 &&
       db_max_index( BOUNDA_SINE, ldum, VERSION_NORMAL, GET )<0 ) 
         goto end_of_bounda;
  db_max_index( NODE, max_node, VERSION_NORMAL, GET );
  if ( max_node<0 ) goto end_of_bounda;

  nodes_in_geometry = get_new_int( 1+max_node );

  db( DOF_LABEL, 0, dof_label, ddum, ldum, VERSION_NORMAL, GET_IF_EXISTS );
  db( DOF_TYPE, 0, dof_type, ddum, ldum, VERSION_NORMAL, GET_IF_EXISTS );
  db( DTIME, 0, idum, &dtime, ldum, VERSION_NEW, GET_IF_EXISTS );
  db( TIME_CURRENT, 0, idum, &time_current, ldum, 
    VERSION_NORMAL, GET_IF_EXISTS );
  total_time = time_current + dtime;
  if ( swit ) pri( "total_time", total_time );

  db_max_index( BOUNDA_TIME, max_bounda_time, VERSION_NORMAL, GET );
  db_max_index( BOUNDA_TIME_FILE, max_bounda_time_file, VERSION_NORMAL, GET );
  db_max_index( BOUNDA_SINE, max_bounda_sine, VERSION_NORMAL, GET ); 
  if ( max_bounda_time>max ) max_bounda = max_bounda_time;
  if ( max_bounda_time_file>max ) max_bounda = max_bounda_time_file;
  if ( max_bounda_sine>max ) max_bounda = max_bounda_sine;
  for ( iboun=0; iboun<=max_bounda; iboun++ ) {
    unknown = db_active_index( BOUNDA_UNKNOWN, iboun, VERSION_NORMAL );
    force   = db_active_index( BOUNDA_FORCE, iboun, VERSION_NORMAL );
    if ( unknown || force ) {
      if ( swit ) pri( "iboun", iboun );
      time = 0;
      sine = 0;
      if      ( db_active_index( BOUNDA_SINE, iboun, VERSION_NORMAL ) ) {
        bounda_sine = db_dbl( BOUNDA_SINE, iboun, VERSION_NORMAL );
        time_start = bounda_sine[0];
        nfreq = ( db_len( BOUNDA_SINE, iboun, VERSION_NORMAL ) - 1 ) / 2;
        ninc = 2;
        sine = 1;
      }
      else if ( db_active_index( BOUNDA_TIME_FILE, iboun, VERSION_NORMAL ) ) {
        db( BOUNDA_TIME_FILE, iboun, &bounda_time_file, ddum, ldum, 
          VERSION_NORMAL, GET_IF_EXISTS );
        if ( bounda_time_file==-YES ) {
          bounda_time_file_apply( iboun, total_time, bounda_time, ninc );
          time = 1;
        }
        else 
          db_error( BOUNDA_TIME_FILE, iboun );
      }
      else {
        db( BOUNDA_TIME, iboun, idum, bounda_time, length, VERSION_NORMAL, 
          GET_IF_EXISTS );
        if ( length<4 ) db_error( BOUNDA_TIME, iboun );
        ninc = length / 2;
        time = 1;
      }

      if ( unknown )
        db( BOUNDA_UNKNOWN, iboun, val, ddum, 
          bounda_length, VERSION_NORMAL, GET );
      else {
        assert( force );
        db( BOUNDA_FORCE, iboun, val, ddum, bounda_length, 
          VERSION_NORMAL, GET );
      }
      if ( db_data_class(val[0])==GEOMETRY ) {
        array_move( val, geometry_ent, 2 );
        array_set( nodes_in_geometry, 0, 1+max_node );
        parallel_sys_routine( &parallel_geometry );
      }

      found = 0;
      for ( inc=0; !found && inc<ninc-1; inc++ ) {

        if ( time ) {
          time0 = bounda_time[inc*2+0];
          load0 = bounda_time[inc*2+1];
          time1 = bounda_time[inc*2+2];
          load1 = bounda_time[inc*2+3];
          if ( time0>=time1 ) db_error( BOUNDA_TIME, iboun );
          if ( swit ) {
            pri( "total_time", total_time );
            pri( "time0", time0 );
            pri( "time1", time1 );
            pri( "load0", load0 );
            pri( "load1", load1 );
          }
          if ( total_time>=(time0-1.e-10) && total_time<=time1 ) {
            found = 1;
            if ( time0==time1 ) load = load0;
            else load = load0 + (load1-load0)*(total_time-time0)/(time1-time0);
          }
        }
        else {
          assert( sine==1 );
          found = time_current>=time_start;
        }

        if ( found ) {

          iu_end = bounda_length - 1;
          use_geom = use_range = use_all = 0;
          if      ( val[0]==-RA ) {
            use_range = 1;
            range_expand( val, integer_range, length, range_length );
            iu_start = length;
          }
          else if ( val[0]==-ALL ) {
            use_all  = 1;
            iu_start = 1;
          }
          else if ( db_data_class(val[0])==GEOMETRY ) {
            use_geom = 1;
            iu_start = 2;
          }
          else
            iu_start = 1;

          for ( in=0, ready=0; !ready; in++ ) {
            found = 0;
            if      ( use_range ) {
              inod = integer_range[in];
              factor = 1.;
              found = 1;
            }
            else if ( use_all ) {
              if ( db_active_index( NODE, in, VERSION_NORMAL ) ) {
                inod = in;
                factor = 1.;
                found = 1;
              }
            }
            else if ( use_geom  ) {
              if ( nodes_in_geometry[in] ) {
                found = 1;
                inod = in;
                geometry( in, ddum, val, found, factor, ddum, rdum,
                  ddum, NODE_START_REFINED, PROJECT_EXACT, VERSION_NORMAL );
              }
            }
            else {
              inod = val[0];
              factor = 1.;
              found = 1;
            }
            if ( found ) {
              if ( db_active_index( NODE, inod, VERSION_NORMAL ) ) {
                swit_node = swit;
                swit = set_swit(-1,inod,"bounda");
                if ( swit ) pri( "inod", inod );
                for ( iu=iu_start; iu<=iu_end; iu++ ) {
                  array_member( dof_label, val[iu], nuknwn, iuknwn );
                  ipuknwn = iuknwn / nder;
                  if ( unknown ) {
                    node_bounded = db_int( NODE_BOUNDED, inod, VERSION_NORMAL );
                    new_node_dof = db_dbl( NODE_DOF, inod, VERSION_NEW );
                    node_dof = db_dbl( NODE_DOF, inod, VERSION_NORMAL );
                    if ( swit ) pri( "node_dof", node_dof, nuknwn );
                    ind1 = ipuknwn*nder + nder - 1;
                    if ( time ) {
                      new_node_dof[iuknwn] = factor * load;
                      if ( derivatives ) new_node_dof[ind1] = 
                        ( new_node_dof[iuknwn] - node_dof[iuknwn] ) / dtime;
                    }
                    else {
                      assert( sine );
                      new_node_dof[iuknwn] = new_node_dof[ind1] = 0.;
                      for ( ifreq=0; ifreq<nfreq; ifreq++ ) {
                        frequency = bounda_sine[1+ifreq*2+0];
                        amplitude = bounda_sine[1+ifreq*2+1];
                        new_node_dof[iuknwn] += factor * amplitude *
                          sin( 2. * PIRAD * frequency * total_time );
                        if ( derivatives ) new_node_dof[ind1] += 
                          factor * amplitude * 2. * PIRAD * frequency *
                          cos( 2. * PIRAD * frequency * total_time );
                      }
                    }
                    node_bounded[ipuknwn] = 1;
                    if ( dof_type[iuknwn]==-MATERI_DISPLACEMENT ) {
                      indx = vel_indx + ( iuknwn - dis_indx );
                      new_node_dof[indx] = 
                      ( new_node_dof[indx] - node_dof[indx] ) / dtime;
                      node_bounded[indx/nder] = 1;
                    }
                    if ( dof_type[iuknwn]==-WAVE_SCALAR ) {
                      new_node_dof[fscal_indx] = 
                      ( new_node_dof[scal_indx] - node_dof[scal_indx] ) / dtime;
                      node_bounded[fscal_indx/nder] = 1;
                    }
                    if ( swit ) {
                      pri( "node_bounded", node_bounded, npuknwn );
                      pri( "new_node_dof", new_node_dof, nuknwn );
                    }
                  }
                  else if ( force ) {
                    if ( dof_type[iuknwn]==-MATERI_DISPLACEMENT || 
                      dof_type[iuknwn]==-WAVE_SCALAR ) 
                      db_error( BOUNDA_FORCE, iboun );
                    node_rhside = db_dbl( NODE_RHSIDE, inod, VERSION_NORMAL );
                    if      ( time )
                      node_rhside[ipuknwn] = factor * load;
                    else {
                      assert( sine );
                      node_rhside[ipuknwn] = 0.;
                      for ( ifreq=0; ifreq<nfreq; ifreq++ ) { 
                        frequency = bounda_sine[1+ifreq*2+0];
                        amplitude = bounda_sine[1+ifreq*2+1];
                        node_rhside[ipuknwn] += factor*amplitude*
                          sin(2.*PIRAD*frequency*total_time);
                      }
                    }
                  }
                }
                swit = swit_node;
              }
            }
            if      ( use_range )
              ready = ( (in+1)==range_length );
            else if ( use_all )
              ready = ( in==max_node );
            else if ( use_geom )
              ready = ( in==max_node );
            else
              ready = 1;
          }
        }
      }
    }
  }

  delete[] nodes_in_geometry;

  end_of_bounda:

  if ( swit ) pri( "Out routine BOUNDA" );

}

void bounda_time_file_apply( long int iboun, double total_time,
  double bounda_time[], long int &ninc )

{
  long int n=0, found=0;
  char str[MCHAR], filename[MCHAR];
  double old_load=0., old_time=0., new_load=0., new_time=0.;

  strcpy( filename, long_to_a(iboun,str) );
  strcat( filename, ".bounda" );
  ifstream in( filename );
  if ( !in ) db_error( BOUNDA_TIME_FILE, iboun );

  ninc = 0;
  for ( ; !in.eof() && !found ; ) {
    n++;
    old_load = new_load;
    old_time = new_time;
    if ( !(in>>new_time) || !(in>>new_load) ) 
      db_error( BOUNDA_TIME_FILE, iboun );
    if ( n>1 && total_time>=old_time && total_time<=new_time ) {
      found = 1;
      ninc = 2;
      bounda_time[0] = old_time;
      bounda_time[1] = old_load;
      bounda_time[2] = new_time;
      bounda_time[3] = new_load;
    }
  }
  if ( ninc<2 ) db_error( BOUNDA_TIME_FILE, iboun );

  in.close();
}
