function value = helperBLEGenerateAttributeValue(cfgATTValue)
%helperBLEGenerateAttributeValue BLE attribute value generation
%   VALUE = helperBLEGenerateAttributeValue(CFGATTVALUE) generates a BLE
%   (Bluetooth Low Energy) attribute value corresponding to the
%   configuration object CFGATTVALUE defined by Bluetooth SIG (Special
%   Interest Group).
%
%   VALUE represents the output BLE attribute value, returned as a
%   character array where each row is the hexadecimal representation of an
%   octet.
%
%   CFGATTVALUE is an object of type <a
%   href="matlab:help('helperBLEAttributeValueConfig')">helperBLEAttributeValueConfig</a>
%   used to generate an ATT PDU.
%
%   See also helperBLEAttributeValueConfig, helperBLEDecodeAttributeValue.

%   Copyright 2018 The MathWorks, Inc.

% Validate input arguments
validateattributes(cfgATTValue, {'helperBLEAttributeValueConfig'}, ...
    {'scalar'}, mfilename, 'cfgATTValue', 1);

% Initialize
octetLen = 8;
value = '';

switch cfgATTValue.AttributeType
    % Primary service
    case 'Primary service'
        % Service ID (2 octets)
        value = convertToOctets(cfgATTValue.ServiceID);
        
    % Characteristic
    case 'Characteristic'
        % Characteristic properties bitmap (1 octet)
        properties = dec2hex(bi2de(generateCharacteristicProperties(cfgATTValue)), 2);
        
        % Characteristic value handle (2 octets)
        valueHandle = convertToOctets(cfgATTValue.CharacteristicValueHandle);
        
        % Characteristic ID (2 octets)
        characteristicID = convertToOctets(...
            helperBluetoothID.getBluetoothID(cfgATTValue.CharacteristicType));
        
        % Put it all together
        value = [properties; valueHandle; characteristicID];
        
    % 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
        value = zeros(1, 2*octetLen);
        
        % Whether the characteristic value notifications are enabled or
        % not (bit-0)
        if strcmp(cfgATTValue.NotifyFlag, 'True')
            value(1) = 1;
        else
            value(1) = 0;
        end
        
        % Whether the characteristic value indications are enabled or
        % not (bit-1)
        if strcmp(cfgATTValue.IndicateFlag, 'True')
            value(2) = 1;
        else
            value(2) = 0;
        end
        
        value = convertToOctets(dec2hex(bi2de(value), 4));
        
    % Heart rate measurement
    case 'Heart rate measurement'
        % Heart rate measurement flags bitmap (1 octet)
        % 6 to 8 bits are reserved for future use
        flags = zeros(1, octetLen);
        
        % Heart rate value format (1-bit)
        if strcmp(cfgATTValue.HeartRateValueFormat, 'UINT16')
            flags(1) = 1;
            % Heart rate measurement value (2 octets)
            heartRateValue = convertToOctets(dec2hex(cfgATTValue.HeartRateValue, 4));
        else
            flags(1) = 0;
            % Heart rate measurement value (1 octet)
            heartRateValue = dec2hex(cfgATTValue.HeartRateValue, 2);
        end
        
        % Sensor contact status (2-bits)
        if strcmp(cfgATTValue.SensorContactStatus, 'Feature is not supported')
            flags(2:3) = [0 0];
        elseif strcmp(cfgATTValue.SensorContactStatus, 'Contact detected')
            flags(2:3) = [1 1];
        else
            flags(2:3) = [1 0];
        end
        
        % Energy expended status (1-bit)
        if strcmp(cfgATTValue.EnergyExpendedFieldFlag, 'Not present')
            flags(4) = 0;
            energyExpended = '';
        else
            flags(4) = 1;
            % Energy expended value (2 octets)
            energyExpended = convertToOctets(dec2hex(cfgATTValue.EnergyExpended, 4));
        end
        
        % RR interval field status (1-bit)
        if strcmp(cfgATTValue.RRIntervalFieldFlag, 'Not present')
            flags(5) = 0;
            rrInterval = '';
        else
            flags(5) = 1;
            % RR interval value (2 octets)
            rrInterval = convertToOctets(dec2hex(cfgATTValue.RRInterval, 4));
        end
        
        flagsByte = dec2hex(bi2de(flags), 2);
        
        % Put it all together
        value = [flagsByte; heartRateValue; energyExpended; rrInterval];
        
    % Body sensor location
    case 'Body sensor location'
        % Body sensor location (1 octet)
        value = dec2hex(generateBodySensorLocation(cfgATTValue.BodySensorLocation), 2);
end
end


function properties = generateCharacteristicProperties(cfg)
% Octet length in bits
octetLen = 8;

% Characteristic properties bitmap (1 octet)
properties = zeros(1, octetLen);

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

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

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

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

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

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

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

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


function value = generateBodySensorLocation(location)
% Generate value based on location

value = [];
switch location
    case 'Chest'
        value = 1;
    case 'Wrist'
        value = 2;
    case 'Finger'
        value = 3;
    case 'Hand'
        value = 4;
    case 'Ear Lobe'
        value = 5;
    case 'Foot'
        value = 6;
    case 'Other'
        value = 0;
end
end


function octets = convertToOctets(value)
% LSB first
octets = reshape(value, 2, [])';
octets = octets(end:-1:1, :);
end
