function cfg = helperBLEDecodeAttributeValue(attributeValue, attributeType)
%helperBLEDecodeAttributeValue BLE attribute value decoder
%   CFGATTVALUE = helperBLEDecodeAttributeValue(ATTRIBUTEVALUE,
%   ATTRIBUTETYPE) decodes the BLE (Bluetooth Low Energy) attribute value
%   and returns the corresponding configuration, CFGATTVALUE.
%
%   CFGATTVALUE represents the decoded attribute value configuration,
%   returned as an object of type <a
%   href="matlab:help('helperBLEAttributeValueConfig')">helperBLEAttributeValueConfig</a>.
%
%   ATTRIBUTEVALUE represents the BLE attribute value, specified as one of
%   the following types:
%       - A character vector representing octets in hexadecimal format.
%       - A string scalar representing octets in hexadecimal format.
%       - A numeric vector representing octets in decimal format, where
%         each element is in between 0 and 255 inclusive.
%       - An n-by-2 character array, where each row represents an octet in
%         hexadecimal format.
%
%   ATTRIBUTETYPE is one of the following values in <a
%   href="matlab:help('helperBluetoothID.BluetoothNameValues')">BluetoothNameValues</a>.
%
%   See also helperBLEAttributeValueConfig,
%   helperBLEGenerateAttributeValue.

%   Copyright 2018 The MathWorks, Inc.

% Initialize
cfg = helperBLEAttributeValueConfig('AttributeType', attributeType);
octetLen = 8;
cnt = 1;

% Validate input arguments
attributeValue = ble.internal.convertPayloadToHexArray(attributeValue);

if(~any(strcmpi(attributeType, cfg.AttributeTypeValues)))
    error('Received attribute type is not supported.');
end

