function [numericData, textData, numHeaderRows] = stringparse(string, delimiter, headerLines)
%STRINGPARSE Helper function for importdata.
%
% See also: LOAD, FILEFORMATS

% Copyright 1984-2000 The MathWorks, Inc.
% $Revision: 1.5 $  $Date: 2000/07/27 21:04:22 $ 

error(nargchk(1,3,nargin));

numericData = [];
textData = {};
numHeaderRows = 0;

% gracefully handle empty
if isempty(string) | all(isspace(string))
    return;
end

% validate delimiter 
if nargin == 1
    delimiter = guessdelim(string);
else
    % handle \t
    delimiter = sprintf(delimiter);
    if length(delimiter) > 1
        error('Multi character delimiters not supported.')
    end
end

if nargin < 3
    headerLines = NaN;
end

% use what user asked for header lines if specified
[numDataCols, numHeaderRows, numHeaderCols, numHeaderChars] = ...
    analyze(string, delimiter, headerLines);

% fetch header lines and look for a line of column headers
headerLine = {};
headerData = {};
origHeaderData = headerData;
useAsCells = 1;

firstLineOffset = 0;
if numHeaderRows
    firstLineOffset = numHeaderRows - 1;

    headerData = strread(string,'%[^\n]',firstLineOffset);
    origHeaderData = headerData;

    if numDataCols
        headerData = [origHeaderData, cell(length(origHeaderData), numHeaderCols + numDataCols - 1)];
    else
        headerData = [origHeaderData, cell(length(origHeaderData), numHeaderCols)];
    end
 
    headerLine = strread(string,'%[^\n]',1,'headerlines',firstLineOffset);
    origHeaderLine = headerLine;
    
    useAsCells = 0;
    
    if ~isempty(delimiter) & ~isempty(findstr(string, delimiter))
        cellLine = split(headerLine{:}, delimiter);
        if length(cellLine) == numHeaderCols + numDataCols
            headerLine = cellLine;
            useAsCells = 1;
        end
    end
    
    if ~useAsCells
        if numDataCols
            headerLine = [origHeaderLine, cell(1, numHeaderCols + numDataCols - 1)];
        else
            headerLine = [origHeaderLine, cell(1, numHeaderCols)];
        end
    end
end

if isempty(delimiter)
    formatString = [repmat('%s', 1, numHeaderCols) repmat('%n', 1, numDataCols)];
else
    formatString = [repmat(['%[^' delimiter ']'], 1, numHeaderCols) repmat('%n', 1, numDataCols)];
end

textCellData = {};
numericCellData = {};

% call strread with format string
% (if we got here, there must be at least one good chunk on the 1st line in the file)
if numHeaderCols & numDataCols
    [textCellData{1:numHeaderCols}, numericCellData{1:numDataCols}] = ...
        strread(string,formatString,1,'delimiter',delimiter,'headerlines',numHeaderRows);
elseif numDataCols
    [numericCellData{1:numDataCols}] = ...
        strread(string,formatString,1,'delimiter',delimiter,'headerlines',numHeaderRows);
end

% now try again for the whole shootin' match
try
    if numHeaderCols & numDataCols
        [textCellData{1:numHeaderCols}, numericCellData{1:numDataCols}] = ...
            strread(string,formatString,'delimiter',delimiter,'headerlines',numHeaderRows);
    elseif numDataCols
        [numericCellData{1:numDataCols}] = ...
            strread(string,formatString,'delimiter',delimiter,'headerlines',numHeaderRows);
    end
    wasError = 0;
catch
    wasError = 1;
end

% setup some default answers if we're not able to do the full read below
if numHeaderCols
    numRows = length(textCellData{1});
else
    numRows = 0;
end

if numDataCols & ~numRows & ~isempty(numericCellData{1})
    numRows = length(numericCellData{1});
end

if nargout > 1
    for i = 1:numHeaderCols
        textData(:,i) = textCellData{i};
    end
end

for i = 1:numDataCols
    numericData(:,i) = numericCellData{i};
    if nargout > 1, textData(:,i+numHeaderCols) = cell(numRows, 1); end
end

if nargout > 1
    if ~isempty(headerLine)
        textData = [headerLine; textData];
    end

    if ~isempty(headerData)
        textData = [headerData; textData];
    end    
end

