classdef ACS < Instrument
  %ACS Summary of this class goes here
  %   Detailed explanation goes here
  
  properties
    device_file = '';
    lambda_ref = [];
    lambda_c = [];
    lambda_a = [];
    cal_param = [];
    modelG50 = [];
    modelmphi = [];
  end
  
  methods
    function obj = ACS(cfg)
      %ACS Construct an instance of this class
      
      % Object Initilization
      obj = obj@Instrument(cfg);
      
      % Change default processing method
      obj.bin_method = 'SB_IN_PRCTL';
      
      % Post initialization
      if isfield(cfg, 'device_file'); obj.device_file = cfg.device_file;
      else; error('Missing field device_file.'); end
      % Set lambda reference to c by default
      if isfield(cfg, 'lambda_reference'); obj.lambda_ref = cfg.lambda_reference; end
      % DEPRECATED AS will be red in device file now
%       if isfield(cfg, 'lambda_a'); obj.lambda_a = cfg.lambda_a;
%       else; error('Missing field lambda_a.'); end
%       if isfield(cfg, 'lambda_c'); obj.lambda_c = cfg.lambda_c;
%       else; error('Missing field lambda_c.'); end

      if isempty(obj.logger) 
        fprintf('WARNING: Logger set to Compass_2.1rc_scheduled.\n');
        obj.logger = 'Compass_2.1rc_scheduled';
      end
    end    
    
    function ReadDeviceFile(obj)
      % Read Device file to set wavelength
      [obj.lambda_c, obj.lambda_a, obj.cal_param] = importACSDeviceFile(obj.device_file);
      if isempty(obj.lambda_ref); obj.lambda_ref = obj.lambda_a; end
    end
    
    function obj = load_HTJSetal2021_model(obj)
      % Load model from Haëntjens et al. 2021v22 to estimate cross-sectional
      % area (G50) and slope of phytoplankton size distribution (in abundance) (mphi):
      load('HTJS20_LinearRegression_5features.mat', 'model_G50');
      load('HTJS20_LinearRegression_5P-mphi.mat', 'model_mphi');
      obj.modelG50 = model_G50;
      obj.modelmphi = model_mphi;
    end
    
    function ReadRaw(obj, days2run, force_import, write)
      % Get wavelengths from device file
      obj.ReadDeviceFile()
      % create wk directory if doesn't exist
      if ~isfolder(obj.path.wk); mkdir(obj.path.wk); end
      % Read raw data
      switch obj.logger
        case 'InlininoACScsv'
          obj.data = iRead(@importInlininoACScsv, obj.path.raw, obj.path.wk, ['acs' obj.sn '_'], ...
                         days2run, 'Inlinino', force_import, ~write, true);
          if isempty(obj.data)
            obj.data = iRead(@importInlininoACScsv, obj.path.raw, obj.path.wk, ['ACS' obj.sn '_'], ...
                           days2run, 'Inlinino', force_import, ~write, true);
          end
        case 'Compass_2.1rc_scheduled'
          warning('DEPRECATED: as wavelength are shifted when saved to .dat by the scheduler of compass.');
          obj.data = iRead(@importACS, obj.path.raw, obj.path.wk, ['acs' obj.sn '_'], ...
                         days2run, 'Compass_2.1rc_scheduled', force_import, ~write, true);
          if isempty(obj.data)
            obj.data = iRead(@importInlininoACScsv, obj.path.raw, obj.path.wk, ['ACS' obj.sn '_'], ...
                           days2run, 'Inlinino', force_import, ~write, true);
          end
        case 'Compass_2.1rc_scheduled_bin'
          obj.data = iRead(@importACSBin, obj.path.raw, obj.path.wk, ['acs' obj.sn '_'], ...
                         days2run, 'Compass_2.1rc_scheduled_bin', force_import, ~write, true, ...
                         true, '', Inf, obj.device_file);
          if isempty(obj.data)
            obj.data = iRead(@importACSBin, obj.path.raw, obj.path.wk, ['ACS' obj.sn '_'], ...
                         days2run, 'Compass_2.1rc_scheduled_bin', force_import, ~write, true, ...
                         true, '', Inf, obj.device_file);
          end
        case 'Compass_2.1rc'
          obj.data = iRead(@importACS, obj.path.raw, obj.path.wk, ['acs' obj.sn '_'], ...
                         days2run, 'Compass_2.1rc', force_import, ~write, true);
          if isempty(obj.data)
            obj.data = iRead(@importACS, obj.path.raw, obj.path.wk, ['ACS' obj.sn '_'], ...
                          days2run, 'Compass_2.1rc', force_import, ~write, true);
          end
        case 'WetView'
          obj.data = iRead(@importACSwetview, obj.path.raw, obj.path.wk, ['acs' obj.sn '_'],...
                         days2run, 'WetView', force_import, ~write, true);
          if isempty(obj.data)
            obj.data = iRead(@importACSwetview, obj.path.raw, obj.path.wk, ['ACS' obj.sn '_'],...
                          days2run, 'WetView', force_import, ~write, true);
          end
        otherwise
          error('ACS: Unknown logger.');
      end
    end
    
    function ReadRawDI(obj, days2run, force_import, write)
      % Get wavelengths from device file
      obj.ReadDeviceFile()
      % Set default parameters
      if isempty(obj.path.di)
        fprintf('WARNING: DI Path is same as raw.\n');
        obj.path.di = obj.path.raw;
      end
      if isempty(obj.di_cfg.logger)
        fprintf('WARNING: DI Logger set to InlininoACScsv.\n');
        obj.di_cfg.logger = 'InlininoACScsv';
      end
      if isempty(obj.di_cfg.postfix)
        fprintf('WARNING: DI Postfix isempty \n');
