// ftable5.cpp

#include "ftable.h"
#include <stdio.h>
#include <stdlib.h>
#include "fileio.h"

// define statics
float Ftable::totalDemand[NUM_ROWS];
TABLE * Ftable::table;
TABLE Ftable::tbl;

//----------------------------------------------------------------------------
//   GENERAL HELPER  FUNCTIONS
//----------------------------------------------------------------------------


//----------------------------------------------------------------------------
Ftable * buildFtables( char multFileName[], char tableFileName[], int *num_ftables ){
  Ftable *ftab;
  int num_ftab = getNumFtables( multFileName );
  ftab = new Ftable [num_ftab];
  ftab[0].readTableInfo( multFileName, tableFileName, ftab, num_ftab );
  
  *num_ftables = num_ftab;
  
  return ftab;
}

//----------------------------------------------------------------------------
int getNumFtables(char multFileName[]){
  int num_ftab;
  
  FILE *in_mult;
  char data_line[240];
  
  if ((in_mult = fopen (multFileName, "rt")) == NULL)
  {
    printf("ERROR - multiplier file did not open"); 
  }

  // read header line
  if( fgets( data_line, 120, in_mult ) == NULL ){
    printf("Error reading multiplier file: exit program\n");
    exit(1);
  }
  
  // get number of tables
  if( fgets( data_line, 120, in_mult ) == NULL ){
    printf("Error reading multiplier file: exit program\n");
    exit(1);
  }
  else
  {
    if( sscanf(data_line, "%d",  &num_ftab ) != 1 ) {
      printf("Incorrect number of parameters on first line of multiplier file\n");
      printf("Expecting 1 parametrs: Num of Ftables needed\n");
      printf("Exiting program\n");
      exit(1);
    }
  }

  fclose( in_mult );
  
  return num_ftab;
}


//----------------------------------------------------------------------------
//   CLASS MEMBER FUNCTIONS
//----------------------------------------------------------------------------
Ftable::Ftable(){
  d_volume = 0;
  d_baseRow = 0;
}

