/* Copyright (C) 2003  Kai Habel
**
** 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., 59 Temple Place, Suite 330, Boston, MA  02111-1307 
*/

/*
29. January 2003 - Kai Habel: first release

TODO:
* handle line terminator \r and \n\r (?) 
*/

#include "config.h"
#include <fstream>
#include <algorithm>
#include <queue>
#include <climits>
#ifdef HAVE_CONFIG_H
#include <config.h>
#endif
#include <octave/oct.h>
#include <octave/lo-ieee.h>

using namespace std;

bool sep_is_next(istringstream *linestrm, string sep) {

  bool ret = true;
  char c;
  
  *linestrm >> c;
  if (c != sep[0]) {
    ret = false;
    linestrm->putback(c);
  }
  return ret;
}

queue<Complex> read_textline(istringstream *linestrm, string sep) {

  queue<Complex> line;
  Complex cv = 0.0;
  unsigned long nchr = 0;

  while (!linestrm->eof() ) {
  
    nchr++;    
    if (sep_is_next(linestrm,sep)) {
    
      if (nchr == 1) line.push(0);
        // first line character is separator

      if (sep_is_next(linestrm,sep)) line.push(0);
        // double occurance of separator  

      if (linestrm->eof()) line.push(0);
        // last line character is sparator
      
    } else {
      linestrm->clear();
      cv = octave_read_double(*linestrm);
      if (linestrm->fail()) {
        // invalid charcter(s), try to find next separator
        linestrm->clear();
        while ( !(sep_is_next(linestrm, sep) || linestrm->eof()) )
	  linestrm->get(); 
	line.push(0);
      } else {
        line.push(cv);
      }    
    }
  }
  return line;
}


DEFUN_DLD (dlmread, args, ,
        "-*- texinfo -*-\n\
@deftypefn {Loadable Function} {@var{data} =} dlmread (@var{file})\n\
@deftypefnx {Loadable Function} {@var{data} =} dlmread (@var{file},@var{sep})\n\
@deftypefnx {Loadable Function} {@var{data} =} dlmread (@var{file},@var{sep},@var{R0},@var{C0})\n\
@deftypefnx {Loadable Function} {@var{data} =} dlmread (@var{file},@var{sep},@var{range})\n\
Read the matrix @var{data} from a text file\n\
The @var{range} parameter must be a 4 element vector containing  the upper left and lower right corner\n\
[@var{R0},@var{C0},@var{R1},@var{C1}]\n\
The lowest index value is zero.\n\
@end deftypefn")

{
  octave_value_list retval;
  queue<ComplexColumnVector> lines;  
  int nargin = args.length();
  if (nargin < 1 || nargin > 4) {
    print_usage ("dlmread");
    return retval;
  }

  if ( !args (0).is_string() ) {
    error ("dlmread: 1st argument must be a string");
    return retval;
  }
  
    // set default values
  string sep(",");
  unsigned long r0 = 0,c0 = 0,r1 = ULONG_MAX-1,c1 = ULONG_MAX-1;
  
  if (nargin > 1) {
    sep = args(1).string_value();
    if (sep.length() != 1) error("separator must be a single character");
  }
    
  if (nargin == 3) {
    ColumnVector range(args(2).vector_value());
    if (range.length() == 4) {
        // double --> unsigned int     
      r0 = static_cast<unsigned long> (range(0));
      c0 = static_cast<unsigned long> (range(1));
      r1 = static_cast<unsigned long> (range(2));
      c1 = static_cast<unsigned long> (range(3));
      if (lo_ieee_isinf(range(2))) r1 = ULONG_MAX-1;
      if (lo_ieee_isinf(range(3))) c1 = ULONG_MAX-1;
      
    } else {
      error("range must include [R1 C1 R2 C2]");
    }
    
  } else if (nargin == 4) {
    r0 = args(2).ulong_value();
    c0 = args(3).ulong_value();
  }
  
  unsigned long dr = r1 - r0 + 1;
  unsigned long dc = c1 - c0 + 1;
  
  string fname(args(0).string_value());
  ifstream file(fname.c_str());
  if (!file) {
    error("could not open file");
    return retval;
  }

    // find file length 
  file.seekg(0, ios::end);
  ifstream::pos_type flen = file.tellg();
  file.seekg(0, ios::beg);

  OCTAVE_LOCAL_BUFFER(char,line,(long int)flen);
  
  unsigned long nr = 0, nc = 0, curr_len = 0,colIdx;
  queue<Complex> lineq;
  
    //skip first r0 - 1 lines
  for (unsigned long i=0;i<r0;i++) {
    file.getline(line,flen,'\n');
  }
  
    // get first line
  file.getline(line,flen,'\n');
  do {
    istringstream lstrm(line); 
    lineq = read_textline(&lstrm,sep);
    
    curr_len = min(static_cast<unsigned long> (lineq.size()), dc);
    ComplexColumnVector col2(curr_len);
    
    for (colIdx = 0;colIdx < curr_len;colIdx++) {
      col2(colIdx) = lineq.front();
      lineq.pop();
    }
      // save current ColumnVector (current line) in queue
    lines.push(col2);
    
      // save maximum number of columns and number of rows
    nc = max(nc,curr_len);
    
     // early break when user limit R2 is reached, increase row counter
    if (nr++ > dr) break;
    
      // get next line
    file.getline(line,flen,'\n');
  } while(!file.eof());
  
  unsigned long cend, r, c;

  if (c0 <= nc) {
    ComplexMatrix cm(nr,nc-c0);
    ComplexColumnVector cv;
    
      // write all ColumnVecotors into Matrix 
    for (r = 0; r < nr;r++) {
      cv = lines.front();
      lines.pop();
      cend = min(dc, cv.length() - c0);
      for (c = 0; c < cend; c++)
        cm(r,c) = cv(c + c0);
    }
    retval(0) = cm;
  }
  
  return retval;
}


syntax highlighted by Code2HTML, v. 0.9.1