function d = read(instclass, file_or_struct, varargin)
% data = read(instclass, file_or_struct, varargin)
%
% instclass (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
% acquisition of single data types  in a cellarray called 'vars'
% include any or all in the cellarray to turn them on
% 'vars',{'vel', 'cor', 'amp', 'pg', 'ts', 'bt', 'all'}
% -- or -- if there is only one variable, it can be a string
% 'vars', 'ts'
%
% 
% 'yearbase', [yearbase]             % default is first year in the file
%
% %% accessing the data : default is [1 : step : end]
% '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
% '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 or 1]                % - be verbose. default = 0 (not verbose)
%
%  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(bb, rawfile, 'vars', 'amp');
%  data = read(bb, rawfile, 'vars', {'vel', 'ts'});
%  data = read(bb, rawfile, 'yearbase', 2001, 'vars', 'all');
%  
%  useful restructuring call:
%  [data, config] = restruct(bb, d);


%%% 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 old read_bb.m to match 
%        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


%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;  % it it's still 0 after varargin, abort.  
cfg.second_set = 0;  %so we can use the same load_raw as the os has
cfg.step      = 1;
cfg.verbose   = 0;  % not verbose
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', 'pg', 'ts', 'bt', 'all'};
out.nav         = 0; %initialized "out" as a nonempty structure AND
                     % separate 'nav' from the rest of 'all' (since 
                     % there may not be any)

% 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

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

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


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

ilist = get_ilist(cfg, last_index);

%initialize everything
NP = length(ilist); 
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
%% NOTE (compared to narrow band):
%%      no  'sw', or 'status' here, but we do have 'cor'
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.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;

fid = fopen(file, 'r', 'ieee-le');  % the BB processor writes little-endian
% now loop through ilist
for ii = 1:length(ilist);
   fseek(fid, nbytes * (ilist(ii) - 1), 'bof'); %use ilist
   [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;
   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 = config.vel_multiplier * to_signed(d.vel); end

if out.bt,
    %% hardwired for beam--temporary
    %%this is beam coordinates so the config.vel_multiplier is hardwired
    %%       for beam coord      %% should be OK for shipboard use
    %% maybe need to change this depending on beam coords or geographic coords
   d.bt.vel = config.vel_multiplier * to_signed(d.bt.vel);
   d.bt.range = 0.01 * d.bt.range;  % BAD is 0; leave it.
end

%%%% add things which will make our lives easier later:
d.config.bin1distance = config.blank + (config.cell + config.pulse)/2;
d.config.tr_depth = 0;        % unknown in nb data; set to 0
d.num_pings = ones(size(d.dday));% only one ping per "ensemble"

%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 = [];