% if the first pass failed to read the whole shootin' match, try again using the character offset
if wasError & numHeaderChars
    % rebuild format string
    formatString = ['%' num2str(numHeaderChars) 'c' repmat('%n', 1, numDataCols)];
    textCharData = '';
    try
        [textCharData, numericCellData{1:numDataCols}] = ...
            strread(string,formatString,'delimiter',delimiter,'headerlines',numHeaderRows,'returnonerror',1);
        numHeaderCols = 1;
        if ~isempty(numericCellData)
            numRows = length(numericCellData{1});
        else
            numRows = length(textCharData);
        end
    end        
    
    numericData = [];

    if numDataCols
        headerData = [origHeaderData, cell(length(origHeaderData), numHeaderCols + numDataCols - 1)];
    else
        headerData = [origHeaderData, cell(length(origHeaderData), numHeaderCols)];
    end
 
    if ~useAsCells
        if numDataCols
            headerLine = [origHeaderLine, cell(1, numHeaderCols + numDataCols - 1)];
        else
            headerLine = [origHeaderLine, cell(1, numHeaderCols)];
        end
    end

    for i = 1:numDataCols
        numericData(:,i) = numericCellData{i};
    end

    if nargout > 1 & ~isempty(textCharData)
        textCellData = cellstr(textCharData);
        if ~isempty(headerLine)
            textData = [headerLine;
                        textCellData(1:numRows), cell(numRows, numHeaderCols + numDataCols - 1)];
        else
            textData = [textCellData(1:numRows), cell(numRows, numHeaderCols + numDataCols - 1)];
        end
        
        if ~isempty(headerData)
            textData = [headerData; textData];
        end
    end
end

if nargout > 1 & ~isempty(textData)
    % trim trailing empty rows from textData
    i = 1;
    while i <= size(textData,1)
        if all(cellfun('isempty',textData(i,:)))
            break;
        end
        i = i + 1;
    end
    if i <= size(textData,1)
        textData = textData(1:i-1,:);
    end

    % trim trailing empty cols from textData
    i = 1;
    while i <= size(textData,2)
        if all(cellfun('isempty',textData(:,i)))
            break;
        end
        i = i + 1;
    end
    if i <= size(textData,2)
        textData = textData(:,1:i-1);
    end
end
        


function [numColumns, numHeaderRows, numHeaderCols, numHeaderChars] = analyze(string, delimiter, header)
%ANALYZE count columns, header rows and header columns

numColumns = 0;
numHeaderRows = 0;
numHeaderCols = 0;
numHeaderChars = 0;

if ~isnan(header)
    numHeaderRows = header;
end

thisLine = strread(string,'%[^\n]',1,'headerlines',numHeaderRows);
if isempty(thisLine)
    return;
end
    
thisLine = thisLine{:};

[isvalid, numHeaderCols, numHeaderChars] = isvaliddata(thisLine, delimiter);

while ~isvalid
    % stop now if the user specified a number of header lines
    if ~isnan(header) & numHeaderRows == header
        break;
    end
    
    numHeaderRows = numHeaderRows + 1;

    newLine = strread(string,'%[^\n]',1,'headerlines',numHeaderRows);
    if isempty(newLine)
        break;
    end

    thisLine = newLine{:};
    
    [isvalid, numHeaderCols, numHeaderChars] = isvaliddata(thisLine, delimiter);
end

if isvalid
    % determine num columns
    delimiterIndexes = findstr(thisLine, delimiter);
    if all(delimiter ==' ') & length(delimiterIndexes) > 1
        delimiterIndexes = delimiterIndexes([logical(1) diff(delimiterIndexes) ~= 1]);
    end

    % format string should have 1 more specifier than there are delimiters
    numColumns = length(delimiterIndexes) + 1;
    if numHeaderCols > 0
        % add one to numColumns because the two set of columns share a delimiter
        numColumns = numColumns - numHeaderCols;
    end
    
    newLine = strread(string,'%[^\n]',1,'headerlines',numHeaderRows);
    thisLine = newLine{:};
end


function [status, numHeaderCols, numHeaderChars] = isvaliddata(string, delimiter)
% ISVALIDDATA delimiters and all numbers or e or + or . or -
% what about single columns???

numHeaderCols  = 0;
numHeaderChars = 0;

if isempty(delimiter)
    % with no delimiter, the line must be all numbers, +, . or -
    status = isdata(string);
elseif isempty(findstr(string, delimiter))
    % a delimiter must occur on each line of data (or there is no delimiter...)
    status = 0;
else
    % if there is data at the end of the line, it's legit
    cellstring = split(string, delimiter);
    flags = isdata(cellstring);
    status = 0;
    if flags(end)
        % num leading zeros in flags is num header cols
        numHeaderCols = max(find(flags == 0));
        if isempty(numHeaderCols)
            numHeaderCols = 0;
        end
        
        % use contents of 1st data data cell to find num leading chars
        numHeaderChars = findstr(string, cellstring{numHeaderCols + 1}) - 1;

        status = 1;
    end
end


function status = isdata(cellstring)
%ISDATA true if string can be shoved into a number

if ~iscell(cellstring)
    cellstring = cellstr(cellstring);
end

status = [];
for i = 1:length(cellstring)
    if length(cellstring) == i & isempty(cellstring{i})
        % ignore empty last field
        break;
    end
    [a,b,c] = sscanf(cellstring{i}, '%g');
    status(i) = isempty(c);
end



function cellOut = split(string, delimiter)
%SPLIT rip string apart using strtok
i = 1;
[cellOut{i}, string] = strtok(string, delimiter);
while ~isempty(string)
    i = i + 1;
    [cellOut{i}, string] = strtok(string, delimiter);
end
