function [R, G, B] = hdlhsv2rgb(H, S, V)
%HDLHSV2RGB Convert hue-saturation-value colors to red-green-blue.
%   [R, G, B] = HDLHSV2RGB(H, S, V) converts the HSV image HSV to the
%   equivalent RGB image RGB.
%
%   As the hue varies from 0 to 1, the resulting color varies from
%   red, through yellow, green, cyan, blue and magenta, back to red.
%   When the saturation is 0, the colors are unsaturated; they are
%   simply shades of gray.  When the saturation is 1, the colors are
%   fully saturated; they contain no white component.  As the value
%   varies from 0 to 1, the brightness increases.
%
%   CLASS SUPPORT
%   -------------
%   The input can be of class uint8, uint16, half, single, or double. The
%   output has the same class as the input.
%
%   See also HDLRGB2HSV, COLORMAP, RGBPLOT.

%   Copyright 2023 The MathWorks, Inc.

%#codegen

% --- Input Validation ----------------------------------------------------
funcName = 'hdlhsv2rgb';
validateattributes(H, ...
    {'single', 'double', 'uint8', 'uint16', 'embedded.fi'}, ... % Class
    {'2d', 'nonempty', 'nonsparse', 'finite', 'nonnan', 'real'}, ... % Attributes
    funcName, 'H', 1); % funcName, argName, argIdx

validateattributes(S, ...
    {'single', 'double', 'uint8', 'uint16', 'embedded.fi'}, ... % Class
    {'2d', 'nonempty', 'nonsparse', 'finite', 'nonnan', 'real'}, ... % Attributes
    funcName, 'S', 2); % funcName, argName, argIdx

validateattributes(V, ...
    {'single', 'double', 'uint8', 'uint16', 'embedded.fi'}, ... % Class
    {'2d', 'nonempty', 'nonsparse', 'finite', 'nonnan', 'real'}, ... % Attributes
    funcName, 'V', 3); % funcName, argName, argIdx

% No doubles for HDL
if coder.target('hdl')
    coder.internal.assert(~isa(H, 'double') && ~isa(S, 'double') && ~isa(V, 'double'),'visionhdl:hdlhsv2rgb:DoubleType', funcName);
end

if ~coder.target('hdl')
    % Consistent data type across all inputs
    coder.internal.errorIf( ~isa(S ,class(H)) || ~isa(V, class(H)), 'visionhdl:hdlhsv2rgb:InputTypes', funcName);

    % Fixed-point Types
    if isfi(H)
        % Consistent word and fraction length across all inputs
        coder.internal.errorIf( (H.WordLength ~= S.WordLength) || (H.WordLength ~= V.WordLength),                 'visionhdl:hdlhsv2rgb:InputTypes', funcName);
        coder.internal.errorIf( (H.FractionLength ~= S.FractionLength) || (H.FractionLength ~= V.FractionLength), 'visionhdl:hdlhsv2rgb:InputTypes', funcName);

        % Sign, WordLength, FractionLength
        coder.internal.errorIf( issigned(H) || issigned(S) || issigned(V),                         'visionhdl:hdlhsv2rgb:SignedType',     funcName);
        coder.internal.errorIf( (H.WordLength < 8) || (H.WordLength > 16),                         'visionhdl:hdlhsv2rgb:WordLength',     funcName, '8', '16');
        coder.internal.errorIf( (H.FractionLength ~= 0) && (H.FractionLength ~= H.WordLength - 1), 'visionhdl:hdlhsv2rgb:FractionLength', funcName);
    end
end

% --- Setup ---------------------------------------------------------------
if isfloat(H)
    FloatLimits = cast([0, 1], 'like', H);
end

if isfloat(H) || (isfi(H) && H.FractionLength ~= 0)
    NormaliseFlag = false;
    if isfloat(H)
        DataTypes.h6Type    = cast(1, 'like', H);
        DataTypes.hFracType = cast(1, 'like', H);
        DataTypes.svType    = cast(1, 'like', H);
        DataTypes.multType  = cast(1, 'like', H);
        DataTypes.outType   = cast(1, 'like', H);
    else
        DataTypes.h6Type    = fi(0, 0,   H.WordLength+3,  H.FractionLength);
        DataTypes.hFracType = fi(0, 0,   H.WordLength,    H.FractionLength);
        DataTypes.svType    = fi(0, 0,   H.WordLength,    H.FractionLength);
        DataTypes.multType  = fi(0, 0, 2*H.WordLength,  2*H.FractionLength);
        DataTypes.outType   = fi(0, 0, 3*H.WordLength,  3*H.FractionLength);
    end
else
    if isa(H, 'uint8')
        inType.WordLength = 8;
    elseif isa(H, 'uint16')
        inType.WordLength = 16;
    else
        inType.WordLength = H.WordLength;
    end
    NormaliseFlag = true;

    DataTypes.h6Type    = fi(0, 0,   inType.WordLength+3,   inType.WordLength);
    DataTypes.hFracType = fi(0, 0,   inType.WordLength,     inType.WordLength);
    DataTypes.svType    = fi(0, 0,   inType.WordLength+1,   inType.WordLength);
    DataTypes.multType  = fi(0, 0, 2*inType.WordLength+1, 2*inType.WordLength);
    DataTypes.outType   = fi(0, 0, 3*inType.WordLength+1, 3*inType.WordLength);
end

% --- Conversion ----------------------------------------------------------
% Input pipeline
h = coder.hdl.pipeline(H, 1);
s = coder.hdl.pipeline(S, 1);
v = coder.hdl.pipeline(V, 1);

