function indices = ehtFieldIndices(cfgFormat,varargin)
% ehtFieldIndices Generate field indices for EHT MU packet
%
%   INDICES = ehtFieldIndices(CFGFORMAT) returns the start and end
%   time-domain sample indices for all fields in a packet relative to the
%   first sample in a packet.
%
%   INDICES is a structure array of field names for the specified
%   configuration and contains the start and end indices of all fields in a
%   packet.
%
%   CFGFORMAT is a format configuration object of type <a
%   href="matlab:help('ehtMUConfig')">ehtMUConfig</a>.
%
%   INDICES = ehtFieldIndices(CFGFORMAT,FIELDNAME) returns the start and
%   end time-domain sample indices for the specified FIELDNAME in a packet.
%   INDICES is a row vector of size two representing the start and end
%   sample indices of the specified field.
%
%   FIELDNAME is a character vector or string specifying the field of
%   interest. FIELDNAME must be one of 'L-STF', 'L-LTF', 'L-SIG', 'RL-SIG'
%   'U-SIG', 'EHT-SIG', 'EHT-STF', 'EHT-LTF', 'EHT-Data', and 'EHT-PE'.
%
%   INDICES = ehtFieldIndices(...,'OversamplingFactor',OSF) returns the
%   start and end time-domain sample indices assuming the waveform is
%   oversampled by the factor OSF. OSF must be greater than or equal to 1.
%   If not specified, 1 is assumed.
%
%   Example:
%   %   Extract the EHT-Data fields to decode EHT MU data from the received
%   %   802.11be EHT MU transmission.
%
%      cfg = ehtMUConfig('CBW320');   % Create packet configuration
%      cfg.User{1}.MCS = 12;          % Modulation: 4096 QAM, Rate: 3/4
%      cfg.User{1}.APEPLength = 1024; % A-MPDU length in bytes
%
%      % Generate transmit waveform
%      txBits = randi([0 1],getPSDULength(cfg)*8,1,'int8');
%      tx = ehtWaveformGenerator(txBits,cfg);
%
%      % Add noise
%      ofdmInfo = ehtOFDMInfo('EHT-Data',cfg); % Extract OFDM parameters
%      snrVal = 35;
%      snrVal = snrVal-10*log10(ofdmInfo.FFTLength/ofdmInfo.NumTones);
%      rx = awgn(tx,snrVal);
%
%      % Recover transmitted bits, assuming synchronization
%      ind = ehtFieldIndices(cfg);
%
%      % OFDM demodulate
%      rxData = rx(ind.EHTData(1):ind.EHTData(2),:);
%      demodSym = ehtDemodulate(rxData,'EHT-Data',cfg);
%      rxSym = demodSym(ofdmInfo.DataIndices,:,:);
%
%      % Noise power
%      nVarEst = 10^(-snrVal/10);
%
%      % Recover data bits
%      rxBits = ehtDataBitRecover(rxSym,nVarEst,cfg);
%      disp(isequal(txBits,rxBits))
%
%   See also ehtOFDMInfo, ehtDemodulate, ehtDataBitRecover.

%   Copyright 2020-2021 The MathWorks, Inc.

narginchk(1,4)
% Validate input is a class object
validateattributes(cfgFormat,{'ehtMUConfig'},{'scalar'}, mfilename,'format configuration object');

p = inputParser;
validFieldName = @(x) any(validatestring(x,{'L-STF','L-LTF','L-SIG','RL-SIG','U-SIG','EHT-SIG','EHT-STF','EHT-LTF','EHT-Data','EHT-PE'}));
validOSF = @(x) validateattributes(x,{'numeric'},{'scalar','>=',1});
addOptional(p,'FieldName','all',validFieldName);
addParameter(p,'OversamplingFactor',1,validOSF);
parse(p,varargin{:}); % Parse inputs
res = p.Results;
osf = res.OversamplingFactor;
fieldName = res.FieldName;