%         fprintf('WARNING: DI Postfix set to "_DI" \n'); DEPRECATED with Inlinino
%         obj.di_cfg.postfix = '_DI'; DEPRECATED with Inlinino
      end
      if isempty(obj.di_cfg.prefix); obj.di_cfg.prefix = ['DIW_ACS' obj.sn '_']; end
      switch obj.di_cfg.logger
        case 'InlininoACScsv'
          obj.raw.diw = iRead(@importInlininoACScsv, obj.path.di, obj.path.wk, obj.di_cfg.prefix, ...
                         days2run, 'Inlinino', force_import, ~write, ...
                         true, false, obj.di_cfg.postfix);
        case 'Compass_2.1rc_scheduled'
          obj.raw.diw = iRead(@importACS, obj.path.di, obj.path.wk, obj.di_cfg.prefix, ...
                         days2run, 'Compass_2.1rc_scheduled', force_import, ~write, ...
                         true, false, obj.di_cfg.postfix);
        case 'Compass_2.1rc_scheduled_bin'
          obj.raw.diw = iRead(@importACSBin, obj.path.di, obj.path.wk, obj.di_cfg.prefix, ...
                         days2run, 'Compass_2.1rc_scheduled_bin', force_import, ~write, ...
                         true, false, obj.di_cfg.postfix);
        case 'Compass_2.1rc'
          obj.raw.diw = iRead(@importACS, obj.path.di, obj.path.wk, obj.di_cfg.prefix, ...
                         days2run, 'Compass_2.1rc', force_import, ~write, ...
                         true, false, obj.di_cfg.postfix);
        otherwise
          error('ACS: Unknown logger.');
      end
    end
    
    function Calibrate(obj, days2run, compute_dissolved, interpolation_method, CDOM, SWT, di_method, ...
        scattering_corr, compute_ad_aphi, TSG, min_nb_pts_per_cluster) % , time_weight_for_cluster
      lambda = struct('ref', obj.lambda_ref, 'a', obj.lambda_a, 'c', obj.lambda_c);
      SWT_constants = struct('SWITCH_FILTERED', SWT.SWITCH_FILTERED, 'SWITCH_TOTAL', SWT.SWITCH_TOTAL);
      % Load model from Haëntjens et al. 2021v22 to estimate cross-sectional
      % area (G50) and slope of phytoplankton size distribution (in abundance) (mphi):
      obj.load_HTJSetal2021_model();
      switch interpolation_method
        case 'linear'
          if compute_dissolved
            [obj.prod.p, obj.prod.g, obj.prod.QCfailed, ~, DIW_biofouling_correction_new] = processACS(lambda, ...
              obj.qc.tsw, obj.qc.fsw, [], obj.modelG50, obj.modelmphi, obj.bin.diw, ...
              [], SWT, SWT_constants, interpolation_method, di_method, scattering_corr, ...
              compute_ad_aphi, TSG, days2run);
          else
            [obj.prod.p, ~, obj.prod.QCfailed] = processACS(lambda, ...
              obj.qc.tsw, obj.qc.fsw, [], obj.modelG50, obj.modelmphi, [], ...
              [], SWT, SWT_constants, interpolation_method, [], scattering_corr, ...
              compute_ad_aphi, TSG, days2run);
          end
        case 'CDOM'
          if ~isfield(CDOM.prod, 'pd') && isempty(CDOM.qc.tsw)
            error('No CDOM data loaded: required for CDOM interpolation');
          end
          obj.cal_param.min_nb_pts_per_cluster = min_nb_pts_per_cluster;
          % obj.cal_param.time_weight_for_cluster = time_weight_for_cluster;
          if compute_dissolved
            [obj.prod.p, obj.prod.g, obj.prod.QCfailed, ~, DIW_biofouling_correction_new] = processACS(lambda, ...
              obj.qc.tsw, obj.qc.fsw, obj.cal_param, obj.modelG50, obj.modelmphi, obj.bin.diw, ...
              CDOM, SWT, SWT_constants, interpolation_method, di_method, scattering_corr, ...
              compute_ad_aphi, TSG, days2run);
          else
            [obj.prod.p, ~, obj.prod.QCfailed] = processACS(lambda, ...
              obj.qc.tsw, obj.qc.fsw, obj.cal_param, obj.modelG50, obj.modelmphi, [], ...
              CDOM, SWT, SWT_constants, interpolation_method, [], scattering_corr, ...
              compute_ad_aphi, TSG, days2run);
          end
        otherwise
          error('Method not supported.');
      end
      if compute_dissolved
        filename = fullfile(obj.path.ui, [obj.model obj.sn '_QCDI_BiofoulingCorrection.mat']);
        if isfile(filename)
          load(filename, 'DIW_biofouling_correction')
          % % Remove old correction for the same period
          % DIW_biofouling_correction(DIW_biofouling_correction.cleaning_dt > min(days2run) & ...
          %   DIW_biofouling_correction.cleaning_dt < min(days2run)+1, :) = [];
          % add missing variable in old table to merge with new
          missing_var_old = find(~ismember(DIW_biofouling_correction_new.Properties.VariableNames, ...
            DIW_biofouling_correction.Properties.VariableNames));
          if ~isempty(missing_var_old)
            for i = 1:size(missing_var_old, 2)
              DIW_biofouling_correction = addvars(DIW_biofouling_correction, NaT(size(DIW_biofouling_correction,1), 2), ...
                'NewVariableNames', DIW_biofouling_correction_new.Properties.VariableNames(missing_var_old(i)), ...
                'Before', DIW_biofouling_correction.Properties.VariableNames{missing_var_old(i)});
            end
          end
          % add missing variable in new table to merge with old
          missing_var_new = find(~ismember(DIW_biofouling_correction.Properties.VariableNames, ...
            DIW_biofouling_correction_new.Properties.VariableNames));
          if ~isempty(missing_var_new)
            for i = 1:size(missing_var_new, 2)
              DIW_biofouling_correction_new = addvars(DIW_biofouling_correction_new, NaT(size(DIW_biofouling_correction_new,1), 2), ...
                'NewVariableNames', DIW_biofouling_correction.Properties.VariableNames(missing_var_new(i)), ...
                'Before', DIW_biofouling_correction_new.Properties.VariableNames{missing_var_new(i)});
            end
          end
          % merge tables
          DIW_biofouling_correction = [DIW_biofouling_correction; DIW_biofouling_correction_new];
        else
          DIW_biofouling_correction = DIW_biofouling_correction_new;
        end
        save(filename, 'DIW_biofouling_correction')
      end
    end
  end
end