switch attributeType
    % Primary service
    case 'Primary service'
        % Service ID (2 octets)
        serviceID = attributeValue(cnt : cnt+1, :);
        cfg.ServiceID = reshape(serviceID(end:-1:1, :)', 1, []);
        
    % Characteristic
    case 'Characteristic'
        % Characteristic properties bitmap (1 octet)
        properties = de2bi(hex2dec(attributeValue(cnt, :)), octetLen);
        cnt = cnt + 1;
        
        cfg = decodeCharacteristicProperties(cfg, properties);
        
        % Characteristic value handle (2 octets)
        characteristicValueHandle = attributeValue(cnt : cnt+1, :);
        cfg.CharacteristicValueHandle = reshape(...
            characteristicValueHandle(end:-1:1, :)', 1, []);
        cnt = cnt + 2;
        
        % Characteristic ID (2 octets)
        type = attributeValue(cnt : cnt+1, :);
        cfg.CharacteristicType = helperBluetoothID.getBluetoothName(...
            reshape(type(end:-1:1, :)', 1, []));
        
    % Client characteristic configuration descriptor
    case 'Client characteristic configuration descriptor'
        % CCCD (Client Characteristic Configuration Descriptor) properties
        % (2 octets)
        % First two bits are used, rest of the 14 bits are reserved for
        % future use
        octets = attributeValue(cnt : cnt+1, :);
        properties = de2bi(hex2dec(reshape(octets(end:-1:1, :)', 1, [])), ...
            2*octetLen);
        
        % Notify bit in characteristic configuration (bit-0)
        if properties(1)
            cfg.NotifyFlag = 'True';
        else
            cfg.NotifyFlag = 'False';
        end
        % Indicate bit in characteristic configuration (bit-1)
        if properties(2)
            cfg.IndicateFlag = 'True';
        else
            cfg.IndicateFlag = 'False';
        end
        
    % Heart rate measurement
    case 'Heart rate measurement'
        % Heart rate measurement characteristic flag (1 octet)
        % 6 to 8 bits are reserved for future use
        flagBits = de2bi(hex2dec(attributeValue(cnt, :)), octetLen);
        cnt = cnt + 1;
        
        % Heart rate value format (1 bit)
        if flagBits(1)
            cfg.HeartRateValueFormat = 'UINT16';
            % Heart rate measurement value (2 octets)
            heartRate = attributeValue(cnt : cnt+1, :);
            cfg.HeartRateValue = hex2dec(reshape(heartRate(end:-1:1, :)', 1, []));
            cnt = cnt + 2;
        else
            cfg.HeartRateValueFormat = 'UINT8';
            % Heart rate measurement value (1 octet)
            cfg.HeartRateValue = hex2dec(attributeValue(cnt, :));
            cnt = cnt + 1;
        end
        
        % Sensor contact status (2 bits)
        if (all(flagBits(2:3) == [0 0]))
            cfg.SensorContactStatus = 'Feature is not supported';
        elseif (all(flagBits(2:3) == [1 1]))
            cfg.SensorContactStatus = 'Contact detected';
        elseif (all(flagBits(2:3) == [1 0]))
            cfg.SensorContactStatus = 'No contact detected';
        else
            cfg.SensorContactStatus = 'Feature is not supported';
        end
        
        % Energy expended field status (1 bit)
        if flagBits(4)
            cfg.EnergyExpendedFieldFlag = 'Present, Units: Kilo Joules';
            % Energy expended value (2 octets)
            energyExpanded = attributeValue(cnt : cnt+1, :);
            cfg.EnergyExpended = hex2dec(reshape(...
                energyExpanded(end:-1:1, :)', 1, []));
            cnt = cnt + 2;
        else
            cfg.EnergyExpendedFieldFlag = 'Not present';
            cfg.EnergyExpended = 0;
        end
        
        % RR interval field status (1 bit)
        if flagBits(5)
            cfg.RRIntervalFieldFlag = 'Present';
            % RR interval value (2 octets)
            rrInterval = attributeValue(cnt : cnt+1, :);
            cfg.RRInterval = hex2dec(reshape(...
                rrInterval(end:-1:1, :)', 1, []));
        else
            cfg.RRIntervalFieldFlag = 'Not present';
            cfg.RRInterval = 0;
        end
        
    % Body sensor location
    case 'Body sensor location'
        % Body sensor location (1 octet)
        cfg.BodySensorLocation = decodeBodySensorLocation(...
            hex2dec(attributeValue(cnt, :)));
end
end


function cfg = decodeCharacteristicProperties(cfg, properties)
% Broadcast flag (bit-0)
if properties(1)
    cfg.BroadcastFlag = 'True';
else
    cfg.BroadcastFlag = 'False';
end

% Read flag (bit-1)
if properties(2)
    cfg.ReadFlag = 'True';
else
    cfg.ReadFlag = 'False';
end

% Write without response flag (bit-2)
if properties(3)
    cfg.WriteWithoutResponseFlag = 'True';
else
    cfg.WriteWithoutResponseFlag = 'False';
end

% Write flag (bit-3)
if properties(4)
    cfg.WriteFlag = 'True';
else
    cfg.WriteFlag = 'False';
end

% Notify flag (bit-4)
if properties(5)
    cfg.NotifyFlag = 'True';
else
    cfg.NotifyFlag = 'False';
end

% Indicate flag (bit-5)
if properties(6)
    cfg.IndicateFlag = 'True';
else
    cfg.IndicateFlag = 'False';
end

% Authenticated signed writes flag (bit-6)
if properties(7)
    cfg.AuthenticatedSignedWritesFlag = 'True';
else
    cfg.AuthenticatedSignedWritesFlag = 'False';
end

% Extended properties flag (bit-7)
if properties(8)
    cfg.ExtendedPropertiesFlag = 'True';
else
    cfg.ExtendedPropertiesFlag = 'False';
end
end


function location = decodeBodySensorLocation(value)
location = '';
% Decode location based on value
switch value
    case 1
        location = 'Chest';
    case 2
        location = 'Wrist';
    case 3
        location = 'Finger';
    case 4
        location = 'Hand';
    case 5
        location = 'Ear Lobe';
    case 6
        location = 'Foot';
    case 0
        location = 'Other';
end
end
