function [ data, i4, r4, nam, xgrid, ygrid, history ] = nc_read( filename, ...
  record, levelreq );

%function [ data, i4, r4, nam, xgrid, ygrid, history ] = nc_read( filename, ...
%  record, levelreq );
%
% Reads one map of netCDF-formatted data.
%
% You need to include the following library to use this script.
% path( path, '/home/disk/tao/willa/matlab-epic' );
% 
%
% data		a row vector of data (of length nlat*nlon). -1 for end-of-file.
% i4, r4	metadata vectors (see Alexis' format specifications).
% 		The only non-zero elements in these vectors are the
%               following:
% iyear		year [i4(1)]
% ipd           month or season [i4(2)]
% iday          day [i4(3)]
% ihour		hour [i4(4)]
% nlon, nlat    number of longitudes and latitudes, respectively.  [i4(6:7)]
% miss   	The missing value flag. [i4(14)]  ***** For now this is
%               always NaN. *****
% base,fctr	add_offset and scale_factor for packing the data,
%               respectively. [r4(1:2)]
%               The use of these variables to pack and unpack the data in
%               the "CDC netCDF Standard Format" specifications and Alexis' 
%               pkd format are consistent.
% xgrid, ygrid  vectors of the longitude and latitude gridpoints,
%               respectively.
% history       an extended header (real words describing the data set
%               or calculation), if it exists
%
% filename      filename of the file to be read.
% record        number of the record to be read (default value 1)
% levelreq      (optional) vertical level to read data for, e.g., 1000
%               for 1000mb.  Type "ncdump -v level filename.nc" to see
%               how the levels are named.  The input is a number so no quotes.
%
% At present the script has no way to read a file in which multiple
% variables are given different variable names, for example, 
% geopotential height and temperature.
%
% July 1995: original script
%
% July 1996: I put in code to read in data which are intrinsically
% two-dimensional, for example, elevations.  If there are only two dimensions,
% the algorithm will fill output time information as zeros.  This is an
% ad hoc solution.  Obviously, you would also want this routine to read
% in time-latitude and time-longitude sections correctly.  
%
% November 1996: The time variable can be written one of two ways:
% 1) "so many units of time since some reference time," (COARDS standard),
% 2) "yyyymmddhhmmss", which is the older convention.
%
% 26 November 1996: Can now pick off the data for a selected level/height
% in a data set with multiple levels/heights.
%
% August 2000: Sets "scale_factor" and "add_offset" to 1 and 0, respectively
% if none are supplied.
%
% Todd Mitchell
%

if nargin==1 ; record = 1; end

i4 = zeros( 1, 30 );
r4 = zeros( 1, 20 );

%OPEN FILE
cdfid = netcdf.open( filename,'NOWRITE');
if cdfid<0; error('Cannot open file'); end

% GET AND CHECK NUMBER OF DIMENSIONS

[ ndim, nvar ] = netcdf.inq( cdfid );

while 0 
% Turn off the following check.  I have begun to write the time 
% variable twice: as hours since some reference time and 
% as yyyymmddhhmm, so the number of variables no longer bears the 
% simple relationship below.
if ndim~=nvar-1 & record==1
disp( sprintf( '\n ndim, nvar: %d %d', ndim, nvar ) );
disp( sprintf( 'Caution: ndim~=nvar-1 as expected.' ) );
end
end

if ndim<3 ; 
disp( sprintf( ...
  'Trouble: the number of dimensions, %d, is smaller than expected.', ndim ) );
error 
end

% Read in the attributes.
lennam  = zeros(nvar,1 );
natt    = zeros(nvar,1 );
ndim    = zeros(nvar,1 );
varnam  = zeros(nvar,20);
dimnam  = zeros(nvar,20);


% *** 3 March 2015: This is as far as I got.  One has to pay a little more
% attention to the details to get it right ! ***
for ivar = 1: nvar
[ nam, dtype, ndim(ivar), dimid, natt(ivar), status ] = ...
  netcdf.inqVar( cdfid, ivar-1 );
if ndim(ivar)>1; 
% This is the variable with the data of interest.
dimid_var=dimid; 
varid    =ivar; 
end
varnam(ivar,1:length(nam)) = nam;
end

% Determine the order in which the variables are written.
leng    = zeros(nvar,1);
for icnt = 1: length(dimid_var)
 [ namdd, lend, status ] = netcdf.inqDim( cdfid, dimid_var(icnt) );
for icnt2 = 1: nvar
if varnam(icnt2,1:length(namdd))==namdd; leng(icnt2) = lend; end
end
end  

