function outProf  = read_aoml2(theFileName,varargin)
% read_aoml: Read an ascii file in the so-called AOML format.
%
% AOML/PhOD CTDCalibration toolbox.
% USAGE:  dataStruct = read_aoml(thePathAndFilename, ...
%								'dataNames',theDataNameCell,...
%								'dataUnits',theDataUnitsCell,...
%								'missingValue',theMissingValue)
% 
% INPUT:
%          thePathAndFilename: string containing the pathand filename to be read
%          century: optional value representing century in which the observation 
%          was made.  It is 1900 by default.  Pass 2000 if necessary.
%          miisingValue: optional value representing missing data.  If passed, all
%          data values ==  missingValue are replaced by NaNs.  If it isn't passed, 
%          no replacements are made.
% 
% OUTPUT:  
%          dataStruct:  Structure containing the data in the input file.  See examples below.
% 
% DESCRIPTION: 
% The AOML ascii format is used for files containing 
% profile data.  Typically, these files are ctd profiles although frequently
% xbts, ladcp profiles etc have been "coerced" into AOML format.  As a result,
% AOML format, means different things to different people.  The formatting rules
% implemented in this program are:
% 1. Each file contains one and only one profile.
% 2. The first line contains ascii descriptors of the fields in line 2.  They are read using
%     c style formatted read statements which means, I don't count columns, but the presence 
%     of the slashes in the date are expected.  White space, no matter the size, is treated as 
%     one space.  As a result, files written in AOML from this Matlab software, may be
%     problematic for older fortran routines.  Contact me if this is a problem.
% 3. The second line contains the fields shown in the example below.
% 4. Lines 3 through 10, are optional text storage lines.  They may contain different text
%     in different files but there are always 7 of them.  All 12 meta-data lines are returned
%     as a cell array called 
%     UserData in the output structure.
% 5. Time is returned in a string of the form 
%     obsTime = 'dd-mmm-yyyy HH:MM:SS'   e.g. 01-Mar-2000 15:45:17
%     which is dateform #0 in the Matlab datestr  documentation.  
%     See below for help in dealing with this form.
%   
%     e.g. dtNum = datenum(obsTime,0) or [yr,mnth,day,hh,mm,ss] = datevec(dtNum);
% 
% 6. obsTimeZone contains the time zone information in column 8 of line 1.
% 7. Line 12 is assumed to contain the data column names.  Occasionally, line 11 contains
%     column names, and line 12 contains the units.  The program makes a valiant effort to sort
%     this out, but if it fails, it reverts to the column headings below without units.
% 8. The program assumes that there are 7 columns in every file.  This isn't always the case
%     either.  It makes another attempt to figure it out on it's own.  If it fails, it makes a 
%     heartfelt plea for you to standardize your standards once and for all.  
%     It then ridicules you mercilessly....
% 9. lat and lon are assumed to be in decimal degrees with lon in the range (-180, 180]
% 10. jday is, in my opinion, misleading.  jday here, is more appropriately called yearday.  It is 
%       redundant and is neglected.  If I'm wrong, let me know....
% 
% 01 %Cast   lat    long    jday   mo/da/yr   gmt
% 02 % 33   6.695  46.324   269    09/26/90  1944
% 03 % 
% 04 % Lab  C,T calibration file:  D8:[PHOD.BITTERMAN.CTD.CALS]CTD1_NOV_90.ALT_CAL
% 05 % Lab Pres calibration file:  D8:[PHOD.BITTERMAN.CTD.CALS]CTD1_NOV_90.PRESS
% 06 % 
% 07 % Bottle calibration values:  deltas  bias  slope  tcor    wt     pcor
% 08 % 0.020  0.03  2.57  -0.031  0.70  0.000141
% 09 % 
% 10 % Poly depth and parameters:  1000. -0.0190   0.511E-04  -0.442E-07   0.116E-10
% 11 %
% 12 % pre    tem     con     sal      oc     ot        ox
%
%
% AUTHOR: Derrick Snowden
%         NOAA/AOML/PhOD
%         Fri Apr 30 13:11:50 EDT 2004
% 
% SEE ALSO: write_aoml.m
% 
% DEPENDENCIES:
%
% EXAMPLES:
% names = cellstr(strvcat('pr','te','co','sa','oc','ot','ox'))
% aoml = read_aoml('/phodnet/data/cruisedat/accp1-1991-01/ctd/calibrated/cast_030.cal')
% aoml2 = read_aoml('/phodnet/data/cruisedat/aug85/ctd/ctd0885.030')
% 
% REFERENCES:

% NOTES:
%     IF YOU FIND ANY BUGS, please do not fix them on your own without at least telling me.
%     I'm trying to create a general PhOD toolbox that is available to everyone in a central 
%     location (/phodnet/share/matlab or on the intranet) Therefore I want these programs to be
%     as general and well tested as possible. If you fix a bug please contact me.
% 

%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
century = 1900;
thePVPairs = [];

% Parse arguments.
error(nargchk(1,7,nargin));
if nargin > 1
	thePVPairs = parse_pv_pairs(varargin);
end
if isfield(thePVPairs , 'missingdata')
	missingData = thePVPairs.missingdata;
else
	missingData = [];
end
if isfield(thePVPairs , 'datanames')
	dataColNames = thePVPairs.datanames;
else
	dataColNames = cellstr(strvcat('c1', 'c2', 'c3', 'c4', 'c5', 'c6', 'c7'));
end 	
if isfield(thePVPairs , 'dataunits')
	dataColUnits = thePVPairs.dataunits;
else
	dataColUnits = '';
end

% Open the file and test the result.
fid = fopen(theFileName,'r');
if (fid < 0) % make sure file opened correctly
	warning(['Unable to open the file ',theFileName])
	outProf = [];
	return
