function hePlotHESIGBAllocationMapping(cfgHE,varargin)
%hePlotHESIGBAllocationMapping Plots the HE-SIG-B allocation for a given HE-MU configuration object
%
%   Note: This is an internal undocumented function and its API and/or
%   functionality may change in subsequent releases.
%
%   hePlotHESIGBAllocationMapping(cfgHE) plots the HE-SIG-B allocation
%   mapping. CFGHE is the format configuration object of type 
%   <a href="matlab:help('wlanHEMUConfig')">wlanHEMUConfig</a>.
%
%   hePlotHESIGBAllocationMapping(cfgHE,AX) plots the allocation in the
%   axes specified by AX instead of in the current axes.

%   Copyright 2017-2019 The MathWorks, Inc.

resize = true; % Resize plot by default
if nargin==1
    ax = gca;
    if strcmp(ax.Title.String,'HE-SIG-B Allocation Signaling')
        % If axis already displaying this type of plot then resize
        resize = false;
    end
else
    ax = varargin{1};
end
cla(ax,'reset')

sigbInfo = wlan.internal.heSIGBCodingInfo(cfgHE);

allocInfo = ruInfo(cfgHE);

% Get the user numbers in each content channel
% Get the RU size and index for each user
ruNumber = zeros(1,allocInfo.NumUsers);
userNumber = zeros(1,allocInfo.NumUsers);
userIdx = 1;
for i = 1:allocInfo.NumRUs
    for j = 1:allocInfo.NumUsersPerRU(i)
        ruNumber(userIdx) = i;
        userNumber(userIdx) = j;
        userIdx = userIdx+1;
    end
end

numColorsUse = sum(sigbInfo.NumUsersPerContentChannel)+sigbInfo.NumContentChannels;
cm = colormap(ax);
numPossibleColors = size(cm,1); % Get number of colors in colormap
c = floor(numPossibleColors/4); % First color: offset from darkest shades
colorStep = floor((numPossibleColors-c)/numColorsUse);

% Plot the RU details

% Get the RU details for content channel 1
ruIndicesCC1 = allocInfo.RUIndices(ruNumber(sigbInfo.ContentChannel1Users));
ruSizesCC1 = allocInfo.RUSizes(ruNumber(sigbInfo.ContentChannel1Users));
ruDetailsTxtCC1 = getRUDetailsTxt(ruIndicesCC1,ruSizesCC1,ruNumber(sigbInfo.ContentChannel1Users));
numCFRows = numel(ruDetailsTxtCC1);
maxNumUsers = numel(ruIndicesCC1);
if sigbInfo.NumContentChannels>1
    % Get the RU details for content channel 2
    ruIndicesCC2 = allocInfo.RUIndices(ruNumber(sigbInfo.ContentChannel2Users));
    ruSizesCC2 = allocInfo.RUSizes(ruNumber(sigbInfo.ContentChannel2Users));
    ruDetailsTxtCC2 = getRUDetailsTxt(ruIndicesCC2,ruSizesCC2,ruNumber(sigbInfo.ContentChannel2Users));
    numCFRows = max(numCFRows,numel(ruDetailsTxtCC2));
    maxNumUsers = max(maxNumUsers,numel(ruIndicesCC2));
end

% Set the font size based on the number of user fields
if maxNumUsers>=16
    fs = 6.5;
elseif maxNumUsers>=9
    fs = 8;
else
    fs = 10;
end

if ~sigbInfo.Compression
    % Plot common field if compression not used
    y = [1 0 0 1];
    x = [0 0 4 4];
    patch(ax,x,y,c,'CDataMapping','direct');
    c = c+colorStep;
    hold(ax,'on');
    if ~isempty(ruDetailsTxtCC1)
        text(ax,2,0.5,{'RU allocation for',ruDetailsTxtCC1{:}},'HorizontalAlignment','center','FontSize',fs); %#ok<CCAT>
    else
        text(ax,2,0.5,{'RU allocation with no','user specific fields'},'HorizontalAlignment','center','FontSize',fs);
    end
    usOffset = 4; % User specific field start offset when common field present
else
    usOffset = 0; % User specific field start offset when no common field present
end

for cc1UserNum = 1:sigbInfo.NumUsersPerContentChannel(1)
    % Plot user specific fields for content channel 1
    y = [1 0 0 1];
    x = [0 0 1 1] + cc1UserNum - 1 + usOffset;
    patch(ax,x,y,c,'CDataMapping','direct');
    hold(ax,'on');
    text(ax,0.5 + cc1UserNum - 1 + usOffset,0.5,{sprintf('User #%d',sigbInfo.ContentChannel1Users(cc1UserNum)) ...
        sprintf('RU #%d',ruNumber(sigbInfo.ContentChannel1Users(cc1UserNum)))}, ...
        'HorizontalAlignment','center','FontSize',fs);
    c = c+colorStep;
end
if isempty(cc1UserNum)
    cc1UserNum = 0;
end
% Set xlim to avoid trailing whitespace
maxX = x(end);
xlim([0 maxX]);

