function varargout = write_aoml(varargin)
% WRITE_AOML - write hydrographic profile data into aoml ascii format
%
% AOML/PhOD CTDCalibration toolbox.
%
% USAGE:  [status,errmessage] = write_aoml(ctd_structure,fname,...
%					'HeaderLines',headerLines,...
%					'DataNames',theDataNames,...
%					'DataUnits',theDataUnits)
% 
% INPUT:
%          dataStruct:  Structure containing the data in the input file.
%			See examples below.
%          thePathAndFilename: string containing the path and filename
%			to be written
% 
% OUTPUT:  
% status: integer indicating success or failure
%		if status is empty, file was written successfully
%      otherwise an error occurred whcih was recorded in errmessage
% If the output file exists, it is renamed with a .bak extension.  
% 
% DESCRIPTION: 
% The AOML ascii format is used to store hydrographic (xbt, ctd etc.) profile
% data.  Typically, these files are ctd profiles although frequently xbts,
% ladcp profiles etc have been "coerced" into AOML format.  See the examples
% below and the data in the samples subdirectory for examples of aoml ascii
% files.  The data structure required as input to this function is defined in
% read_aoml.  It is the same structure returned from that read program.  In
% addition, if a cell array called 'headerLines' is present as a field in the
% ctd_structure, the first 8 elements will  be taken and written into the aoml
% file in lines 3-10.  See the examples and the test_aoml routine.  
%
% EXAMPLES: type write_aoml('demo') for a demo... or see test_aoml.m
%
% 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')
% 
% SEE ALSO: read_aoml, test_aoml
% 
% DEPENDENCIES: 
%  Must use Matlab V6.5.  Not backward compatible with 6.1 or earlier.

% AUTHOR: Derrick Snowden
%         NOAA/AOML/PhOD
%         Mon May 17 14:01:15 EDT 2004
% 
% 
% VERSION: 
%	$Revision: 1.9 $
%	$Date: 2008/02/18 19:48:03 $
%	$Id: write_aoml3.m,v 1.9 2008/02/18 19:48:03 duncombe Exp duncombe $
%
% CHANGELOG: 
%
%

% 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.  
% 

%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
error(nargchk(1,8,nargin));
thePVPairs = [];

% Do a demo?
if nargin==1  strncmp(lower(varargin{1}),'demo',4)
	do_demo(mfilename)
end

% Parse arguments.

if nargin >1
	% TODO: Add better error checking here.
	inStruct = varargin{1};
	fName = varargin{2};
	varargin(1:2) = [];
end	
if nargin > 2
	thePVPairs = parse_pv_pairs(varargin);
end
if isfield(thePVPairs , 'headerlines')
	headerLines = thePVPairs.headerlines;
else
	headerLines = [];
end
if isfield(thePVPairs , 'datanames')
	dataNames = thePVPairs.datanames;
else
	dataNames = cellstr(strvcat('c1', 'c2', 'c3', 'c4', 'c5', 'c6', 'c7'));
end 	
if isfield(thePVPairs , 'dataunits')
	dataUnits = thePVPairs.dataunits;
else
	dataUnits = '';
end

% Collect all the fieldnames 
theFieldNames = fieldnames(inStruct);

% Open the output file
[fid,err,warn] = gfopen(fName);
warning('AOML:OutputFileExists',warn);
error(err)

% Parse the date
[obsDateTime,theFieldNames] = parse_field('obsDateTime',theFieldNames,inStruct);
try
	[yr,mnth,day,hr,min,sec] = datevec(obsDateTime);
catch
	error(['Unable to parse the obsDateTime field of the input ', ...
		'structure. Not a datestr?']);
end

% Parse the rest of the metadata
[latitude,theFieldNames] = parse_field('lat',theFieldNames,inStruct);
[longitude,theFieldNames]  = parse_field('lon',theFieldNames,inStruct);
[castNum,theFieldNames]  = parse_field('castNum',theFieldNames,inStruct);
[obsTimeZone,theFieldNames]  = ...
	parse_field('obsTimeZone',theFieldNames,inStruct);
[numDpthLevels,theFieldNames]  = ...
	parse_field('numDpthLevels',theFieldNames,inStruct);
[UserData,theFieldNames]  = parse_field('UserData',theFieldNames,inStruct);
%[dataUnits,theFieldNames]  = parse_field('dataUnits',theFieldNames,inStruct)

% Do the dataNames correspond to actual fields in the structure?
theFieldNames = get_aoml_data_names(inStruct);

% write the output
% Line 1
count = fprintf(fid,' Cast   lat    long    jday   mo/da/yr   %s\n', ...
		obsTimeZone);