end

% Count the depth levels (total lines in file minus the 
% mandatory 12 lines of metadata.
numLinesMetaData = 12;
numDpthLevels = count_lines(fid) - numLinesMetaData;

% Save all meta-data lines in UserData
UserData = parse_metadata(fid,numLinesMetaData);

% Initialize output structure with 
profile = struct('UserData',[]);
profile.UserData = UserData;

% line 1 column names assumed to be...
%Cast   lat    long    jday   mo/da/yr   gmt
line = UserData{1};
i = 1;
while line
	[colNames{i},line] = strtok(line,' /');
	i = i + 1;
end
timeZone = colNames{8};

% line 2 column data containing station metadata.
% Here we try to figure out the format used to write the header line.
nExpecHeaderItems = 8;
line = del_leading_spaces(del_following_spaces(UserData{2}));
% replace slashes in the dates with spaces.
line = strrep(line,'/',' ');
% Count the number of space separated elements.
nActualHeaderItems = count_space_separated_cols(line);
switch nActualHeaderItems
case 8
	% e.g. 
	% 33   6.695  46.324   269    09 26 90  1944
	[line,count] = sscanf(line,'%f %f %f %f %d %d %d %f\n',nActualHeaderItems);
otherwise
	error(['Unrecognized number of columns in line 2. ',theFileName]);
end
if count ~= nExpecHeaderItems
	error(['Incorrect number of elements in line 2.: ',theFileName])
end

profile.obsDateTime = make_date_string(line(5),line(6),line(7),line(8)  ,century);
profile.obsTimeZone = timeZone;
profile.castNum = line(1);
profile.lat = line(2);
profile.lon = line(3);
profile.numDpthLevels = numDpthLevels;

% Try to count the data columns
defNum = 7; % Default number of columns to expect.
errmsg = position_cursor_in_file(fid,numLinesMetaData);
line = fgetl(fid);
nCols = length(dataColNames);
nColsFirstData = count_space_separated_cols(line);
if nCols ~= nColsFirstData
	error('The number of names in the dataNames cell array differs from the number of columns in the first data line.')
end

% Why did I do this again???
nCols = length(dataColNames);
for i = 1:length(dataColNames)
	dataColNames{i} = prettyprint(dataColNames{i});
        eval(['profile.' dataColNames{i} ' = [];'])
end
profile.dataUnits = dataColUnits;

%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
% Read in each depth level
errmsg = position_cursor_in_file(fid,numLinesMetaData);

formStr = build_format_string(nCols);
[data,count] = fscanf(fid,formStr,[nCols numDpthLevels]);
if ~isempty(missingData)
	idx = find(data==missingData);
	if ~isempty(idx)
		data(idx) = NaN;
	else
		% missingData was improperly or not passed.  Do nothing
	end
end

% Store data.
% TODO: Could add some trouble shooting here to make sure data is the size 
% we hope it is.
for i = 1:nCols
  eval(['profile.' dataColNames{i} ' = data(i,:);']);
end

if nargout==0
	assignin('caller','ans',profile)
elseif nargout==1
	outProf = profile;
else 
	error('Incorrect number of outputs requested.')
end

fclose (fid);
return

%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
function nLines = count_lines(fid)
% Count the number of lines in this file.
% Assuming it is already opened. 
frewind(fid);
cnt = 0;
while ~feof(fid)
	line = fgetl(fid);
	cnt = cnt + 1;
end
nLines = cnt;
frewind(fid);
return

%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
function obsDateTime = make_date_string(mnth,day,yy,hhmm,century)
% Parse the observation date and time into a string.
hh = floor(hhmm./100); 
mm = hhmm - hh.*100;
obsDateTime = datestr([century+yy mnth day hh mm 0],0);
return


%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
function userData = parse_metadata(fid,nLines);
% Create a cell array and store meta-data for safekeeping.
userData = cell(nLines,1);
frewind(fid);
for i = 1:nLines
	userData{i} = fgetl(fid);
end
return

%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
function errmsg = position_cursor_in_file(fid,n)
% Position the cursor at the n plus first  line of an ascii file.
frewind(fid)
for i = 1:n
	junk = fgetl(fid);
end
errmsg = '';
return

%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
function nCols = count_space_separated_cols(line);
% Count the number of space separated columns in a string.
%
i = 1;
while line
	[junk,line] = strtok(line,' ');
	i = i + 1;
end
nCols = i-1;
return

%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
function [outStr]  = prettyprint(inStr)
% prettyprint: Clean up strings. Remove space, period, parens etc.
%

inStr = del_leading_spaces(del_following_spaces(inStr));
inStr = strrep(inStr,' ','_');
inStr = strrep(inStr,'.','_');
inStr = strrep(inStr,'(','');
inStr = strrep(inStr,')','');
inStr = strrep(inStr,'[','');
inStr = strrep(inStr,']','');
outStr = inStr;

return
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
function nameMap  = parse_pv_pairs(in)
% parse_pv_pairs: Parse the pv pairs
%
i = 1;
while ~isempty( in)
	p{i} = lower(in{1});
	v{i} = lower(in{2});
	in(1:2) = [];
	i = i+1;
end
nameMap = cell2struct(v,p,2);

return
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
function formStr = build_format_string(nCols)
% build_format_string: 
%
formStr = '';
% Adjust the format string based on the number of columns in the file
switch nCols
case 1
	formStr = '%f\n';
	
case 2
	formStr = '%f\n';
	
case 3
	formStr = '%f\n';
	
case 4
	formStr = '%f\n';
	
case 5
	formStr = '%f\n';
	
case 6
	formStr = '%f\n';
	
case 7
	formStr = '%f\n';
otherwise
	error('Invalid number of columns in the aoml file.')
end



return