//----------------------------------------------------------------------------
int Ftable::readTableInfo(  char multFileName[], char tableFileName[], Ftable *ftab, 
                          int num_ftab ){
  int i, j, k, ct, tab, numTables, ftab_num, total;
  
  
  FILE *in_mult, *in_table;
  char data_line[240];
  
  if ((in_table = fopen (tableFileName, "rt")) == NULL)
  {
    printf("ERROR - ftable file did not open"); 
  }

  // read header line
  if( fgets( data_line, 120, in_table ) == NULL ){
    printf("Error reading table file: exit program\n");
    exit(1);
  }
  
  // read number of tables and allocate memory
  if( fgets( data_line, 120, in_table ) == NULL ){
    printf("Error reading table file: exit program\n");
    exit(1);
  }
  else
  {
    if( sscanf(data_line, "%d",  &numTables ) != 1 ) {
      printf("Incorrect number of parameters on line 1 of ftable file\n");
      exit(1);
    }
  }
  
  allocateTables( numTables );
  
  // read the tables  NOTE:  SETUP FOR MAX OF 4 OUTFLOWS  ------------------------
  for( i = 0; i < numTables; i++ ){
    ct = 1;

    // read header line
    if( fgets( data_line, 120, in_table ) == NULL ){
      printf("Error reading table file: exit program\n");
      exit(1);
    }


    if( fgets( data_line, 120, in_table ) == NULL ){ // get size of first table
      printf("Error reading table file: exit program\n");
      exit(1);
    }
    else {
      if( sscanf(data_line, "%d %d %d", 
        &tab, &(table[i].numRows), &(table[i].numOutflows) ) != 3 ) {
        printf("Incorrect number of parameters on line %d of ftable %d\n", ct, i+1);
        exit(1);
      }
    }
    ct++;

    // read header line
    if( fgets( data_line, 120, in_table ) == NULL ){
      printf("Error reading table file: exit program\n");
      exit(1);
    }
    
    for( j = 0; j < table[i].numRows; j++ ){
      if( fgets( data_line, 120, in_table ) == NULL ){ // get size of first table
        printf("Error reading table file: exit program\n");
        exit(1);
      }
      else {
        total = sscanf(data_line, " %f %f %f %f %f %f %f \n", &(table[i].key[j]), 
          &(table[i].surfArea[j]),  &(table[i].volume[j]),
          &(table[i].outflow[j][0]), &(table[i].outflow[j][1]), 
          &(table[i].outflow[j][2]), &(table[i].outflow[j][3]) );
        if(total < table[i].numOutflows + 3){
          printf("Incorrect number of parameters on line %d of ftable %d\n", ct, i+1);
          exit(1);
        }
      }
      for( k = table[i].numOutflows; k < NUM_OUTFLOWS; k++ )table[i].outflow[j][k] = 0;
      ct++;
    }  // end row
  }
  
  fclose( in_table );
  
  // get multiplier infromation ---------------------------------------------------
  if ((in_mult = fopen (multFileName, "rt")) == NULL) {
    printf("ERROR - multiplier file did not open"); 
  }

  // read header line
  if( fgets( data_line, 120, in_mult ) == NULL ){
    printf("Error reading table file: exit program\n");
    exit(1);
  }
  
  // read number of tables and allocate memory
  if( fgets( data_line, 120, in_mult ) == NULL ){
    printf("Error reading table file: exit program\n");
    exit(1);
  }
  else
  {
    if( sscanf(data_line, "%d",  &num_ftab ) != 1 ) {
      printf("Incorrect number of parameters on line 1 of multiplier file\n");
      exit(1);
    }
  }

  // read header line
  if( fgets( data_line, 120, in_mult ) == NULL ){
    printf("Error reading table file: exit program\n");
    exit(1);
  }

  
  for( i = 0; i < num_ftab; i++ ){
    if( fgets( data_line, 120, in_table ) == NULL ){ // get size of first table
      printf("Error reading table file: exit program\n");
      exit(1);
    }
    else {
      total = sscanf(data_line, " %d %d %f %f %f %f %f %f %f\n", &ftab_num, &(ftab[i].d_type),
        &(ftab[i].d_mult[0]),  &(ftab[i].d_mult[1]),
        &(ftab[i].d_mult[2]), &(ftab[i].d_mult[3]), 
        &(ftab[i].d_mult[4]), &(ftab[i].d_mult[5]),
        &(ftab[i].d_mult[6]));
      if(total < table[ftab[i].d_type - 1].numOutflows+3){
        printf("Incorrect number of parameters on line %d of ftable %d\n", ct, i+1);
        exit(1);
      }
    }
    ftab[i].d_type--;  // set ftable number to use (won't need w/ below)
    ftab[i].d_base = &(table[ftab[i].d_type]);  // pointer to correct base ftable

    ct++;
  }  // end row
  
  
  return 1;
}
//----------------------------------------------------------------------------
int Ftable::allocateTables( int numTypes ){
  
  // allocate memory for base ftables
  try {
    table = new TABLE [numTypes];
    return 1;
  } catch (bad_alloc xa) {
    cout << "Allocation Failure\n";
    return 0;
  }
}

//----------------------------------------------------------------------------
// OVERLOADED FUNCTION:  this form uses values from multiplier file
int Ftable::adjustTable(){
  
  tbl.numRows = d_base->numRows;
  tbl.numOutflows = d_base->numOutflows;
  for( int i = 0; i < tbl.numRows; i++ ){
    tbl.key[i] = d_base->key[i] * d_mult[0];
    tbl.surfArea[i] = d_base->surfArea[i] * d_mult[1];
    tbl.volume[i] = d_base->volume[i] * d_mult[2];
    int col = 3;
    for( int j = 0; j < tbl.numOutflows; j++ ){
      tbl.outflow[i][j] = d_base->outflow[i][j] * d_mult[col];
      col++;
    }
  }
  
  return 0;
  
}