% Line 2
[yr,mnth,day,hh,mm,ss] = datevec(obsDateTime,0);
yrday = ceil(yearday(yr,yr,mnth,day,hh,mm,ss));
yrString = num2str(yr,'%6d');

% example fortran read statement from
% Molly's read_sta5 routine
%200 format(/,i4,2f8.3,i6,i6,1x,i2,1x,i2,i6,//////////)

count = fprintf(fid,'%4d%8.3f%8.3f%6d    %0.2d/%0.2d/%2s  %0.2d%0.2d\n',...
			castNum, latitude, longitude, yrday, mnth,day,yrString(3:4),hh,mm);


% Line 3 through 10
% TODO: Add the headerLines cell array instead of userdata.  
% 	Constrain to be 9 lines.
count = fprintf(fid,'%s\n',UserData{3});
count = fprintf(fid,'%s\n',UserData{4});
count = fprintf(fid,'%s\n',UserData{5});
count = fprintf(fid,'%s\n',UserData{6});
count = fprintf(fid,'%s\n',UserData{7});
count = fprintf(fid,'%s\n',UserData{8});
count = fprintf(fid,'%s\n',UserData{9});
count = fprintf(fid,'%s\n',UserData{10});

% Line 11 contains column names
nCols = length(theFieldNames);
formFieldNames = build_format_string_datahdr(nCols);
formFieldData = build_format_string_data(nCols);
count = fprintf(fid,formFieldNames,theFieldNames{:});

% Line 12 contains units if they exist
if isempty(dataUnits)
      count = fprintf(fid,' \n');
else
      count = fprintf(fid,formFieldNames,dataUnits{:});
end

% Line 13 on is data
[data,errmss] = make_data_matrix(dataNames,inStruct);
if isempty(data)
	error(errmss);
end	
count = fprintf(fid,formFieldData,data');
fclose(fid);

% Return to the workspace
if nargout > 0
        varargout{1}= [];
else
        assignin('caller', 'ans', [])
end

return
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
function [data,errmss] = make_data_matrix(theFieldNames,inStruct);
% make_data_matrix: Build matrix suitable for output.
%
% Step through the data names
data = [];
errmss = '';
for idx = 1:length(theFieldNames)
	eval(['this = inStruct.' theFieldNames{idx} ';']);
	[rows,cols] = size(this);
	if min(rows,cols) ~= 1
		data = [];
		errmss = 'Data must exist in columns or rows, not matrices';
		return
	end
	if idx ==1
		data = NaN.*ones(max(rows,cols),length(theFieldNames));
		sizes = max(rows,cols);
	end
	if isempty(intersect(sizes,max(rows,cols)))
		data = [];
		errmss = 'Data columns have identical sizes.';
		return
	else
		data(:,idx) = this(:);
	end
	if idx > 1
		sizes = [sizes max(rows,cols)];
	end
	
end
return



%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
function [fid,errmessage, warningmessage]  = gfopen(fName)
% gfopen: Graceful fopen which protects us from ourselves
%
errmessage = '';
warningmessage = '';
[isItThere,mess,mssid] = fileattrib(fName);
if isItThere  % file exists, rename
	warningmessage = [fName , ' exists.  Renaming it ',fName,'.bak'];
	[success, mess,messid]=copyfile(fName,[fName,'.bak'],'f');
	if ~success % Couldn't rename it for some reason
		errmessage = [mess,': ',messid];
	end
end

fid  = fopen(fName,'w');

return

%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
function [thisValue,theFieldNames]  = parse_field(thisFieldName, ...
		theFieldNames,aoml);
% parse_field: make sure fields are available.
%
idxStr = strmatch(thisFieldName,theFieldNames);
if isempty(idxStr)
	error(['Unable to find the ',thisFieldName, ...
		' field of the input structure.'])
end
% Remove the name from the list of fields but not from the structure itself.
theFieldNames(idxStr) = [];
eval(['thisValue = aoml.' thisFieldName ';']);
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 do_demo(name)
% Perform a demonstration.
%
eval(['help ',name])

disp(['Demo for ',name])
disp(' ')
disp('Read in data ...')
cmd = ['names = cellstr(strvcat(''press'',''temp'',''co'',''sa'',' ...
	' ''oc'',''ox'',''ot''))']
eval(cmd)
cmd = ['aoml = read_aoml(''sample/aoml_ctd_7column.asc'',''dataNames'',names)']
eval(cmd)
disp('Rename press to pr and temp to te ...')
disp(' ')
cmd=['aoml2 = rename_aoml_columns(aoml,''press'',''pr'',''temp'',''te'')']
eval(cmd)
disp(' ')
cmd=['aoml2 = rename_aoml_columns(aoml,''press'',''pr'',''temp'',''te'')']
eval(cmd)
disp('Have fun...')
return



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

return



