/*
    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"

#define MDEFINE 10
#define MSTRING 10

long int using_define=0, idefine=0, ndefine=0, istring=0, define_nstring[MDEFINE];
char define_words[MDEFINE][MCHAR], define_strings[MDEFINE][MSTRING][MCHAR];

void input( char nam[] )

{
  long int i=0, n=0, l=0, iv=0, length=0, max=0, idat=0, itmp=0, idim=0,
    ready=0, index=0, range_length=0, last_data_value=0, any_point=0,
    unknown_indx=0, ninitia=0, ldum=0, integer_range[MRANGE], range[DATA_ITEM_SIZE], 
    ival[DATA_ITEM_SIZE+1], dof_label[DATA_ITEM_SIZE], dof_type[MUKNWN], 
    dof_principal[MUKNWN], dof_scal_vec_mat[MUKNWN], 
    initialization_values[DATA_ITEM_SIZE];
  double ddum[1], dval[DATA_ITEM_SIZE+1];
  char str[MCHAR], str_tmp[MCHAR];

  array_set( initialization_values, -EMPTY, DATA_ITEM_SIZE );
  array_set( dof_principal, -NO, MUKNWN );
  array_set( define_nstring, 0, MDEFINE );

    // append .dat
  l = strlen( nam );
  strcpy( data_file_base, "" );
  for ( i=0; i<l && !any_point; i++ ) {
    if ( nam[i]=='.' ) any_point = 1;
    else strncat( data_file_base, &nam[i], 1 );
  }
  if ( !any_point ) strcat( nam, ".dat" );

    // store data file name
  strcpy( data_file, nam );

  ofstream out( "tn.log", ios::app );
  out << "\n\nCalculation with data file " << data_file << " starts.";
  out.close();

  if ( freopen( nam, "r", stdin ) == NULL ) {
    cout << "\nError: cannot open " << nam << ".\n";
    exit(TN_EXIT_STATUS);
  }

    /* read initialization part */
  strcpy(str,"");
  while( strcmp(str,"end_initia") ) {
    npuknwn += n; 
    nuknwn = npuknwn*nder;
    if ( npuknwn>MPUKNWN ) {
      cout << "\nError: MPUKNWN too small. Increase it in tochnog.h and recompile.\n";
      exit(TN_EXIT_STATUS);
    }
    if ( nuknwn>MUKNWN ) {
      cout << "\nError: MUKNWN too small. Increase it in tochnog.h and recompile.\n";
      exit(TN_EXIT_STATUS);
    }
    unknown_indx += n * nder;
    if ( !(cin >> str) ) {
      pri( "\nError in initialization part." );
      exit(TN_EXIT_STATUS);
    }
    input_skip_comment( str ); if ( echo ) cout << str << " ";
    ninitia++; 
    if ( ninitia>DATA_ITEM_SIZE ) {
      pri( "\nError in initialization part. DATA_ITEM_SIZE too small." );
      exit(TN_EXIT_STATUS);
    }
    strcpy(initialization_names[ninitia-1],str);
    if      ( !strcmp(str,"echo") ) {
      if ( ninitia!=1 ) {
        pri( "\nError in initialization part." );
        exit(TN_EXIT_STATUS);
      }
      if ( !(cin >> str_tmp) ) {
        pri( "\nError in initialization part." );
        exit(TN_EXIT_STATUS);
      }
      input_skip_comment( str_tmp );
      if ( !strcmp(str_tmp,"-yes") ) {
        echo = 1;
        initialization_values[ninitia-1] = -YES;
        cout << "echo -yes";
      }
      else {
        if ( strcmp(str_tmp,"-no") ) {
          pri( "\nError in initialization part." );
          exit(TN_EXIT_STATUS);
        }
        initialization_values[ninitia-1] = -NO;
      }
    }
    else if ( !strcmp(str,"number_of_space_dimensions") || !strcmp(str,"ndim")) {
      if ( ninitia!=2 ) {
        pri( "\nError in initialization part." );
        exit(TN_EXIT_STATUS);
      }
      if ( !(cin >> ndim) ) {
        pri( "\nError in initialization part." );
        exit(TN_EXIT_STATUS);
      }
      if ( echo ) cout << ndim;
      initialization_values[ninitia-1] = ndim;
      if ( ndim<=0 || ndim>MDIM ) {
        pri( "\nError in initialization part." );
        exit(TN_EXIT_STATUS);
      }
    }
    else if ( !strcmp(str,"derivatives") ) {
      if ( ninitia!=3 ) {
        pri( "\nError in initialization part." );
        exit(TN_EXIT_STATUS);
      }
      derivatives = 1;
      nder = 1 + ndim + 1;
    }
    else if ( !strcmp(str,"condif_temperature") ) {
      condif_temperature = 1;
      temp_indx = unknown_indx;
      n = 1;
      array_set( &dof_type[temp_indx], -CONDIF_TEMPERATURE, n*nder );
      array_set( &dof_scal_vec_mat[unknown_indx], -SCALAR, n*nder );
      array_set( &dof_principal[temp_indx], nprinc, n*nder );
      nprinc += n;
    }
    else if ( !strcmp(str,"groundflow_pressure") ) {
      groundflow_pressure = 1;
      pres_indx = unknown_indx;
      n = 1;
      array_set( &dof_type[pres_indx], -GROUNDFLOW_PRESSURE, n*nder );
      array_set( &dof_scal_vec_mat[unknown_indx], -SCALAR, n*nder );
      array_set( &dof_principal[pres_indx], nprinc, n*nder );
      nprinc += n;
    }
    else if ( !strcmp(str,"groundflow_velocity") ) {
      groundflow_gvelocity = 1;
      gvel_indx = unknown_indx;
      n = ndim;
      array_set( &dof_type[gvel_indx], -GROUNDFLOW_VELOCITY, n*nder );
      array_set( &dof_scal_vec_mat[unknown_indx], -VECTOR, n*nder );
    }
    else if ( !strcmp(str,"materi_damage") ) {
      materi_damage = 1;
      dam_indx = unknown_indx;
      n = 1;
      array_set( &dof_type[dam_indx], -MATERI_DAMAGE, n*nder );
      array_set( &dof_scal_vec_mat[unknown_indx], -SCALAR, n*nder );
    }
    else if ( !strcmp(str,"materi_density") ) {
      materi_density = 1;
      dens_indx = unknown_indx;
      n = 1;
      array_set( &dof_type[dens_indx], -MATERI_DENSITY, n*nder );
      array_set( &dof_scal_vec_mat[unknown_indx], -SCALAR, n*nder );
    }
    else if ( !strcmp(str,"materi_displacement") ) {
      materi_displacement = 1;
      dis_indx = unknown_indx;
      n = ndim;
      array_set( &dof_type[dis_indx], -MATERI_DISPLACEMENT, n*nder );
      array_set( &dof_scal_vec_mat[unknown_indx], -VECTOR, n*nder );
    }
    else if ( !strcmp(str,"materi_history_variables") ) {
      if ( !(cin >> materi_history_variables) ) {
        pri( "\nError in initialization part." );
        exit(TN_EXIT_STATUS);
      }
      assert( materi_history_variables>0 );
      if ( echo ) cout << materi_history_variables;
      initialization_values[ninitia-1] = materi_history_variables;
      hisv_indx = unknown_indx;
      n = materi_history_variables;
      array_set( &dof_type[hisv_indx], -MATERI_HISTORY_VARIABLES, materi_history_variables*nder );
      array_set( &dof_scal_vec_mat[unknown_indx], -SCALAR, n*nder );
    }
    else if ( !strcmp(str,"materi_maxwell_stress") ) {
      if ( !(cin >> materi_maxwell_stress) ) {
        pri( "\nError in initialization part." );
        exit(TN_EXIT_STATUS);
      }
      assert( materi_maxwell_stress>0 );
      assert( materi_maxwell_stress<MMAXWELL );
      if ( echo ) cout << materi_maxwell_stress;
      initialization_values[ninitia-1] = materi_maxwell_stress;
      mstres_indx = unknown_indx;
      n = 6 * materi_maxwell_stress;
      array_set( &dof_type[mstres_indx], -MATERI_MAXWELL_STRESS, n*nder );
      array_set( &dof_scal_vec_mat[unknown_indx], -MATRIX, n*nder );
    }
    else if ( !strcmp(str,"materi_plasti_kappa") ) {
      materi_plasti_kappa = 1;
      kap_indx = unknown_indx;
      n = 1;
      array_set( &dof_type[kap_indx], -MATERI_PLASTI_KAPPA, n*nder );
      array_set( &dof_scal_vec_mat[unknown_indx], -SCALAR, n*nder );
    }
    else if ( !strcmp(str,"materi_plasti_rho") ) {
      materi_plasti_rho = 1;
      rho_indx = unknown_indx;
      n = 6;
      array_set( &dof_type[rho_indx], -MATERI_PLASTI_RHO, n*nder );
      array_set( &dof_scal_vec_mat[unknown_indx], -MATRIX, n*nder );
    }
    else if ( !strcmp(str,"materi_strain_elasti") ) {
      materi_strain_elasti = 1;
      epe_indx = unknown_indx;
      n = 6;
      array_set( &dof_type[epe_indx], -MATERI_STRAIN_ELASTI, n*nder );
      array_set( &dof_scal_vec_mat[unknown_indx], -MATRIX, n*nder );
    }
    else if ( !strcmp(str,"materi_strain_plasti") ) {
      materi_strain_plasti = 1;
      epp_indx = unknown_indx;
      n = 6;
      array_set( &dof_type[epp_indx], -MATERI_STRAIN_PLASTI, n*nder );
      array_set( &dof_scal_vec_mat[unknown_indx], -MATRIX, n*nder );
    }
    else if ( !strcmp(str,"materi_strain_total") ) {
      materi_strain_total = 1;
      ept_indx = unknown_indx;
      n = 6;
      array_set( &dof_type[ept_indx], -MATERI_STRAIN_TOTAL, n*nder );
      array_set( &dof_scal_vec_mat[unknown_indx], -MATRIX, n*nder );
    }
    else if ( !strcmp(str,"materi_stress") ) {
      materi_stress = 1;
      stres_indx = unknown_indx;
      n = 6;
      array_set( &dof_type[stres_indx], -MATERI_STRESS, n*nder );
      array_set( &dof_scal_vec_mat[unknown_indx], -MATRIX, n*nder );
    }
    else if ( !strcmp(str,"materi_velocity") ) {
      materi_velocity = 1;
      vel_indx = unknown_indx;
      n = ndim;
      array_set( &dof_type[vel_indx], -MATERI_VELOCITY, n*nder );
      array_set( &dof_scal_vec_mat[unknown_indx], -VECTOR, n*nder );
      for ( idim=0; idim<ndim; idim++ ) {
        array_set( &dof_principal[vel_indx+idim*nder], nprinc, nder );
        nprinc += 1;
      }
    }
    else if ( !strcmp(str,"materi_void_fraction") ) {
      materi_void_fraction = 1;
      void_indx = unknown_indx;
      n = 1;
      array_set( &dof_type[void_indx], -MATERI_VOID_FRACTION, n*nder );
      array_set( &dof_scal_vec_mat[unknown_indx], -SCALAR, n*nder );
    }
    else if ( !strcmp(str,"residue") ) {
      residue = 1;
      res_indx = unknown_indx;
      n = 1;
      array_set( &dof_type[res_indx], -RESIDUE, n*nder );
      array_set( &dof_scal_vec_mat[unknown_indx], -SCALAR, n*nder );
    }
    else if ( !strcmp(str,"wave_scalar") ) {
      wave_scalar = 1;
      scal_indx = unknown_indx;
      n = 1;
      array_set( &dof_type[scal_indx], -WAVE_SCALAR, n*nder );
      array_set( &dof_scal_vec_mat[unknown_indx], -SCALAR, n*nder );
    }
    else if ( !strcmp(str,"wave_fscalar") ) {
      wave_fscalar = 1;
      fscal_indx = unknown_indx;
      n = 1;
      array_set( &dof_type[fscal_indx], -WAVE_FSCALAR, n*nder );
      array_set( &dof_scal_vec_mat[unknown_indx], -SCALAR, n*nder );
      array_set( &dof_principal[fscal_indx], nprinc, n*nder );
      nprinc += n;
    }
    else if ( strcmp(str,"end_initia") ) {
      pri( "\nError in initialization part." );
      exit(TN_EXIT_STATUS);
    }
    if ( echo ) cout << "\n";
  }
  nuknwn = npuknwn * nder;
  if ( nuknwn>DATA_ITEM_SIZE ) {
    cout << "\nError: too many unknowns per node: " << nuknwn << ".\n";
    cout << "The maximum is " << DATA_ITEM_SIZE << ".\n";
    exit(TN_EXIT_STATUS);
  }
  if ( ndim<0 || ndim>MDIM ) {
    pri( "\nError: number_of_space_dimensions is not correct." );
    exit(TN_EXIT_STATUS);
  }

  db_initialize( dof_type, dof_label );
  db( INITIALIZATION_VALUES, 0, initialization_values, ddum, 
    ninitia, VERSION_NORMAL, PUT );

    /* check initialization part */
  if ( residue ) 
    check_unknown( "derivatives", YES, CHECK_USAGE_AND_ERROR );
  if ( materi_damage )  {
    check_unknown( "materi_strain_elasti", YES, CHECK_USAGE_AND_ERROR );
    check_unknown( "materi_stress", YES, CHECK_USAGE_AND_ERROR );
  }
  if ( materi_displacement ) 
    check_unknown( "materi_velocity", YES, CHECK_USAGE_AND_ERROR );
  if ( wave_fscalar ) 
    check_unknown( "wave_scalar", YES, CHECK_USAGE_AND_ERROR );
  if ( wave_scalar ) 
    check_unknown( "wave_fscalar", YES, CHECK_USAGE_AND_ERROR );

    /* read data part */
  input_read_string( echo, str );
  while ( strcmp(str,"end_data") ) {

      // data item name
    input_skip_comment( str );
    if ( echo ) cout << str << " ";
    idat = db_number( str ); 
    if ( idat<0 ) {
      pri( "\nError in data part." );
      exit(TN_EXIT_STATUS);
    }
    check( idat, CHECK_USAGE_AND_ERROR );

    if ( db_print_only( idat ) ) {
      pri( "\nError in data part. Data item ", idat );
      pri( "is meant for printing only." );
      exit(TN_EXIT_STATUS);
    }

      // index
    if ( db_no_index(idat ) )
      range[0] = 0;
    else {
      input_read_string( echo, str );
      input_skip_comment(str);
      if ( echo ) cout << " " << str << " ";
      if ( !strcmp(str,"-ra") ) range_scan( range, range_length );
      else {
        if ( !string_isinteger(str) ) {
          pri( "\nError in data part." );
          exit(TN_EXIT_STATUS);
        }
        range[0] = atoi(str);
      }
    }

      // data item values
    length = last_data_value = 0;
    for ( iv=0; !last_data_value; iv++ ) {
      input_read_string( echo, str );
      input_skip_comment( str );
      if      ( db_fixed_length(idat) )
        last_data_value = ( iv==(db_data_length(idat)) );
      else if ( !strcmp(str,"end_data") )
        last_data_value = 1;
      else
       last_data_value = ( db_number(str)>=0 );
      if ( !last_data_value ) {
        if ( echo ) cout << " " << str << " ";
        if ( db_type(idat)==INTEGER ) {
          if ( str[0]=='-' ) {
            itmp = db_number( &str[1] );
            if ( itmp<0 ) {
              pri( "\nError in data part." );
              exit(TN_EXIT_STATUS);
            }
            check( itmp, CHECK_USAGE_AND_ERROR );
            ival[iv] = -itmp;
          }
          else {
            if ( !string_isinteger(str) ) {
              pri( "\nError in data part." );
              exit(TN_EXIT_STATUS);
            }
            ival[iv] = atoi(str);
          }
        }
        else if ( db_type(idat)==DOUBLE ) {
          if ( !string_isdouble(str) ) {
            pri( "\nError in data part." );
            exit(TN_EXIT_STATUS);
          }
          dval[iv] = atof(str);
        }
        length = iv + 1;
        if ( length>DATA_ITEM_SIZE ) {
          pri( "\nError in data part. Maximum data item size exceeded." );
          exit(TN_EXIT_STATUS);
        }
      }
    }
    if ( echo ) cout << "\n";

      // store 
    if ( range[0]==-RA ) range_expand( range, integer_range, ldum, range_length );
    db_max_index( idat, max, VERSION_NORMAL, GET );
    for ( i=0, ready=0; !ready; i++) {
      if ( range[0]==-RA ) 
        index = integer_range[i];
      else 
        index = range[0];
      if ( db_active_index(idat,index,VERSION_NORMAL) ) db_error( idat, index );
      db( idat, index, ival, dval, length, VERSION_NORMAL, PUT );
      if ( range[0]==-RA ) 
        ready = ( (i+1)==range_length );
      else 
        ready = 1;
    }
  }
  if ( echo ) cout << "end_data" << "\n\n";

  if ( nuknwn>0 ) {
    db( DOF_LABEL, 0, dof_label, ddum, nuknwn, VERSION_NORMAL, PUT );
    db( DOF_TYPE, 0, dof_type, ddum, nuknwn, VERSION_NORMAL, PUT );
    db( DOF_SCAL_VEC_MAT, 0, dof_scal_vec_mat, ddum, nuknwn, VERSION_NORMAL, PUT );
    db( DOF_PRINCIPAL, 0, dof_principal, ddum, nuknwn, VERSION_NORMAL, PUT );
  }

}