//----------------------------------------------------------------------------
// OVERLOADED FUNCTION:  this form uses values on the fly
int Ftable::adjustTable( float multKey, float multArea, float multVol, float multOut[] ){
  
  // test ------------------
  //TABLE *t = &table[d_type];
  // t is set in read table info   
  tbl.numRows = d_base->numRows;
  tbl.numOutflows = d_base->numOutflows;
  for( int i = 0; i < tbl.numRows; i++ ){
    tbl.key[i] = d_base->key[i] * multKey;
    tbl.surfArea[i] = d_base->surfArea[i] * multArea;
    tbl.volume[i] = d_base->volume[i] * multVol;
    int col = 0;
    for( int j = 0; j < tbl.numOutflows; j++ ){
      tbl.outflow[i][j] = d_base->outflow[i][j] * multOut[col];
      col++;
    }
  }
  
  return 0;
  
}

//----------------------------------------------------------------------------
int Ftable::getDemand(){
  
  for( int i = 0; i < table[d_type].numRows; i++ ){
    totalDemand[i] = 0;
    for( int j = 0; j < table[d_type].numOutflows; j++ ){
      totalDemand[i] += tbl.outflow[i][j];    
    }
  }
  
  return 0;
  
}


//----------------------------------------------------------------------------
void Ftable::endOfdT(RESULTS *r){
  
  int i = d_baseRow;
  float ratio = ( d_volume - tbl.volume[i] ) / ( tbl.volume[i+1] - tbl.volume[i] );
  
  r->volume = d_volume;
  r->key = tbl.key[i] + ratio * (tbl.key[i+1] - tbl.key[i]);
  r->surfArea = tbl.surfArea[i] + ratio * (tbl.surfArea[i+1] - tbl.surfArea[i]);
  
  for( int j = 0; j < tbl.numOutflows; j++ ){
    r->outflow[j] = tbl.outflow[i][j] + ratio * (tbl.outflow[i+1][j] - tbl.outflow[i][j]);
  }
  
}
//----------------------------------------------------------------------------
void Ftable::getFlowVols( int i, float volume, float time, float totalOutVol, 
                         float outVol[] ){
  
  int j;
  
  if( volume >= 0 ){
    float ratio = ( volume - tbl.volume[i] ) / ( tbl.volume[i+1] - tbl.volume[i] );
    for( j = 0; j < tbl.numOutflows; j++ ){
      outVol[j] = time * (tbl.outflow[i][j] + ratio * (tbl.outflow[i+1][j] - tbl.outflow[i][j]) );
    }
  }
  else {  /* ran out of water */
    for( j = 0; j < tbl.numOutflows; j++ ){
      if( totalOutVol > tbl.outflow[0][j] * time){
        outVol[j] = tbl.outflow[0][j] * time;
        totalOutVol -= outVol[j];
      }
      else{
        outVol[j] = totalOutVol;
        totalOutVol = 0;
      }
    }
  }
}




//----------------------------------------------------------------------------
inline float Ftable::getMeanVol( int i, float meanQ ){
  
  return ( (meanQ - totalDemand[i]) / (totalDemand[i+1]-totalDemand[i]) ) 
    * ( tbl.volume[i+1] - tbl.volume[i] ) + tbl.volume[i];
  
}


//----------------------------------------------------------------------------
float Ftable::solveODE( int i, float vol, float inflow, float dT, float *slope, float *ratio ){
  
  if( i == 0 && (inflow*dT + vol) < totalDemand[0]*dT ) return 0;
  
  *slope = ( totalDemand[i+1] - totalDemand[i] ) / ( tbl.volume[i+1] - tbl.volume[i] );
  *ratio = (inflow - totalDemand[i] + *slope * tbl.volume[i]) / *slope;
  float endVol = *ratio + (vol - *ratio) * exp( -*slope * dT );
  return endVol;
  
}

