function H = generateChannels(chan,x,varargin)
%generateChannels generates channel matrices between nodes in a simulation
% H = generateChannels(CHAN,NUMNODES) returns a frequency domain channel
% matrix H between multiple nodes in a simulation. The channel and OFDM
% configuration is assumed to be the same for all links in the simulation.
%
% H is an array of size Nst-by-Ntx-by-Nrx-by-Nl where Nst is the number of
% active subcarriers, Ntx is the number of transmit antennas, Nrx is the
% number of receive antennas and Nl is the number of links.
%
% CHAN is a wlanTGaxChannel, wlanTGacChannel or wlanTGnChannel object. The
% ChannelFiltering property must be set to false and the NumSamples
% property must be set. The channel configuration is assumed to be the same
% between all nodes.
%
% NUMNODES is the number of nodes in the simulation. This assumes all nodes
% can transmit and receive and channels between the nodes are reciprocal.
%
% H = generateChannels(CHAN,LINKMASK) returns a frequency domain channel
% matrix H between all nodes in a simulation for downlink node links
% described by LINKMASK.
%
% LINKMASK is a NumTxNodes-by-NumRxNodes logical array were an element is
% true if a link between a transmitter and receiver is to be modeled and
% false otherwise. NumTxNodes is the number of transmitters and NumRxNodes
% is the number of receivers. This assumes that the transmitters and
% receivers are separate nodes and there are no reciprocal channels.
%
% H = generateChannels(...,CFGSU) returns a frequency domain channel matrix
% H between all nodes assuming a data field OFDM configuration described by
% the format configuration object CFGSU. If not provided an HE-SU
% configuration is assumed with the channel bandwidth set to the channel
% bandwidth specified in CHAN.
%
% CFGSU is a wlanHESUConfig, wlanVHTConfig, wlanHTConfig or wlanNonHTConfig
% object which specifies the transmission parameters.
%
% H = generateChannels(...,CFGMU,RUIDX) returns a frequency domain channel
% matrix H between all nodes assuming a data field OFDM configuration
% described by the multi-user format configuration object CFGMU and RU
% index RUIDX.
%
% CFGMU is a wlanHEMUConfig object which specifies the transmission
% parameters.
%
% H = getChannelMatrix(...,CFG,FIELD) returns the channel matrix for the
% given field, either 'preamble' or 'data'. If not provided the data field
% is assumed.

%   See also channelIndex.

%   Copyright 2019 The MathWorks, Inc.

%#codegen

if isscalar(x)
    numNodes = x;
    % Generate channels between all transmitters and receivers.
    % * Assume a transmitter and receiver are the same node,
    %   therefore do not generate a channel between a node and
    %   itself.
    % * Assume the channel is reciprocal between a transmitter
    %   and receiver therefore only generate channels in one
    %   direction.
    linkMask = triu(~eye(numNodes,numNodes));
else
    % Assume transmitting nodes are different to receiving nodes
    linkMask = logical(x); % Force to logical
end

if nargin>2
    cfg = varargin{1};
else
    cfg = wlanHESUConfig('ChannelBandwidth',chan.ChannelBandwidth);
end

ruIdx = 1;
field = 'data';
if nargin>3
    if isa(cfg,'wlanHEMUConfig')
        ruIdx = varargin{2};
        if nargin>4
            field = varargin{3};
        end
    else
        field = varargin{2};
    end
end

reset(chan);
chanInfo = info(chan);
pathFilters = chanInfo.ChannelFilterCoefficients;

% Allocate arrays to store channels generated
numChannels = sum(linkMask,'all');

ofdmInfo = getOFDMInfo(cfg,field,ruIdx);
    
% Generate channels
H = coder.nullcopy(complex(zeros(ofdmInfo.NumTones,chan.NumTransmitAntennas,chan.NumReceiveAntennas,numChannels)));
for ichan = 1:numChannels
    % Generate new channel response
    pathGains = generateChannel(chan);

    % Generate channel matrix
    H(:,:,:,ichan) = getChannelMatrix(pathGains,pathFilters,ofdmInfo);
end

end

function pathGains = generateChannel(chan)
    % Reset channel for a new realization
    reset(chan);

    % Get path gains
    pathGains = chan();
end

function ofdmInfo = getOFDMInfo(cfg,field,varargin)
    % Get OFDM info for preamble and data portion
    switch field
        case 'data'
            switch class(cfg)
                case 'wlanHEMUConfig'
                    ofdmInfo = wlanHEOFDMInfo('HE-Data',cfg,varargin{:});
                case 'wlanHESUConfig'
                    ofdmInfo = wlanHEOFDMInfo('HE-Data',cfg);
                case 'wlanVHTConfig'
                    ofdmInfo = wlanVHTOFDMInfo('VHT-Data',cfg);
                case 'wlanHTConfig'
                    ofdmInfo = wlanHTOFDMInfo('HT-Data',cfg);
                case 'wlanNonHTConfig'
                    ofdmInfo = wlanNonHTOFDMInfo('NonHT-Data');
                otherwise
                    error('Unexpected format');
            end
        case 'preamble'
            switch class(cfg)
                case 'wlanHEMUConfig'
                    ofdmInfo = wlanHEOFDMInfo('HE-SIG-A',cfg);
                case 'wlanHESUConfig'
                    ofdmInfo = wlanHEOFDMInfo('HE-SIG-A',cfg);
                case 'wlanVHTConfig'
                    ofdmInfo = wlanVHTOFDMInfo('VHT-SIG-A',cfg);
                case 'wlanHTConfig'
                    ofdmInfo = wlanHTOFDMInfo('HT-SIG',cfg);
                case 'wlanNonHTConfig'
                    ofdmInfo = wlanNonHTOFDMInfo('NonHT-Data');
                otherwise
                    error('Unexpected format');
            end
        otherwise
            error('unexpected field')
    end
end

function H = getChannelMatrix(pathGainsUse,pathFiltersUse,ofdmInfo)
    % Perfect channel estimate
    Htmp = helperPerfectChannelEstimate(pathGainsUse,pathFiltersUse,ofdmInfo.FFTLength,ofdmInfo.CPLength,ofdmInfo.ActiveFFTIndices);
    % Average over symbols to Nst-by-Nt-by-Nr
    H = permute(mean(Htmp,2),[1 3 4 5 2]);
end