if sigbInfo.NumContentChannels>1
    % Plot the RU details for content channel 2    
    if ~sigbInfo.Compression
        % Plot common field if compression not used
        y = [1 0 0 1]+1;
        x = [0 0 4 4];
        patch(ax,x,y,c,'CDataMapping','direct');
        if ~isempty(ruDetailsTxtCC2)
            text(ax,2,1.5,{'RU allocation for',ruDetailsTxtCC2{:}},'HorizontalAlignment','center','FontSize',fs) %#ok<CCAT>
        else
            text(ax,2,1.5,{'RU allocation with no','user specific fields'},'HorizontalAlignment','center','FontSize',fs)
        end
        c = c+colorStep;
    end
    
    for cc2UserNum = 1:sigbInfo.NumUsersPerContentChannel(2)
        % Plot user specific fields for content channel 2
        y = [2 1 1 2];
        x = [0 0 1 1] + cc2UserNum - 1 + usOffset;
        patch(ax,x,y,c,'CDataMapping','direct');
        hold(ax,'on');
        text(ax,0.5 + cc2UserNum - 1 + usOffset,1.5,{sprintf('User #%d',sigbInfo.ContentChannel2Users(cc2UserNum)), ...
            sprintf('RU #%d',ruNumber(sigbInfo.ContentChannel2Users(cc2UserNum)))}, ...}, ...
            'HorizontalAlignment','center','FontSize',fs);
        c = c+colorStep;
    end
    if isempty(cc2UserNum)
        cc2UserNum = 0;
    end
    xlim([0 max(x(end),maxX)]);
else
    cc2UserNum = 0;
end

% Plot padding
if sigbInfo.NumContentChannels>1
    cc1UsersToPad = cc2UserNum-cc1UserNum;
    if cc1UsersToPad>0
        patch(ax,[0 0 cc1UsersToPad cc1UsersToPad] + cc1UserNum + usOffset,[1 0 0 1],[0.9 0.9 0.9])
        text(ax,cc1UsersToPad/2 + cc1UserNum + usOffset, 0.5,'Padding','HorizontalAlignment','center','FontSize',fs);
    end
    cc2UsersToPad = cc1UserNum-cc2UserNum;
    if cc2UsersToPad>0
        patch(ax,[0 0 cc2UsersToPad cc2UsersToPad] + cc2UserNum + usOffset,[2 1 1 2],[0.9 0.9 0.9])
        text(ax,cc2UsersToPad/2 + cc2UserNum + usOffset, 1.5,'Padding','HorizontalAlignment','center','FontSize',fs);
    end
end

xticks(ax,[])

% Plot Y ticks for content channels, title and x label
if sigbInfo.NumContentChannels>1
    yticks(ax,[0.5 1.5])
    ax.YTickLabel = {'Content channel #1','Content channel #2'};
    offset = 0;
else
    yticks(ax,0.5)
    ax.YTickLabel = {'Content channel #1'};
    offset = -1;
end
if ~sigbInfo.Compression
    title(ax,'HE-SIG-B Allocation Signaling');
else
    title(ax,'HE-SIG-B Allocation Signaling with Compression (No Common Field)');
end
xlabel(ax,'Bit allocation in time');

if ~sigbInfo.Compression
    % Arrow and text annotation for common field
    haCommon = annotation('doublearrow');
    haCommon.Parent = ax;
    haCommon.X = [0 4];
    haCommon.Y = [2.1 2.1]+offset;
    text(ax,2,2.25+offset,'Common field','HorizontalAlignment','center')
end

% Arrow for and text annotation for user specific field
haUS = annotation('doublearrow');
haUS.Parent = ax;
cx = xlim(ax);
haUS.X = [usOffset cx(2)];
haUS.Y = [2.1 2.1]+offset;
text(ax,cx(2)/2+usOffset/2,2.25+offset,'User specific field','HorizontalAlignment','center')

% Resize to make it easier to read
if nargin==1 && resize
    pos = get(gcf, 'Position');
    if numCFRows>=6 
        yScaleFactor = 1.2;
    elseif numCFRows>3 
        yScaleFactor = 1.5;
    else
        yScaleFactor = 2;
    end
    set(gcf, 'Position', [pos(1) pos(2) pos(3)*2 pos(4)/yScaleFactor]);
end

ylim(ax,[0 2.5+offset])
hold(ax,'off');

end

function ruDetailsTxt = getRUDetailsTxt(ruIndices, ruSizes, ruNumber)
    % Return a cell array of RU index-size
    [uniqueRUDetails,ai] = unique([ruIndices; ruSizes].','rows','stable');
    uniqueRUDetails = uniqueRUDetails.';
    uniqueRUNumbers = ruNumber(ai);
    
    % The number of RUs per row depends of the number of user fields.
    % Reduce the number when more users
    if numel(ruNumber)>=8
        numRUPerRow = 2;
    else
        numRUPerRow = 3;
    end
    % Create a cell array with an element per row
    numRows = ceil(size(uniqueRUDetails,2)/numRUPerRow);
    ruDetailsTxt = cell(numRows,1);
    
    for i = 1:size(uniqueRUDetails,2)
        % Plot on the correct row
        j = floor((i-0.5)/numRUPerRow)+1;

        % Add comma separation on rows
        if mod(i,numRUPerRow)==1% any(i==[1 numRUPerRow+1 2*numRUPerRow+1])
           ruDetailsTxt{j} = '';
        else
           ruDetailsTxt{j} = [ruDetailsTxt{j} ', '];
        end

        ruDetailsTxt{j} = [ruDetailsTxt{j},sprintf('RU #%d (RU%d-%d)',uniqueRUNumbers(i),uniqueRUDetails(1,i),uniqueRUDetails(2,i))];
    end
end