function [cfg, varargout] = NETFrameDecoder(netFrame)
%NETFRAMEDECODER Decode ZigBee network-layer frames
%   CFG = NETFRAMEDECODER(NETFRAME) decodes the NET frame NETFRAME
%   and outputs all decoded information to the zigbee.NETFrameConfig object CFG.
%
%   [CFG, PAYLOAD] = NETFRAMEDECODER(NETFRAME) is the same as the
%   syntax above, except that it additionally outputs the NET frame
%   payload. This syntax is allowed only for data frame types.
%
%   See also zigbee.NETFrameConfig, zigbee.NETFrameGenerator

%   Copyright 2017-2023 The MathWorks, Inc.

% convert bytes to bits
netFrameBin = zigbee.internal.bytes2bits(netFrame);

% 0. Initialize:
cfg = zigbee.NETFrameConfig();

% 1. Frame Control (first two octets)
frameControl = netFrameBin(1 : 2*8);
cfg = decodeFrameControl(cfg, frameControl);

% 2. Destination address (next two octets)
cfg.DestinationAddress = dec2hex(bit2int(netFrameBin(2*8+1 : 4*8)', 16, false), 4);

% 3. Source address (next two octets)
cfg.SourceAddress = dec2hex(bit2int(netFrameBin(4*8+1 : 6*8)', 16, false), 4);

% 4. Radius (next 1 octet)
cfg.Radius = bit2int(netFrameBin(6*8+1 : 7*8)', 8, false);

% 5. Sequence number (next 1 octet)
cfg.SequenceNumber = bit2int(netFrameBin(7*8+1 : 8*8)', 8, false);

cnt = 8*8;

% 6. Destination IEEE address (absent or next 8 octets)
if any(strcmp(cfg.IEEEAddressing, {'Both', 'Destination'}))
  cfg.DestinationIEEEAddress(1:16)  = dec2hex(bit2int(uint8(netFrameBin( ...
      cnt+1 : cnt + 8*8)'), 8*8, false), 16);
  cnt = cnt + 8*8;
end

% 7. Source IEEE address (absent or next 8 octets)
if any(strcmp(cfg.IEEEAddressing, {'Both', 'Source'}))
  cfg.SourceIEEEAddress(1:16) = dec2hex(bit2int(uint8(netFrameBin( ...
      cnt+1 : cnt + 8*8)'), 8*8, false), 16);
  cnt = cnt + 8*8;
end

% 8. Multicast control (absent or next octet)
if cfg.Multicast
  multicastControl = netFrameBin(cnt+1:cnt+8);
  cfg = decodeMulticastControl(cfg, multicastControl);
  cnt = cnt + 1*8;
end

% 9. Source control subframe (variable length)
if cfg.SourceRouting
  [cfg, cnt] = decodeSourceRouteSubframe(cfg, netFrameBin, cnt);
end

% 10. Security auxiliary header
if cfg.Security
  [cfg, cnt] = zigbee.internal.decodeSecurityHeader(cfg, netFrameBin, cnt);
end

% 11. Frame payload
if strcmp(cfg.FrameType, 'NET command')
  % 11.1 Command type
  cfg = decodeCommandID(cfg, netFrameBin(cnt+1:cnt+8));
  cnt = cnt + 8;
end
% 11.2 Payload
varargout{1} = netFrame(cnt/8+1:end, :);


function cfg = decodeFrameControl(cfg, frameControl)

% 1. Frame type
switch bit2int(frameControl(1:2)', 2, false)
  case 0
    cfg.FrameType = 'Data';  
  case 1
    cfg.FrameType = 'NET command';
  otherwise
    warning(message('lrwpan:ZigBee:InvalidNETFrameType'));
end

% 2. Protocol version
protocolVersion = bit2int(frameControl(3:6)', 4, false);
switch protocolVersion 
  case 1
    cfg.ProtocolVersion = 'ZigBee 2004';  
  case 2
    cfg.ProtocolVersion = 'ZigBee 2007';
  otherwise
    warning(message('lrwpan:ZigBee:InvalidProtocolVersion', protocolVersion));
end

% 3. Discover route
switch bit2int(frameControl(7:8)', 2, false)
  case 0
    cfg.DiscoverRoute = false;
  case 1
    cfg.DiscoverRoute = true;
  otherwise
    warning(message('lrwpan:ZigBee:InvalidDiscoverRoute'));
end

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

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

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

% 7. IEEE addressing
switch bit2int(frameControl(12:13)', 2, false)
  case 0
    cfg.IEEEAddressing = 'None';
  case 1
    cfg.IEEEAddressing = 'Destination';
  case 2
    cfg.IEEEAddressing = 'Source';
  otherwise
    cfg.IEEEAddressing = 'Both';
end

if any(frameControl(14:16))
  warning(message('lrwpan:ZigBee:InvalidReservedBits'));
end


function cfg = decodeMulticastControl(cfg, multicastControl)

switch bit2int(multicastControl(1:2)', 2, false)
  case 0
    cfg.MulticastMode = 'Non-member';  
  case 1
    cfg.MulticastMode = 'Member';  
  otherwise
    warning(message('lrwpan:ZigBee:InvalidMulticastMode'));
end
cfg.NonMemberRadius = bit2int(multicastControl(3:5)', 3, false);
cfg.MaxNonMemberRadius = bit2int(multicastControl(6:8)', 3, false);



function [cfg, cnt] = decodeSourceRouteSubframe(cfg, netFrame, cnt)
  
% 1. Relay count
relayCount = bit2int(netFrame(cnt+1 : cnt+8)',8,false);

cnt = cnt+8;

% 2. Relay index
cfg.RelayIndex = bit2int(netFrame(cnt+1 : cnt+8)',8,false);
cnt = cnt+8;

% 3. Relay list
relayList = repmat(' ', relayCount, 4);
for idx = 1:relayCount
  relayList(idx, :) = dec2hex(bit2int(netFrame(cnt+1:cnt+2*8)', 2*8, false), 4);
  cnt = cnt + 2*8;
end
cfg.RelayList = relayList; 



function cfg = decodeCommandID(cfg, bits)

ID = bit2int(bits(:), length(bits), false);
switch ID
  case 1
    cfg.CommandType = 'Route request';
  case 2
    cfg.CommandType = 'Route reply';
	case 3
    cfg.CommandType = 'Network status';
  case 4
    cfg.CommandType = 'Leave';
  case 5
    cfg.CommandType = 'Route record';
	case 6
    cfg.CommandType = 'Rejoin request';
	case 7
    cfg.CommandType = 'Rejoin response';
  case 8
    cfg.CommandType = 'Link status';
	case 9
    cfg.CommandType = 'Network report';
  case 10
    cfg.CommandType = 'Network update';
  otherwise
    warning(message('lrwpan:ZigBee:InvalidNETCommand', ID));
end
