function [cfg, zclPayload] = ZCLFrameDecoder(apsPayload, varargin)
%ZCLFrameDecoder Decode ZigBee Cluster Library frames
%   CFG = ZCLFRAMEDECODER(APSPAYLOAD) decodes the ZigBee Cluster Library
%   frame APSPAYLOAD and outputs the corresponding configuration to the
%   object CFG.
%
%   [CFG, PAYLOAD] = ZCLFRAMEDECODER(APSPAYLOAD) is the same as the syntax
%   above, except that it additionally outputs the cluster frame payload.
%
%   See also zigbee.ZCLFrameConfig, zigbee.ZCLFrameGenerator.

%   Copyright 2017-2023 The MathWorks, Inc.

  % convert bytes to bits
  apsPayloadBin = zigbee.internal.bytes2bits(apsPayload);

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

  % 1. Frame Control (first octet)
  frameControl = apsPayloadBin(1:8);
  cfg = decodeFrameControl(cfg, frameControl);

  % 2. ManufacturerCode (2 octets)
  if cfg.ManufacturerCommand    
    cfg.ManufacturerCode = bit2int(apsPayloadBin(9:24)', 16, false);
    offset = 3*8 + 1;
  else
    offset = 8 + 1;
  end

  % 3. SequenceNumber 8-bits
  cfg.SequenceNumber = bit2int(apsPayloadBin(offset:offset+7)', 8, false);
  offset = offset+8;

  % 4. CommandID 8-bits
  commandID = bit2int(apsPayloadBin(offset:offset+7)', 8, false);
  cfg.CommandType = zigbee.commandName(commandID, cfg.FrameType, varargin{1}, cfg.Direction);
  offset = offset+8;

  % 5. Frame payload
  if offset <= length(apsPayloadBin)
    zclPayload = apsPayload((offset-1)/8+1:end, :);
  else
    zclPayload = [];
  end

end


function cfg = decodeFrameControl(cfg, frameControl)
  
  % 1. FrameType (2 bits)
  if isequal(frameControl(1:2), [0 0])
      cfg.FrameType = 'Library-wide';
  elseif isequal(frameControl(1:2), [1 0])
      cfg.FrameType = 'Cluster-specific';
  end

  % 2. ManufacturerSpecific (1-bit)
  cfg.ManufacturerCommand = logical(frameControl(3));

  % 3. Direction (1-bit)
  if frameControl(4)
    cfg.Direction = 'Downlink';
  else
    cfg.Direction = 'Uplink';
  end

  % 4. DisableDefaultResponse (1-bit)
  cfg.DisableDefaultResponse = logical(frameControl(5));

  % bits 6-8 are reserved
end
