function d = read(instclass, file_or_struct, varargin)
% data = read(instclass, file_or_struct, varargin)
% instclass: os, bb
%
% file_or_struct:  
%     file:  typical usage for a one-time call to look at the data
%             file is the relative path to full raw file name, eg:
%                   '../raw/adcp_pings/9602078r.019'
%     struct: specialized usage for repeated calls (as in "load_enr")
%             uses info from a previous read OF THE SAME FILE 
%             (read the top of this file for more details)
% 
% assume varargin is a collection of name, values pairs
% 'vars', {'vel', 'cor', 'amp', 'ts', 'bt', 'pg', 'nav', 'all'}
% i.e. aquisition of single data types  in a cellarray called 'vars'
%     include any or all in the cellarray to turn them on
% -- or -- if there is only one variable, it can be a string 
% 'vars', 'ts'
% -- or --  use the string 'all' tog get all variables possible
%       (values returned will be NaN if variable is not available)
% 'vars', 'all'
%% 
% 'yearbase', [yearbase]           % default is first year in the file
% 'second_set', [0 or 1]           % if interleaved BB/NB which set?
%                                  % 0 (default) get BB
%                                  % 1           get NB 
%                                  % 
%                                  % different ways to specify data extracted
% 'irange',  [istart istop]        % range of indices to use
% 'ilist', [ index list]           % explicit index list overrides step
% 'step', [step]                   % use only every "step" index
% 'ends', [choose from 0,1,2,4]    % 0 (default - don't do it)
%                                  % 1: get only first profile
%                                  % 2: get first and last profile
%                                  % 3: not implimented
%                                  % 4: get first and last pair of profiles
%                                  % NOTE: use of 'ends' overrides range, step,
%                                  %    and ens_list
% 'back_to_front' [0 or 1]         %   find last good index by reading file
%                                      from back to front (default)
%                                      (otherwise back_to_front = 0 starts
%                                      at the beginning of the file -
%                                      slower but more reliable)
%
%
% 'verbose' [0,1]                  %default is 0; add verbosity
%
%  NOTE on ranges: default is 1:end
%       You may only use one of the following choices.  
%       -  ilist
%       -  irange (step is optional, defaults to 1) - overrides ilist
%       -  ends  - overrides ilist and irange
% 
%  typical one-time usage:
%  rawfile =  '../raw/adcp_pings/nbp2001_091_52596.raw';
%  data = read(os, rawfile, 'vars', 'amp');
%  data = read(os, rawfile, 'vars', {'vel', 'ts'});
%  data = read(os, rawfile, 'yearbase', 2001, 'vars', 'all');
%
%  useful restructuring call:
%  [dd, config] = restruct(bb, data);


%%% ADITIONAL NOTES %%%
%  NOTE a structure called "info" in the outgoing data structure contains
%         - info.fullrawfile        %the whole path to the file
%         - info.rawfilebase        %the base of the file read 
%         - info.ilist              %the index list used to get the data
%                                   % NOTE: must use name, value pair with
%                                   % name 'ilist' to choose the values to
%                                   % be read
%         - info.nbytes             % 
%         - info.config             %
%         - info.last_index         % 
%
%  NOTE file_or_struct - usage with a structure
%          previous call should explicitly output additional info:
%             "d = read(instclass, file_or_strut, 'return_fid', 1, varargin)
%          then call as either
%             "d = read(instclass, d.info , 'return_fid', 1, varargin)
%                                   % does not close raw file upon return
%             "d = read(instclass, d.info , varargin) 
%                                   % closes file upon return
%
%  NOTE ens_num is the raw ensemble value contained in the data.  There could
%       potentially be gaps in ens_num, but it should always increase (with 
%       the single exception of a wrap which occurs at 2^16)
%


% 00/01/31 EF
% 00/02/22 JH (bb from read_os)
% 00/07/05 JH (fixed ipp bug (here and in read_os))  
%  *** NOTE: fix was insufficient - check read_os and fix the fix
% 01/06/02 JH  add instrument class: modify  to match bb/read.m and 
%        nb/read.m since that is the most recent 


%% undocumented features:
%% 'vars' defaults to none, turn on pieces or 'all' exlicitly
%% range defaults to all; choose specific subsets explicitly

% should make it so all cfg fields are initialized - then use fillstruct(x,x,0)
%    to help with debugging (i.e. fail if a new field is introduced)


if (nargin < 2)
   help read 
   error('must set instclass and file_or_struct')
end

id_arg = 0;               % (instclass = os ONLY)
                          % Call with 1 instead of zero to get NB if both NB
                          % and BB are being collected.


%set defaults.  all except field "vars" are present in the original
%  cfg structure; fillstruct will update the values listed
%  Field "vars" will be initialized by fillstruct.  set those chosen to 1
cfg.yearbase  = 0;  %default: use first year in the file
cfg.second_set  = 0; %instclass == os ONLY: (NB and BB collected: get BB)
cfg.verbose     = 0;
cfg.step        = 1;
cfg.irange    = []; % default all range specifications to [] (see function
cfg.ilist     = []; % default all range specifications to [] test_range_request
cfg.back_to_front = 1;  %find last_index by reading back to front
cfg.ends      = []; % default all range specifications to []
cfg.vars =      [];% default variables to []
vars            = {'vel', 'cor', 'amp', 'ts', 'bt', 'pg', 'nav', 'all'};
out.nav         = 0; %initialized "out" as a nonempty structure 



% structure "out" fields from "vars" cellarray initialized as zero
for cii = 1:length(vars)
   out = setfield(out, vars{cii}, 0); 
end


% update cfg.
if (nargin > 2)%   we'll assume the rest is a set of name,value pairs
               %override input structure with name,value pairs
   cfg = fillstruct(cfg, varargin,0 );
end


%take cfg it apart into variables
% first look at the variables that already exist
if (cfg.second_set == 1)      %it was changed from default of 0 to value of 1
   id_arg = 1;              
end
if isa(instclass, 'bb') & (id_arg == 1)
   error('cannot use ''second_set'' with instclass bb')
end


%now check whether cellarray 'vars' has been added - use the elements of
% cfg.vars (if it exists) to turn on aquisition of the listed fields
% NOTE: cellarray "vars" does not have "nav", whereas cfg.vars could.
if (isfield(cfg, 'vars'))
   if (ischar(cfg.vars))  %if there is only variable chosen, allow a string
      cfg.vars = {cfg.vars};
   end
   
   for cii = 1:length(cfg.vars)
      out = setfield(out, cfg.vars{cii}, 1); 
   end
   
   % out.all (override individual chosen data types and output everything)
   if ismember({'all'}, cfg.vars)
      %they turned it on so get everything
      for cii = 1:length(vars)
         out = setfield(out, vars{cii}, 1);
      end
   end
   out = rmfield(out, 'all');  %now remove: there shouldn't be an out.all
end


id = instrument_ids(instclass, id_arg);
params.id = id;
params.yearbase = cfg.yearbase;
params.out = out;

%had to wait for this to get params initialized
if (ischar(file_or_struct))
   file = file_or_struct;
   if (~exist(file, 'file'))
      error(sprintf('file %s does not exist\n', file))
   end
elseif (isstruct(file_or_struct))
   finfo = file_or_struct;
   file       = finfo.fullrawfile;
   nbytes     = finfo.nbytes;
   config     = finfo.config;
   last_index = finfo.last_index;
else
   error('second argument was not an existing file or appropriate structure')
end


%open and close in here
if (~exist('config', 'var')) % file_or_struct was a file
   [nbytes, last_index, config] = get_config(instclass, params, cfg, file);
end

if (isempty(nbytes)) %probably specified "second_set" in error
   d = [];
   return   %jump out now.
end


%otherwise, continue

yearbase = fix_yearbase(config);
cfg.yearbase = yearbase;

ilist = get_ilist(cfg, last_index);

%now initialize everything

NP = length(ilist);  %%1 + floor((stop - start) / step);
NC = config.ncells;
NB = 4; % beams

if out.vel, d.vel = zeros(NC, NB, NP); end
if out.cor, d.cor = zeros(NC, NB, NP); end
if out.amp, d.amp = zeros(NC, NB, NP); end
if out.pg,  d.pg  = zeros(NC, NB, NP); end
if out.ts,
   d.heading = zeros(NP, 1);
   d.pitch =      d.heading;
   d.roll =       d.heading;
   d.soundspeed = d.heading;
   d.temperature =d.heading;
end
if out.bt,
   d.bt.vel = zeros(NB, NP);
   d.bt.range = d.bt.vel;
   d.bt.cor =   d.bt.vel;
   d.bt.amp =   d.bt.vel;
   d.bt.pg =    d.bt.vel;
   d.bt.rssi =  d.bt.vel;
end
if out.nav,
   d.nav.sec_pc_minus_utc = zeros(NP, 1);
   d.nav.txy1 = zeros(3, NP);
   d.nav.txy2 = d.nav.txy1;
end

% Always include dday and ens_num.
d.dday = zeros(NP, 1);
d.ens_num = zeros(NP, 1);
d.num_pings = zeros(NP, 1);
d.config = config;

d.depth = config.tr_depth + config.bin1distance + (0:(NC-1)) * config.cell;
% now loop through ilist
fid = fopen(file, 'r', 'ieee-le');
for ii = 1:length(ilist)
   if (cfg.verbose) ii, end
   
   fseek(fid, nbytes * (ilist(ii) - 1), 'bof');
   [buf, config] = read_buf(instclass, fid, params, config);
   
   if ischar(buf),
      d.error = buf;
      break
   end
   d.dday(ii) = buf.dday;
   d.ens_num(ii) = buf.ens_num;
   d.num_pings(ii) = buf.num_pings;
   if out.ts,
      d.heading(ii) = buf.heading;
      d.pitch(ii)   = buf.pitch;
      d.roll(ii)    = buf.roll;
      d.soundspeed(ii)  = buf.soundspeed;
      d.temperature(ii) = buf.temperature;
   end
   if out.vel, d.vel(:, :, ii) = buf.vel; end
   if out.cor, d.cor(:, :, ii) = buf.cor; end
   if out.amp, d.amp(:, :, ii) = buf.amp; end
   if out.pg,  d.pg(:, :, ii) = buf.pg; end
   if out.bt,
      d.bt.vel(:, ii) = buf.bt.vel;
      d.bt.range(:, ii) = buf.bt.range;
      d.bt.cor(:, ii) = buf.bt.cor;
      d.bt.amp(:, ii) = buf.bt.amp;
      d.bt.pg(:, ii) = buf.bt.pg;
      d.bt.rssi(:, ii) = buf.bt.rssi;
   end
   if out.nav,
      d.nav.sec_pc_minus_utc(ii) = buf.nav.sec_pc_minus_utc;
      d.nav.txy1(:,ii) = buf.nav.txy1;
      d.nav.txy2(:,ii) = buf.nav.txy2;
   end
end

if out.ts,
   d.heading = 0.01 * d.heading;
   d.pitch = 0.01 * to_signed(d.pitch);
   d.roll = 0.01 * to_signed(d.roll);
   d.temperature = 0.01 * to_signed(d.temperature);
end

if out.vel, d.vel = 0.001 * to_signed(d.vel); end

if out.bt,
   d.bt.vel = 0.001 * to_signed(d.bt.vel);
   d.bt.range = 0.01 * d.bt.range;  % BAD is 0; leave it.
end

%%%took out some nasty overrides lefover from NB read

%augment d with some useful information
if (isstr(file_or_struct))
   [fpath, rawfilebase, ext] = fileparts(file);
   d.info.rawfilebase = rawfilebase;
   d.info.fullrawfile = file;
else
   d.info.rawfilebase = finfo.rawfilebase;
   d.info.rawfilebase = finfo.fullrawfile;
end

d.info.ilist = ilist;
d.info.nbytes =     nbytes;
d.info.last_index = last_index;
d.info.config =     config;

fclose(fid);
d.error = [];

%----------------------------------------------
function id = instrument_ids(instclass, incr)
%% get offsets for os or bb
if isa(instclass, 'os')
   id.header     = hex2dec('7f7f');
   id.flead      = 0 + incr;
   id.vlead      = hex2dec('0080') + incr;
   id.vel        = hex2dec('0100') + incr;
   id.cor        = hex2dec('0200') + incr;
   id.amp        = hex2dec('0300') + incr;
   id.pg         = hex2dec('0400') + incr;
   id.status     = hex2dec('0500') + incr; % Useless; ignore it.
   id.bt         = hex2dec('0600') + incr;
   % Added in STA and LTA files:
   id.nav        = hex2dec('2000');           % No separate nav type.
   id.fix_att    = hex2dec('3000');       % Or fixed attitude.
   id.var_att1   = hex2dec('30e0');
   id.var_att2   = hex2dec('30e8');
   id.var_att    = id.var_att1; % default: NP only
                             % other possibilities to be added.
else
   id.header     = hex2dec('7f7f');
   id.flead      = hex2dec('0000');
   id.vlead      = hex2dec('0080');          % 128
   id.vel        = hex2dec('0100');          % 256;
   id.cor        = hex2dec('0200');          % 512;
   id.amp        = 768;
   id.pg         = hex2dec('0400');          % 1024;
   id.bt         = 1536;
   id.nav        = 8192;
end

