/*-----------------------------------------------------------------------------

Copyright (C) 2018, 2019

A. Ronald Gallant
Post Office Box 659
Chapel Hill NC 27514-0659
USA

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, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.

-------------------------------------------------------------------------------

Function      Read and write data stored as a table.

Syntax        #include "libscl.h"
              #include "rwtable.h"
              INTEGER readtable(const char* filename, realmat& a);
              INTEGER readtable(const char* filename, realmat& a, 
                vector<string>& header);
              INTEGER readtable(istream& stream, realmat& a);
              INTEGER readtable(istream& stream, realmat& a, 
                vector<std::string>& header);
              INTEGER writetable (const char* filename, realmat& a, 
                INTEGER width, INTEGER precision, char fc='f');
              INTEGER writetable(const char* filename, realmat& a, 
                vector<string>& header, 
		INTEGER width, INTEGER precision, char fc='f');
              INTEGER writetable(ostream& stream, realmat& a, 
                INTEGER width, INTEGER precision, char fc='f');
              INTEGER writetable(ostream& stream, realmat& a, 
                vector<std::string>& header, 
		INTEGER width, INTEGER precision, char fc='f');

Prototype in  libscl.h

Description   Reads newline terminated rows of white space separated data
              into a realmat from a file.
              Writes a realmat to a file as newline terminated rows of
              white space separated data with user specified width,
              precision, and format code.  Codes may be 'f' for fixed,
	      'e' or 'E' for exponential, or 'g' or 'G' for shortest.

Return value  Number of data items read or written, not counting header.

Remark        The data may have a header line if indicated by the call.
              A space is written between items so field width is one
              larger than width specified in the call.

Functions     Library: std::vector, std::string
called        libscl:  realmat, cutstr, isREAL, fmt

------------------------------------------------------------------------------*/

#include "libscl.h"

using namespace scl;
using namespace std;

namespace scl {

  INTEGER readtable(const char* filename, realmat& a)
  {
    ifstream fin;
    fin.open(filename);
    if (!fin) {
      warn("Warning, readtable, read error");
      return 0;
    }
    return readtable(fin, a);
  }
  
  INTEGER readtable(const char* filename, realmat& a, vector<string>& header)
  {
    ifstream fin;
    fin.open(filename);
    if (!fin) {
      warn("Warning, readtable, read error");
      return 0;
    }
    return readtable(fin, a, header);
  }
  
  INTEGER readtable(istream& fin, realmat& a)
  {
    string line;
    getline(fin,line);
  
    if (!fin.good()) {
      warn("Warning, readtable, read error");
      return 0;
    }
  
    vector<string> first = cutstr(line,' ');
  
    INTEGER cols = first.size();
  
    realmat r;                           // r will have rowwise storage
  
    REAL x;
  
    INTEGER count = 0;
  
    for (vector<string>::const_iterator p = first.begin(); p<first.end(); ++p){
      if (!isREAL(*p,x)) {
        stringstream ss;
        ss << "Warning, readtable, are you sure this not a header" << '\n';
        ss << '\t';
        for (vector<string>::const_iterator p=first.begin(); p<first.end();++p){
          ss << *p << ' ';
        }
        ss << '\n';
        ss << "Warning, readtable, read error";
        warn(ss.str());
        return 0;
      } 
      r.push_back(x);
      ++count;
    }
  
    while (fin >> x) {
      r.push_back(x);
      ++count;
    }
  
    INTEGER rows = count/cols;
  
    if (count != rows*cols) {
      warn("Warning, readtable, missing values");
      return 0;
    }
  
    a.resize(rows,cols); 
  
    for (INTEGER j=1; j<=cols; ++j) {
      for (INTEGER i=1; i<=rows; ++i) {
        a(i,j) = r[cols*(i-1)+j];         // transpose to columnwise storage
      }
    }
  
    return count;
  }
  
  INTEGER readtable(istream& fin, realmat& a, vector<string>& header)
  {
    string line;
    getline(fin,line);
    header = cutstr(line,' ');
  
    bool realnos = true;
    for (vector<string>::const_iterator p=header.begin(); p<header.end(); ++p){
      if (!isREAL(*p)) realnos = false; 
    }
  
    if (realnos) {
      stringstream ss;
      ss << "Warning, readtable, are you sure this is a header" << '\n';
      ss << '\t';
      for (vector<string>::const_iterator p=header.begin(); p<header.end();++p){
        ss << *p << ' ';
      }
      warn(ss.str());
    }
  
    return readtable(fin, a);
  }
  
  INTEGER writetable(const char* filename, const realmat& a,
    INTEGER width, INTEGER precision, char fc)
  {
    ofstream fout;
    fout.open(filename);
    if (!fout) {
      warn("Warning, writetable, write error");
      return 0;
    }
    return writetable(fout, a, width, precision, fc);
  }
  
  INTEGER writetable(const char* filename, const realmat& a,
    const vector<string>& header, INTEGER width, INTEGER precision, char fc)
  {
    ofstream fout;
    fout.open(filename);
    if (!fout) {
      warn("Warning, writetable, write error");
      return 0;
    }
    return writetable(fout, a, header, width, precision, fc);
  }
  
  INTEGER writetable(ostream& fout, const realmat& a, 
    INTEGER width, INTEGER precision, char fc)
  {
    switch (fc) {
      case 'f': ;
      case 'e': ;
      case 'g': ;
      case 'E': ;
      case 'G': break;
      default : 
        warn("Warning, writetable, fc must be f, e, g, E, or G, set to f");
	fc = 'f';
	break;
    }

    INTEGER r = a.nrow();
    INTEGER c = a.ncol();
  
    for (INTEGER i=1; i<=r; ++i) {
      for (INTEGER j=1; j<=c; ++j) {
        fout << ' ' << fmt(fc,width,precision,a(i,j));
      }
      fout << '\n';
    }
  
    if (!fout.good()) {
      warn("Warning, writetable, write error");
      return 0;
    }
  
    return r*c;
  }
  
  INTEGER writetable(ostream& fout, const realmat& a, 
    const vector<string>& header, INTEGER width, INTEGER precision, char fc)
  {
    for(vector<string>::const_iterator p=header.begin(); p<header.end(); ++p) {
      fout << ' ' << fmt('r',width,*p);
    }
    fout << '\n';
    return writetable(fout, a, width, precision, fc);
  }

}