% Read in all of the variables, but the 'data.'
% Test to see if the variable is the data.  Reading the data requires 
% knowledge of the size of the array, and so you have to read in the
% data last.  The if i~=varid & leng(i)>0 test keeps the algorithm
% from reading the data.
for ivar = 1: nvar
if ivar~=varid & leng(ivar)>0
% Read in time, depth, lat, or lon.
  d = mexcdf( 'VARGET', cdfid, ivar-1, 0, leng(ivar) ); 
  if varnam(ivar,1)=='t' | varnam(ivar,1)=='T'
    time = d;
    time_units_string = mexcdf( 'VARGET',netcdf.getVar( cdf( 'ATTGET', cdfid, ivar-1, 'units' );
% Test to see if the first letter of the variable name begins with
% an upper or lower case "x", or an upper or lower case "o" (as in
% longitude).  If yes, than this variable is the x-coordinate (the
% longitude).
  elseif varnam(ivar,1)=='x' | varnam(ivar,1)=='X' | varnam(ivar,2)=='o' ...
    | varnam(ivar,2)=='O'
    xgrid=d;
  elseif varnam(ivar,1)=='y' | varnam(ivar,1)=='Y' | varnam(ivar,2)=='a' ...
    | varnam(ivar,2)=='A'
    ygrid=d;
  else
    level = d;
  end
%disp( sprintf( 'Variable %d size %d %d', ivar, size(d) ) );
end
end

% Special case: Requested a map beyond the end of the data.  For example,
% a file of daily data for the present year where the number of maps
% is equal to the julian date.
if record>length(time) 
disp( sprintf( 'End-of-file.  "data" is set to -1.' ) )
data = -1;
return
end

% Read in the scale_factor and add_offset.
[ datatype, len, status ] = ...
  mexcdf( 'VARGET',netcdf.getVar( cdf( 'ATTINQ', cdfid, varid-1, 'scale_factor' );
if status==-1
  scale_factor = 1;
else
  scale_factor = mexcdf( 'ATTGET', cdfid, varid-1, 'scale_factor' );
end
[ datatype, len, status ] = ...
  mexcdf( 'ATTINQ', cdfid, varid-1, 'add_offset' );
if status==-1
  add_offset = 0;
else
  add_offset   = mexcdf( 'ATTGET', cdfid, varid-1, 'add_offset'   );
end
nam          = mexcdf( 'ATTGET', cdfid, varid-1, 'units'        );

% Read in the history variable
[ datatype, len, status ] = mexcdf( 'ATTINQ', cdfid, -1, 'history' );
if status~=-1
  history = mexcdf( 'ATTGET', cdfid, -1, 'history' );
else
  history = [];
end

% Read in the data variable.
% The first case is data that does not inherently have a time, such as
% topography.
if ndim<=3
[ data, status ] = mexcdf( 'VARGET', cdfid, varid-1, [record-1 0 0], ...
  [ 1 length(ygrid) length(xgrid) ] );
else
% Second case: single level data that changes with time.
  if nargin==2
  [ data, status ] = mexcdf( 'VARGET', cdfid, varid-1, ...
    [ record-1 0 0 0 ], [1 1 length(ygrid) length(xgrid) ] );
  else
% Third case: picking a single level off of a multi-level data set with
% time dependence.
  levelid = find( level==levelreq );
if isempty(levelid);
error( ...
  'You are not specifying the level correctly or this level does not exist.' );
end
disp( sprintf( 'levelid: %d', levelid ) )
  [ data, status ] = mexcdf( 'VARGET', cdfid, varid-1, ...
    [ record-1 levelid-1 0 0 ], [1 1 length(ygrid) length(xgrid) ] );
  end
end

% Convert the time variable into year, month, day, and hour.
% Two-dimensional data sets (for example, elevation) do not have a time
% value.
if ndim==2
iyear = 0;
ipd   = 1;
iday  = 1;
ihour = 0;
isec  = 0;
else

% There are at least two possibilities for the time convention:
% 1) time is written the old-fashioned way: yyyymmddhhmmss
% 2) time is written as "so many units since some reference time"

if isempty( findstr(time_units_string,'since') )
% Time is, by default, written as yyyymmddhhmmss
temp  = time(record);

else
% Time is written as "so many units since some reference time"
% Here, there are again two possibilities.  I wrote my reference times
% to include hours, minutes, and seconds so that it conforms with the
% files written at CDC.  Christian skips the hours, minutes, and seconds
% information if the basic unit of time is "hours".
if     strcmp(time_units_string(1:3),'sec') ; time_units = 'seconds' ;
elseif strcmp(time_units_string(1:3),'Sec') ; time_units = 'seconds'   ;
elseif strcmp(time_units_string(1:3),'hou') ; time_units = 'hours'   ;
elseif strcmp(time_units_string(1:3),'Hou') ; time_units = 'hours'   ;
elseif strcmp(time_units_string(1:3),'day') ; time_units = 'days'    ;
elseif strcmp(time_units_string(1:3),'Day') ; time_units = 'days'    ;
else   error( 'The time unit is not seconds, hours, or days.' );
end
ltu     = length(time_units_string);
yr1     = findstr( time_units_string, 'since' ) + 6;
dashpos = findstr( time_units_string, '-' );
yr2     = dashpos(1)-1;
mon1    = yr2 + 2;
mon2    = dashpos(2)-1;
dy1     = mon2 + 2;
if dy1==ltu | dy1+1==ltu
% Christian's code where "days" are the units of time, and there is
% no hours, minutes, and seconds in the reference time information.
dy2 = ltu;
basetime = ...
    sscanf( time_units_string(yr1:yr2),   '%i' ) * 1.0e10 ...
  + sscanf( time_units_string(mon1:mon2), '%i' ) * 1.0e8  ...
  + sscanf( time_units_string(dy1:dy2),   '%i' ) * 1.0e6;
else
% Reference time includes hours, minutes, seconds information.
temp2   = findstr( time_units_string(dy1+1:ltu), ' ' );
dy2     = dy1 + temp2(1) - 1;
hr1     = dy2 + 2;
colonpos = findstr( time_units_string, ':' );
hr2     = colonpos(1) - 1;
mn1     = hr2 + 2;
mn2     = colonpos(2) - 1;
se1     = mn2 + 2;
periodpos = findstr( time_units_string, '.' );
if isempty(periodpos) ; periodpos = ltu + 1; end
%if periodpos==[] ; periodpos = ltu + 1; end
basetime = ...
    sscanf( time_units_string(yr1:yr2),   '%i' ) * 1.0e10 ...
  + sscanf( time_units_string(mon1:mon2), '%i' ) * 1.0e8 ...
  + sscanf( time_units_string(dy1:dy2),   '%i' ) * 1.0e6 ...
  + sscanf( time_units_string(hr1:hr2),   '%i' ) * 1.0e4 ...
  + sscanf( time_units_string(mn1:mn2),   '%i' ) * 1.0e2 ...
  + sscanf( time_units_string(se1:periodpos-1), '%i' );
end

reltime  = time(record);
[ iyear, ipd, iday, ihour, imin, isec ] = yymmddtime( reltime, ...
  time_units_string );
% date = mexeps( 'array19tot', reltime, time_units, basetime );
temp = date;
end

while 0 
% Turn this off with turning off mexeps.
iyear = fix( temp / 1.0e10 );
temp  = rem( temp,  1.0e10 );
ipd   = fix( temp / 1.0e8  );
temp  = rem( temp,  1.0e8  );
iday  = fix( temp / 1.0e6  );
temp  = rem( temp,  1.0e6  );
ihour = fix( temp / 1.0e4  );
temp  = rem( temp,  1.0e4  );
imin  = fix( temp / 1.0e2  );
temp  = rem( temp,  1.0e2  );
isec  = fix( temp ) + imin * 60;
end
end

% Put in NaNs for the missing values.
missing_value = mexcdf( 'ATTGET', cdfid, varid-1, 'missing_value' );
% NaN2 = NaN;  *** 17 March 2015: turned this off. I don't see what it does. ***
% The CDC folks use INF as their missing value.  You need to 
% test for this slightly differently than you would other missing
% values.
if missing_value==Inf
x = find( data==Inf );
elseif missing_value==32767;
x = find( data==32767 );
else
x = find( abs(data(:)-missing_value) < 1.0e-4 );
end

if( length(x)>0 ); data(x) = ones( length(x), 1 ) * NaN; end
% *** the next two lines were removed on 17 March 2015
% if( length(x)>0 ); data(x) = ones( length(x), 1 ) * NaN2; end 
% NaN  = NaN2;

data = ( data * scale_factor ) + add_offset;

% Put the metadata into the "i4" and "r4" vectors.
nlon     = length(xgrid);
nlat     = length(ygrid);
base     = add_offset;
fctr     = scale_factor;
x0       = xgrid(1);
y0       = ygrid(1);
dx       = xgrid(2) - xgrid(1);
dy       = ygrid(2) - ygrid(1);
i4(1:5)  = [ iyear ipd iday ihour isec ];
i4(6:7)  = [ nlon nlat ];
i4(14)   = NaN;
r4(1:2)  = [ base fctr ];
r4(9:12) = [ x0 y0 dx dy ];

% Reshape the map of data so that it is a row vector (so that the 
% data is a more typical MATLAB form).
% The first transform puts the data in "map-format" --- it is arranged
% like a map.  The second transform operation puts the data into the a 
% row in the way that MATLAB expects things to be ordered.
data = data' ;
data = reshape( data', 1, nlon*nlat );

varid=varid-1;

mexcdf( 'CLOSE', cdfid );

disp( sprintf( ...
  'Finished reading record %d .  Year, month/season, day %d %d %d', ...
  record, iyear, ipd, iday ) )