//----------------------------------------------------------------------------
inline float Ftable::timeToVol( float volStart, float volEnd, float slope, float ratio ){
  
  float time = -log( (volEnd - ratio) / (volStart - ratio) ) / slope;
  return time;
  
}



//----------------------------------------------------------------------------
RESULTS Ftable::route( float invol, float dT ){
  
  RESULTS r;
  int go = 0;
  int i = d_baseRow, j;
  float inflow, slope, ratio, vol, time = dT;
  float volStart = d_volume, step, meanVol, totalOutVol;
  float outVol[NUM_OUTFLOWS] = {0.0};
  float oldFlow[NUM_OUTFLOWS] = {0.0};
  
  inflow = invol / dT; // work with rate
//  adjustTable();
  getDemand();
  
  for( j = 0; j < NUM_OUTFLOWS; j++ ) r.outVol[j] = 0.0;
  
  do{
    
    vol = solveODE( i, volStart, inflow, time, &slope, &ratio );
    
    // check if bounded
    go = 0;
    if( vol > tbl.volume[i+1] && i < tbl.numRows - 2 ){
      step = timeToVol( volStart, tbl.volume[i+1], slope, ratio );
      
      totalOutVol = ( inflow*step + volStart - tbl.volume[i+1] );
      meanVol = getMeanVol( i, totalOutVol/step );  // vol assosiated w/ mean total outflow
      getFlowVols( i, meanVol, step, totalOutVol, outVol );
      for( j = 0; j < tbl.numOutflows; j++ ) r.outVol[j] += outVol[j];
      
      time -= step;
      volStart = tbl.volume[i+1];
      i++;
      go = 1;
      //cout << "Moving up the table" << endl;
    }
    else{
      if( vol < tbl.volume[i] ){
        step = timeToVol( volStart, tbl.volume[i], slope, ratio );
        
        totalOutVol = ( inflow*step + volStart - tbl.volume[i] );
        meanVol = getMeanVol( i, totalOutVol/step );  // vol assosiated w/ mean total outflow
        getFlowVols( i, meanVol, step, totalOutVol, outVol );
        for( j = 0; j < tbl.numOutflows; j++ ) r.outVol[j] += outVol[j];
        
        time -= step;
        volStart = tbl.volume[i];
        i--;
        go = 1;
        //cout << "Moving down the table" << endl;
      }
    }
    if( i < 0 || i > tbl.numRows-1 ){
      cout << "Error in Ftable::solve:  trying to compute beyond FTABLE" << endl;
      cout << "Input volume: " << invol << endl;
      cout << "Last table vol: " << tbl.volume[ tbl.numRows - 1 ] << endl;
      cout << "Index i: " << i << endl;
      cout << "tbl.numRows-1: " << tbl.numRows-1 << endl;
      exit(1);
    }
    if( vol > tbl.volume[ tbl.numRows - 1 ] ){
      cout << "Ftable: extrapolating beyond FTABLE" << endl;
//       cout << "Input volume: " << invol << endl;
//       cout << "Last table vol: " << tbl.volume[ tbl.numRows - 1 ] << endl;
    }
    
    
  } while( go );
  
  
  // done  
  d_baseRow = i;
  d_volume = vol;
  
  totalOutVol = ( inflow*time + volStart - vol );
  meanVol = getMeanVol( i, totalOutVol/time );  // vol assosiated w/ mean total outflow
  getFlowVols( i, meanVol, time, totalOutVol, outVol );
  for( j = 0; j < tbl.numOutflows; j++ ) r.outVol[j] += outVol[j];
  endOfdT( &r );  

// printf("f2=%.4f ", outVol[1]);
  
  return r;
  
  
}