if strcmp(fieldName,'all')
    indices = getEHTIndices(cfgFormat,osf);
else
    indices = getEHTIndices(cfgFormat,osf,fieldName);
end

end

function ind = getEHTIndices(cfg,osf,varargin)
%getEHTIndices Get field indices of EHT MU packet

    trc = ehtTimingRelatedConstants(cfg.ChannelBandwidth,cfg.GuardInterval,cfg.EHTLTFType,4); % Use default preFEC padding factor
    cbw = wlan.internal.cbwStr2Num(cfg.ChannelBandwidth);
    sf = cbw*osf*1e-3; % Scaling factor to convert bandwidth and time in ns to samples

    % L-STF
    nFieldSamples = trc.TLSTF*sf;
    indLSTF = [1 nFieldSamples];
    numCumSamples = nFieldSamples;
    validateIndices(indLSTF,osf,'L-STF');
    if nargin>2 && strcmpi(varargin{1},'L-STF')
        ind = uint32(indLSTF);
        return;
    end

    % L-LTF
    nFieldSamples = trc.TLLTF*sf;
    indLLTF = [numCumSamples+1 numCumSamples+nFieldSamples];
    numCumSamples = numCumSamples+nFieldSamples;
    validateIndices(indLLTF,osf,'L-LTF');
    if nargin>2 && strcmpi(varargin{1},'L-LTF')
        ind = uint32(indLLTF);
        return;
    end

    % L-SIG
    nFieldSamples = trc.TLSIG*sf;
    indLSIG = [numCumSamples+1 numCumSamples+nFieldSamples];
    numCumSamples = numCumSamples+nFieldSamples;
    validateIndices(indLSIG,osf,'L-SIG');
    if nargin>2 && strcmpi(varargin{1},'L-SIG')
        ind = uint32(indLSIG);
        return;
    end

    % RL-SIG
    nFieldSamples = trc.TRLSIG*sf;
    indRLSIG = [numCumSamples+1 numCumSamples+nFieldSamples];
    numCumSamples = numCumSamples+nFieldSamples;
    validateIndices(indRLSIG,osf,'RL-SIG');
    if nargin>2 && strcmpi(varargin{1},'RL-SIG')
        ind = uint32(indRLSIG);
        return;
    end

    % U-SIG
    nFieldSamples = trc.TUSIG*sf;
    indUSIG = [numCumSamples+1 numCumSamples+nFieldSamples];
    numCumSamples = numCumSamples+nFieldSamples;
    validateIndices(indUSIG,osf,'U-SIG');
    if nargin>2 && strcmpi(varargin{1},'U-SIG')
        ind = uint32(indUSIG);
        return;
    end

    % EHT-SIG
    sigInfo = ehtSIGCodingInfo(cfg);
    numEHTSIGSymbols = sigInfo.NumSIGSymbols;
    nFieldSamples = trc.TEHTSIG*numEHTSIGSymbols*sf;
    indEHTSIG = [numCumSamples+1 numCumSamples+nFieldSamples];
    numCumSamples = numCumSamples+nFieldSamples;
    validateIndices(indEHTSIG,osf,'EHT-SIG');
    if nargin>2 && strcmpi(varargin{1},'EHT-SIG')
        ind = uint32(indEHTSIG);
        return;
    end

    % EHT-STF
    nFieldSamples = trc.TEHTSTFNT*sf;
    indEHTSTF = [numCumSamples+1 numCumSamples+nFieldSamples];
    numCumSamples = numCumSamples+nFieldSamples;
    validateIndices(indEHTSTF,osf,'EHT-STF');
    if nargin>2 && strcmpi(varargin{1},'EHT-STF')
        ind = uint32(indEHTSTF);
        return;
    end

    % EHT-LTF
    validateConfig(cfg,'EHTLTFGI'); % Validate GuardInterval and EHTLTFType type
    allocInfo = ruInfo(cfg);
    numLTFSym = wlan.internal.numVHTLTFSymbols(max(allocInfo.NumSpaceTimeStreamsPerRU));

    nFieldSamples = trc.TEHTLTFSYM*numLTFSym*sf;
    indEHTLTF = [numCumSamples+1 numCumSamples+nFieldSamples];
    numCumSamples = numCumSamples+nFieldSamples;
    validateIndices(indEHTLTF,osf,'EHT-LTF');
    if nargin>2 && strcmpi(varargin{1},'EHT-LTF')
        ind = uint32(indEHTLTF);
        return;
    end

    % EHT-Data
    s = validateConfig(cfg,'DataLocationLength'); % Validate parameters for data field location and length
    Nsym = s.NumDataSymbols; % Number of data symbols
    if Nsym==0 % NDP
        indEHTData = zeros(0,2); % NDP
    else
        TEHTDATA = Nsym*trc.TSYM;
        nFieldSamples = TEHTDATA*sf;
        indEHTData = [numCumSamples+1 numCumSamples+nFieldSamples];
        numCumSamples = numCumSamples+nFieldSamples;
    end
    validateIndices(indEHTData,osf,'EHT-Data');
    if nargin>2 && strcmpi(varargin{1},'EHT-Data')
        ind = uint32(indEHTData);
        return;
    end

    % EHT-PE
    % Calculate number of PE samples
    commonCodingParams = ehtCodingParameters(cfg);
    npp = wlan.internal.heNominalPacketPadding(cfg);
    trc = ehtTimingRelatedConstants(cfg.ChannelBandwidth,cfg.GuardInterval,cfg.EHTLTFType,commonCodingParams.PreFECPaddingFactor,npp,commonCodingParams.NSYM);
    nFieldSamples = trc.TPE*sf;
    if trc.TPE~=0
        indEHTPE = [numCumSamples+1 numCumSamples+nFieldSamples];
    else
        indEHTPE = zeros(0,2);
    end
    validateIndices(indEHTPE,osf,'EHT-PE');
    if nargin>2 && strcmpi(varargin{1},'EHT-PE')
        ind = uint32(indEHTPE);
        return;
    end

    % Return indices for all fields
    indField = struct;
    indField.LSTF = uint32(indLSTF);
    indField.LLTF = uint32(indLLTF);
    indField.LSIG = uint32(indLSIG);
    indField.RLSIG = uint32(indRLSIG);
    indField.USIG = uint32(indUSIG);
    indField.EHTSIG = uint32(indEHTSIG);
    indField.EHTSTF = uint32(indEHTSTF);
    indField.EHTLTF = uint32(indEHTLTF);
    indField.EHTData = uint32(indEHTData);
    indField.EHTPE = uint32(indEHTPE);
    ind = indField;
end

function validateIndices(indices,osf,varargin)
if osf==1
    % No need to validate
    return
end
if isstruct(indices)
    field = fieldnames(indices);
    for i = 1:numel(field) % Test each filed in the structure
        ind = indices.(field{i});
        if isempty(ind)
            continue % No need to test empty fields
        end
        fieldLength = ind(:,2)-ind(:,1);
        invalidInd = any(floor(fieldLength)~=fieldLength);
        if any(invalidInd)
            firstFailingInd = find(invalidInd); % In error show first failing ind
            coder.internal.error('wlan:wlanFieldIndices:InvalidOSF',sprintf('%f',osf),field{i},round((fieldLength(firstFailingInd(1))+1)/osf));
        end
    end
elseif ~isempty(indices)
    fieldLength = indices(2)-indices(1);
    field = varargin{1};
    if any(floor(fieldLength)~=fieldLength)
        coder.internal.error('wlan:wlanFieldIndices:InvalidOSF',sprintf('%f',osf),field,round((fieldLength+1)/osf));
    end
end
end