function theStructs = ReadStructsFromText(filename)
% theStructs = ReadStructsFromText(filename)
%
% Open a tab delimited text file.  The first row should
% contain the field names for a structure.  Each following
% row contains the data for one instance of that structure.
%
% This routine reads each row and returns an array of structures,
% one struct for each row, with the data filled in.  Data
% can be numeric or string for each field.
%
% Not a lot of checking is done for cases where the read file
% fails to conform to the necessary format.
%
% See Also: WriteStructsToText

% 6/15/03   dhb			Wrote it.
% 07/01/03  dhb 		Support string as well as numeric data.
% 07/02/03	dhb, jg     Handle white space in column headers.
% 07/03/03  dhb         More little tweaks.
% 08/06/03  dhb         Handle fgetl returns empty string.
% 08/22/07  dhb         This was modified on disk but not commented our uploaded to SVN repository.
% 4/26/12   dhb         Squeeze '/' out of field names too.
% 5/31/12   dhb         Squeeze '*' out of field names too.
% 6/7/13    dhb         Suppress uninteresting warning on str2num.
% 4/3/14    dhb         Try to handle NaN in text files.  Worked for at least one case.

% Open the file
fid = fopen(filename);
if fid == -1
  error('Cannot open file %s', filename);
end

% Read first line to get field names for returned structure
theFields = {};
firstLine = fgetl(fid);
theIndex = 1;
i = 1;
while (1)
  wholeField = '';
  while (1)
    readString = firstLine(theIndex:end);
    [field,count,nil,nextIndex] = sscanf(readString,'%s',1);
    if (count == 0)
      break;
    end
    wholeField = [wholeField field];
    theIndex = theIndex+nextIndex-1;
    if (nextIndex <= length(readString) && abs(readString(nextIndex)) == 9)
      break;
    else
      wholeField = [wholeField ' '];
    end
  end
  if (count == 0)
    if (~isempty(wholeField))
      theFields{i} = wholeField;
      i = i+1;
    end
    break;
  end
  theFields{i} = wholeField;
  wholeField = [];
  i = i+1;
end
nFields = length(theFields);

% Squeeze white space out of each field
for i = 1:nFields
  newField = '';
  oldField = theFields{i};
  for j = 1:length(oldField)
    if (~isspace(oldField(j)) && oldField(j) ~= '.' && oldField(j) ~= '/' && oldField(j) ~= '*')
      newField = [newField oldField(j)];
    end
  end
  theFields{i} = newField;
end

% Octave doesn't support the textscan function, so we use the old method
% of extracting data for Octave users.  The new method allows spaces in
% strings.
if ~IsOctave
  % Read out all the data from the text file delimited by newline
  % characters.
  data = textscan(fid, '%s', 'delimiter', '\n');
  data = data{1};
  
  % Read the values from each line of the text and convert them to
  % doubles if possible.
  f = 1;
  for i = 1:size(data, 1)
    values = textscan(data{i}, '%s', 'delimiter', '\t');
    values = values{1};

    for j = 1:size(values, 1)
      convertedValue = [];
      
      % Convert the entry from a string to a number if possible.  We
      % first check to see if the value is on the path as a function
      % because the str2num function calls eval on its input which
      % will cause it to execute.
            if (strcmp(lower(values{j}),'nan'))
                convertedValue = NaN;
            elseif isempty(which(values{j})) && isempty(which(strtok(values{j})))
                oldWarn = warning('off','MATLAB:namelengthmaxexceeded');
        convertedValue = str2num(values{j}); %#ok<ST2NM>
                warning(oldWarn.state,'MATLAB:namelengthmaxexceeded');
      end

      % If the value successfully converted, overwrite what was
      % already in the cell array.
      if ~isempty(convertedValue)
        values{j} = convertedValue;
      end
    end
    
    theStructs(f) = cell2struct(values, theFields, 1); %#ok<AGROW>
    f = f + 1;
  end
else
  % Now read lines and pull out structure elements
  f = 1;
  while (1)
    theLine = fgetl(fid);
    if (isempty(theLine) || theLine == -1)
      break;
    end
    theIndex = 1;
    theData = cell(nFields,1);
    for i = 1:nFields
      readString = theLine(theIndex:end);
      [field,count,nil,nextIndex] = sscanf(readString,'%g',1);
      if (count == 0)
        [field,count,nil,nextIndex] = sscanf(readString,'%s',1);
        if (count == 0)
          error('Cannot parse input');
        end
      end
      theIndex = theIndex+nextIndex-1;
      theData{i} = field;
    end
    theStruct = cell2struct(theData,theFields,1);
    theStructs(f) = theStruct;
    f = f+1;
  end
end

% If there was no data in the file, return an empty matrix.
if ~exist('theStructs', 'var')
  theStructs = [];
end

% Close the file.
fclose(fid);