function netFrame = NETFrameGenerator(cfg, payload)
%NETFRAMEGENERATOR Generate ZigBee NET frames
%   NETFRAME = NETFRAMEGENERATOR(CFG) generates the network-layer frame
%   NETFRAME corresponding to the zigbee.NETFrameConfiguration object CFG
%   and containing the payload PAYLOAD.
%
%   See also zigbee.NETFrameConfig, zigbee.NETFrameDecoder

%   Copyright 2017-2023 The MathWorks, Inc.

% 1. Frame Control field
frameControl = generateFrameControl(cfg);

% 2, 3. Source and destination addresses
addresses = [ int2bit(hex2dec(cfg.DestinationAddress),  2*8, false)' ...
              int2bit(hex2dec(cfg.SourceAddress),       2*8, false)' ];

% 4. Radius (1 octet)
radius = int2bit(cfg.Radius, 8, false)';

% 5. Sequence number
seqNo = int2bit(cfg.SequenceNumber, 8, false)';

% 6, 7. IEEE Source and destination addresses
IEEEaddresses = generateIEEEAddresses(cfg);

% 8. Multicast control field
multicastControl = generateMulticastControl(cfg);

% 9. Source route subframe
sourceRouteSubframe = generateSourceRouteSubframe(cfg);

% 10. Auxiliary (security) header
securityHeader = zigbee.internal.generateSecurityHeader(cfg);
if cfg.Security
  error(message('lrwpan:ZigBee:SecurityNotSupported'));
end

% 11. Put everything together, including payload
netFrame = [frameControl addresses radius seqNo IEEEaddresses ...
            multicastControl sourceRouteSubframe securityHeader];

netFrameBytes = zigbee.internal.bits2bytes(netFrame);
netFrame = [netFrameBytes; payload];

function frameControl = generateFrameControl(cfg)

% 0. Initialize (two octets)
frameControl = zeros(1, 2*8);

% 1. Frame type
if ~strcmp(cfg.FrameType, 'Data')
  error(message('lrwpan:ZigBee:NETCommandsNotSupported'));
% else Data frame type value is 00
end

% 2. Protocol version
if strcmp(cfg.ProtocolVersion, 'ZigBee 2007')
  frameControl(3:4) = [0 1]; % version = 2
elseif strcmp(cfg.ProtocolVersion, 'ZigBee 2004')
  frameControl(3:4) = [1 0]; % version = 1
end

% 3. Discover route
frameControl(7) = double(cfg.DiscoverRoute);

% 4. Multicast
frameControl(9) = double(cfg.Multicast);

% 5. Security
frameControl(10) = double(cfg.Security);

% 6. Source routing
frameControl(11) = double(cfg.SourceRouting);

% 7. IEEE addressing
if any(strcmp(cfg.IEEEAddressing, {'Both', 'Destination'}))
  frameControl(12) = 1;
end
if any(strcmp(cfg.IEEEAddressing, {'Both', 'Source'}))
  frameControl(13) = 1;
end


function IEEEaddresses = generateIEEEAddresses(cfg)
  
IEEEaddresses = [];
% 1. Destination IEEE Address
if any(strcmp(cfg.IEEEAddressing, {'Both', 'Destination'}))
  % two steps because of 2^53 limit in dec representation
  IEEEaddresses(1    :4*8) = int2bit(hex2dec(cfg.DestinationIEEEAddress(9:end)), 4*8, false);
  IEEEaddresses(4*8+1:8*8) = int2bit(hex2dec(cfg.DestinationIEEEAddress(1:8)),   4*8, false);
end
% 2. Source IEEE Address
if any(strcmp(cfg.IEEEAddressing, {'Both', 'Source'}))
  % two steps because of 2^53 limit in dec representation
  IEEEaddresses = [ IEEEaddresses ...
                    int2bit(hex2dec(cfg.SourceIEEEAddress(9:end)), 4*8, false)' ...
                    int2bit(hex2dec(cfg.SourceIEEEAddress(1:8)),   4*8, false)'];
end


function multicastControl = generateMulticastControl(cfg)

if ~cfg.Multicast
  multicastControl = [];
else
  multicastControl = zeros(1, 8);
  
  % 1. Multicast mode
  if strcmp(cfg.MulticastMode, 'Member')
    multicastControl(1) = 1;
    % else 0 for Non-member
  end
  
  % 2. Nonmember radius
  multicastControl(3:5) = int2bit(cfg.NonMemberRadius, 3, false);
  
  % 3. Maxnonmember radius
  multicastControl(6:8) = int2bit(cfg.MaxNonMemberRadius, 3, false);
end



function sourceRouteSubframe = generateSourceRouteSubframe(cfg)

if ~cfg.SourceRouting
  sourceRouteSubframe = [];
else
  % 1. Relay count
  relayCountDec = size(cfg.RelayList, 1);
  relayCountBin = int2bit(relayCountDec, 8, false)';
  
  % 2. Relay index
  relayIndex = int2bit(cfg.RelayIndex, 8, false)';

  % 3. Relay list
  relayList = zeros(1, relayCountDec*2*8);
  for idx = 1:relayCountDec
    relayList(1+(idx-1)*2*8 : idx*2*8) = int2bit(hex2dec(cfg.RelayList(idx, :)), 2*8, false);
  end
  
  % putting it all together:
  sourceRouteSubframe = [relayCountBin relayIndex relayList];
end