function [apNodeIDMap, nodeMap, isAP, nodeMACPath] = edcaGetBSSInfo(nodeNamesList)
%edcagetBSSInfo Return BSS information
%
%   [APNODEIDMAP, NODEMAP, ISAP, NODEMACPATH] =
%   edcaGetBSSInfo(NODENAMESLIST) returns BSS information
%
%   APNODEIDMAP maps node IDs of nodes to their respective AP node IDs,
%   specified as an array.
%
%   NODEMAP maps node IDs of nodes to their respective paths of node
%   blocks, specified as a string array.
%
%   ISAP maps node IDs of nodes to their respective node types (AP or STA),
%   specified as a logical array.
%
%   NODEMACPATH maps node IDs to their respective paths of EDCA MAC block,
%   specified as an array.
%
%   NODENAMESLIST is a list of all nodes in the Simulink model specified as
%   a cell array.

%   Copyright 2020 The MathWorks, Inc.

% Total number of nodes
numNodes = numel(nodeNamesList);
% Initialize a string array to map node IDs to the node path
nodeMap = strings([1, numNodes]);
% Initialize an array to map node IDs to their respective AP node IDs
apNodeIDMap = zeros(1, numNodes);
% Number of AP's
numAP = 0;
% Initialize a logical array to map node IDs to their node types (AP/STA)
isAP = false(1, numNodes);

% Loop over all the nodes
for i = 1:numNodes
    % Get the current node ID
    currentNodeID = str2double(get_param(nodeNamesList{i}, 'NodeID'));
    % Get the node type
    nodeType = get_param(nodeNamesList{i}, 'NodeType');
    nodeMap(currentNodeID) = nodeNamesList{i};
    % Get the node name
    [~, nodeName] = fileparts(nodeNamesList{i});
    if strcmp(nodeType, 'AP')
        set_param(nodeNamesList{i}, 'NodeTypeFlag', '1');
        isAP(currentNodeID) = true;
        numAP = numAP + 1;
        apNodeIDMap(currentNodeID) = currentNodeID;
    else
        set_param(nodeNamesList{i}, 'NodeTypeFlag', '0');
        apName = get_param(nodeNamesList{i}, 'APName');
        
        % Error out if the AP Name parameter is empty
        if isempty(apName)
            error(['The value of parameter AP Name of node ', nodeName, ' cannot be empty. Set AP Name to a valid AP node in the network.'])
        end
        
        destinationNode = Simulink.findBlocks(bdroot, 'Name', apName);
        
        if isempty(destinationNode)
            error(['Invalid value of parameter AP Name of node ', nodeName, '. Set AP Name to a valid AP node in the network.'])
        end
        
        if ~strcmp(get_param(getfullname(destinationNode), 'NodeType'), 'AP')
            error(['Invalid value of parameter AP Name of node ', nodeName, '. Set AP Name to the node for which Node Type parameter is configured as AP.'])
        end
        apNodeIDMap(currentNodeID) = str2double(get_param(destinationNode,'NodeID'));
    end
end

% Check if all the nodes in a BSS do not have the same BSS color.
[nodeMACPath] = bssChecks(isAP, apNodeIDMap, nodeMap, numNodes, '1');

% Check if multiple AP's have same BSS color.
bssChecks(isAP, apNodeIDMap, nodeMap, numNodes, '2');
end

function nodeMACPath = bssChecks(isAP, apNodeIDMap, nodeMap, numNodes, check)
    % Initialize an array to indicate unprocessed nodes in a BSS
    processBSSNodes = ones(1, numel(apNodeIDMap));
    % Initialize an array to indicate which colors are used
    apColorPresent = zeros(1, 63);
    % Initialize a string array to map node IDs to the node EDCA MAC path
    nodeMACPath = strings([1, numNodes]);
    % Loop over all the nodes
    
    for i = 1:numNodes
        if processBSSNodes(i)
            % Get all the node IDs per BSS
            bss = (apNodeIDMap == apNodeIDMap(i));
            bssNodeIDs = find(bss);

            % Initialize BSS color variable
            bssColorPrev = '0';

            % Node ID of AP
            apNodeID = bssNodeIDs(isAP(bssNodeIDs));

            % Get the AP node name
            [~, apName] = fileparts(nodeMap(apNodeID));

            for j = 1:numel(bssNodeIDs)
                % Extract the list of block names in the model
                data = getfullname(Simulink.findBlocks(nodeMap(bssNodeIDs(j))));
                for k = 1:length(data)
                    % Get the dialouge parameters of the block
                    dialogPrms = get_param(data{k}, 'DialogParameters');

                    % If block mask params contains 'EnableSROperation' field
                    % and if SR operation is enabled for the node
                    if isfield(dialogPrms, 'EnableSROperation') && strcmp(get_param(data{k}, 'EnableSROperation'), 'on')
                        % Get the BSS color value
                        bssColorCurr = get_param(data{k}, 'BSSColor');

                        switch check
                            case '1'
                                % Error out, if all the nodes in a BSS do not have the
                                % same BSS color.
                                if str2double(bssColorPrev) ~= 0 && ~strcmp(bssColorCurr, bssColorPrev)
                                    error(['BSS colors of the nodes in the BSS containing AP (', char(apName), ') do not match. All the nodes in a BSS must have the same BSS color.']);
                                end
                            case '2'
                                % Error out, if multiple AP's have same BSS color.
                                if (apColorPresent(str2double(bssColorCurr)) ~= apNodeID) && apColorPresent(str2double(bssColorCurr))
                                    collisonedAPIDs = [apColorPresent(str2double(bssColorCurr)), apNodeID];
                                    [~, AP1] = fileparts(nodeMap(collisonedAPIDs(1)));
                                    [~, AP2] = fileparts(nodeMap(collisonedAPIDs(2)));
                                    error(['AP ', char(AP1), ' and AP ', char(AP2), ' have same BSS color. Each AP must have a unique BSS color.']);
                                end
                        end
                        apColorPresent(str2double(bssColorCurr)) = apNodeID;
                        % Update previous node BSS color with current node BSS
                        % color
                        bssColorPrev = bssColorCurr;
                    end
                    if isfield(dialogPrms, 'EnableSROperation')
                        nodeMACPath(bssNodeIDs(j)) = data{k};
                    end
                end
                processBSSNodes(bssNodeIDs(j)) = 0;
            end
        end
    end
end