void input_read_string( long int echo, char str[] )

{

  long int i=0, ready=0, minus=0;
  char str_tmp[MCHAR];

  start_of_input_read_string:

  if ( !using_define ) {
    if ( !(cin >> str) ) {
      pri( "\nError in data part." );
      exit(TN_EXIT_STATUS);
    }
    string_convert_to_lower_case( str );
  }

  if ( !strcmp(str,"start_define") ) {
    if ( echo ) cout << "\nstart_define\n";
    if ( ndefine==MDEFINE ) {
      cout << "\nError: maximal number of defines is " << MDEFINE << "\n";
      exit(TN_EXIT_STATUS);
    }
      // read word
    if ( !(cin >> str) ) {
      pri( "\nError in data part." );
      exit(TN_EXIT_STATUS);
    }
    string_convert_to_lower_case( str );
    input_skip_comment( str );
    if ( echo ) cout << str << " ";
    strcpy( define_words[ndefine], str );
      // read strings
    ready = 0;
    do {
      if ( !(cin >> str) ) {
        pri( "\nError in data part." );
        exit(TN_EXIT_STATUS);
      }
      string_convert_to_lower_case( str );
      input_skip_comment( str );
      if ( strcmp("end_define",str) ) {
        if ( echo ) cout << " " << str << " ";
        istring = define_nstring[ndefine];
        if ( istring==MSTRING ) {
          cout << "\nError: maximal number of strings is " << MSTRING << "\n";
          exit(TN_EXIT_STATUS);
        }
        strcpy( define_strings[ndefine][istring], str );
        istring++;
        define_nstring[ndefine] = istring;
      }
      else {
        if ( echo ) cout << "\nend_define\n";
        ready = 1;
      }
    }
    while ( !ready );
    ndefine++;
    goto start_of_input_read_string;
  }
  else {
    minus = 0;
    if ( !using_define ) {
      if ( str[0]=='-' ) {
        minus = 1;
        strcpy( str_tmp, &str[1] );
      }
      else
        strcpy( str_tmp, &str[0] );
      for ( i=0; i<ndefine; i++ ) {
        if ( !strcmp( define_words[i], str_tmp ) ) {
          using_define = 1;
          idefine = i;
          istring = 0;
        }
      }
    }
    if ( using_define ) {
      if ( minus ) 
        strcpy( str, "-" );
      else 
        strcpy( str, "" );
      strcat( str, define_strings[idefine][istring] );
      istring++;
      if ( istring==define_nstring[idefine] ) using_define = 0;
    }
  }

}