% Saturate floating-point inputs to [0, 1]
if isfloat(H)
    hIn = coder.hdl.pipeline(max(FloatLimits(1), coder.hdl.pipeline(min(h, FloatLimits(2)), 1)), 1);
    sIn = coder.hdl.pipeline(max(FloatLimits(1), coder.hdl.pipeline(min(s, FloatLimits(2)), 1)), 1);
    vIn = coder.hdl.pipeline(max(FloatLimits(1), coder.hdl.pipeline(min(v, FloatLimits(2)), 1)), 1);
elseif NormaliseFlag
    hIn = coder.hdl.pipeline(cast(coder.hdl.pipeline((h * fi((1 / (2^inType.WordLength-1)),0,2*inType.WordLength,2*inType.WordLength)), 1), 'like', DataTypes.svType), 1);
    sIn = coder.hdl.pipeline(cast(coder.hdl.pipeline((s * fi((1 / (2^inType.WordLength-1)),0,2*inType.WordLength,2*inType.WordLength)), 1), 'like', DataTypes.svType), 1);
    vIn = coder.hdl.pipeline(cast(coder.hdl.pipeline((v * fi((1 / (2^inType.WordLength-1)),0,2*inType.WordLength,2*inType.WordLength)), 1), 'like', DataTypes.svType), 1);
else
    hIn = h;
    sIn = s;
    vIn = v;
end

[r, g, b] = hdl.npufun(@hsv2rgbKernel, [1, 1], hIn, sIn, vIn, 'KernelArg', DataTypes);

% Saturate floating-point output to [0, 1]
if isfloat(H)
    R = coder.hdl.pipeline(max(FloatLimits(1), coder.hdl.pipeline(min(r, FloatLimits(2)), 1)), 1);
    G = coder.hdl.pipeline(max(FloatLimits(1), coder.hdl.pipeline(min(g, FloatLimits(2)), 1)), 1);
    B = coder.hdl.pipeline(max(FloatLimits(1), coder.hdl.pipeline(min(b, FloatLimits(2)), 1)), 1);
elseif NormaliseFlag
    R = coder.hdl.pipeline(cast(coder.hdl.pipeline((r * fi(2^inType.WordLength-1, 0, inType.WordLength, 0)), 1), 'like', H), 1);
    G = coder.hdl.pipeline(cast(coder.hdl.pipeline((g * fi(2^inType.WordLength-1, 0, inType.WordLength, 0)), 1), 'like', H), 1);
    B = coder.hdl.pipeline(cast(coder.hdl.pipeline((b * fi(2^inType.WordLength-1, 0, inType.WordLength, 0)), 1), 'like', H), 1);
else
    R = r;
    G = g;
    B = b;
end

end

% --- NPUFun --------------------------------------------------------------
function [R, G, B] = hsv2rgbKernel(H, S, V, DataTypes)

h = coder.hdl.pipeline(H, 1);
s = coder.hdl.pipeline(S, 1);
v = coder.hdl.pipeline(V, 1);

h6  = coder.hdl.pipeline(cast(h * 6, 'like', DataTypes.h6Type), 1); % Determine Hue arc
sFi = coder.hdl.pipeline(cast(s,     'like', DataTypes.svType), 1);
vFi = coder.hdl.pipeline(cast(v,     'like', DataTypes.svType), 1);

hInt  = coder.hdl.pipeline(floor(h6), 1);
hFrac = coder.hdl.pipeline(cast(h6 - hInt, 'like', DataTypes.hFracType), 1);

p = coder.hdl.pipeline((hFrac .* sFi), 1);

% Piecewise elements for RGB
alpha = coder.hdl.pipeline(cast(1 - sFi,   'like', DataTypes.svType),   1);
beta  = coder.hdl.pipeline(cast(1 - p,     'like', DataTypes.multType), 1);
gamma = coder.hdl.pipeline(cast(alpha + p, 'like', DataTypes.multType), 1);

% Determine base RGB based on Hue arc
switch hInt
    case {0, 6}
        RFi = cast(1,     'like', DataTypes.multType);
        GFi = cast(gamma, 'like', DataTypes.multType);
        BFi = cast(alpha, 'like', DataTypes.multType);
    case 1
        RFi = cast(beta,  'like', DataTypes.multType);
        GFi = cast(1,     'like', DataTypes.multType);
        BFi = cast(alpha, 'like', DataTypes.multType);
    case 2
        RFi = cast(alpha, 'like', DataTypes.multType);
        GFi = cast(1,     'like', DataTypes.multType);
        BFi = cast(gamma, 'like', DataTypes.multType);
    case 3
        RFi = cast(alpha, 'like', DataTypes.multType);
        GFi = cast(beta,  'like', DataTypes.multType);
        BFi = cast(1,     'like', DataTypes.multType);
    case 4
        RFi = cast(gamma, 'like', DataTypes.multType);
        GFi = cast(alpha, 'like', DataTypes.multType);
        BFi = cast(1,     'like', DataTypes.multType);
    otherwise
        RFi = cast(1,     'like', DataTypes.multType);
        GFi = cast(alpha, 'like', DataTypes.multType);
        BFi = cast(beta,  'like', DataTypes.multType);
end
RFi = coder.hdl.pipeline(RFi, 1);
GFi = coder.hdl.pipeline(GFi, 1);
BFi = coder.hdl.pipeline(BFi, 1);

% RGB .* V
R = coder.hdl.pipeline(cast(coder.hdl.pipeline(RFi .* vFi, 1), 'like', DataTypes.outType), 1);
G = coder.hdl.pipeline(cast(coder.hdl.pipeline(GFi .* vFi, 1), 'like', DataTypes.outType), 1);
B = coder.hdl.pipeline(cast(coder.hdl.pipeline(BFi .* vFi, 1), 'like', DataTypes.outType), 1);

end