void input_skip_comment( char str[] )

{
  long int i=0, l=0, nleft=0, nright=0;
  char *s, tmp_str[MCHAR];

  s = str;

  if ( *s=='(' ) {
    loop_comment:
    l = strlen(str);
    if ( cin.eof() ) {
      pri( "\nError in data file. Comment not closed." );
      exit(TN_EXIT_STATUS);
    }
    for ( i=0; i<=l-1; i++ ) {
      if ( *(s+i)=='(' ) nleft++;
      else if ( *(s+i)==')' ) nright++;
      if ( nright==nleft ) {
        if ( i==l-1 ) {
          if ( cin.eof() ) {
            pri( "\nError in data file. Comment not closed." );
            exit(TN_EXIT_STATUS);
          }
          else {
            input_read_string( 0, str );
              /* new part also starts with comment? */
            if ( str[0]=='(' ) goto loop_comment;
            string_convert_to_lower_case( str );
            return;
          }
        }
        else {
          strcpy( tmp_str, str );
          strcpy( str, &tmp_str[i+1] );
            /* new part also starts with comment? */
          if ( str[0]=='(' ) goto loop_comment;
          string_convert_to_lower_case( str );
          return;
        }
      }
    }
    input_read_string( 0, str );

    s = str;
    goto loop_comment;
  }

  string_convert_to_lower_case( str );

  return;

}
