function hfi = fi_impl(varargin)
% MATLAB Code Generation Library function for fi the fixed-point value constructor.
%
%   All the possible function signatures are:
%
%     fi(data)
%     fi(data, s)
%     fi(data, s, w)
%     fi(data, s, w, f)
%     fi(data, s, w, slope, bias)
%     fi(data, s, w, slopeadjustmentfactor, fixedexponent, bias)
%
%     fi(data, T)
%     fi(data, T, F)
%
%     fi(..., property1, value1, ...)
%     fi(property1, value1, ....)
%

% $INCLUDE(DOC) toolbox/eml/lib/fixedpoint/fi.m $
% Copyright 2002-2017 The MathWorks, Inc.
%#codegen

coder.extrinsic('eml_fi_constructor_helper');
coder.extrinsic('sprintf');
coder.extrinsic('getString');
coder.extrinsic('message');

% This MATLAB Coder library function allows and can handle mxArray inputs
eml_allow_mx_inputs;

coder.internal.prefer_const(varargin);

% Always inline
coder.inline('always');

% Check for ambiguous types
if eml_ambiguous_types
    if nargin==0
        hfi = coder.ignoreConst([]);
    elseif isnumeric(varargin{1}) || islogical(varargin{1})
        hfi = varargin{1};
    else
        hfi = coder.ignoreConst(0);
    end
    return;
end

coder.unroll();
for k = 1:nargin
    coder.internal.errorIf(issparse(varargin{k}), ...
                           'fixed:fi:unsupportedSparseInput','fi constructor');
end

% DTO: Get Simulink DTO setting to set fipref accordingly in calls to eml_fi_constructor_helper.
slDTOStr = coder.internal.get_eml_option('FixptDatatypeOverride');
slDTOAppliesToStr = coder.internal.get_eml_option('FixptDatatypeOverrideAppliesTo');

emlInputFimath = eml_fimath;

eml_invariant(nargin==0 || ~isstruct(varargin{1}),'fixed:fi:unsupportedStructFirstInput');
maxWL = getFixedPointWidthLimit();

if nargin > 1
    if isnumerictype(varargin{2})
        T = varargin{2};
        if strcmpi(T.DataType,'boolean') % Call to isboolean(T) errors out in code gen
            eml_invariant(false, 'fixed:coder:unsupportedBooleanNumerictype');
        end
    end
end

% Switch on the number of input arguments.
switch nargin
    case 1 % Only data is given or if fi(a_fi)
        hfi = eml_fi_case1(maxWL,slDTOStr,slDTOAppliesToStr,emlInputFimath,varargin{1});
        
    case 2 % (data,s) or (data,T) or (data,F) or (fi,T) or (fi,F) or (pv pair)
        hfi = eml_fi_case2(maxWL,slDTOStr,slDTOAppliesToStr,emlInputFimath,varargin{1}, varargin{2});
        
    case 3 % (data/fi,s,w) or (data/fi,T,F) or (data/fi,pv pair)
        hfi = eml_fi_case3(maxWL,slDTOStr,slDTOAppliesToStr,emlInputFimath,varargin{1}, varargin{2}, varargin{3});
        
    otherwise
        hfi = fi_helper(maxWL,slDTOStr,slDTOAppliesToStr,emlInputFimath,varargin{:});
end

function hfi = eml_fi_case1(maxWL,slDTOStr,slDTOAppliesToStr,emlInputFimath,v)
eml_allow_mx_inputs;
coder.inline('always');
coder.internal.prefer_const(maxWL);
coder.internal.prefer_const(slDTOStr);
coder.internal.prefer_const(slDTOAppliesToStr);
coder.internal.prefer_const(emlInputFimath);
coder.internal.prefer_const(v);
coder.extrinsic('eml_fi_constructor_helper');
coder.extrinsic('getString');
coder.extrinsic('message');

b = eml_is_const(v);

if isfi(v)
    % fi(v) where v is a fi object
    hfi = v;
elseif isinteger(v) && ~b
    % non-const builtin integer
    [data,datasz]      = local_createConstDataFromInput(v);
    [T,F,val,fiisautoscaled,pvpairsetdata,isfimathlocal] = coder.const(@eml_fi_constructor_helper,maxWL,slDTOStr,slDTOAppliesToStr,emlInputFimath,datasz,data); %#ok extra args needed to prevent coder optimizations
    hfi = eml_fi_checkforerror(v,T,F,isfimathlocal);
    return;
elseif b
    % const non-fi
    [T,F,val,fiisautoscaled,pvpairsetdata,isfimathlocal] = coder.const(@eml_fi_constructor_helper,maxWL,slDTOStr,slDTOAppliesToStr,emlInputFimath,size(v),v); %#ok extra args needed to prevent coder optimizations
    hfi = eml_fi_checkforerror(val,T,F,isfimathlocal);
elseif isa(v,'single')
    hfi = eml_fi_case2(maxWL,slDTOStr,slDTOAppliesToStr,emlInputFimath,v,numerictype('single'));
else
    % non-const non-fi not allowed
    eml_invariant(false,'fixed:coder:inputMustBeConstantOrAFI','v', 'fi(v)');
end

function hfi = eml_fi_case2(maxWL,slDTOStr,slDTOAppliesToStr,emlInputFimath,p1,p2)
eml_allow_mx_inputs;
coder.inline('always');
coder.internal.prefer_const(maxWL);
coder.internal.prefer_const(slDTOStr);
coder.internal.prefer_const(slDTOAppliesToStr);
coder.internal.prefer_const(emlInputFimath);
coder.internal.prefer_const(p1, p2);
coder.extrinsic('eml_fi_constructor_helper');
coder.extrinsic('getString');
coder.extrinsic('message');

if eml_is_const(p1)
    eml_invariant(eml_is_const(p2),'fixed:coder:fiVar2MustBeConstant');
    if isfi(p1) && isfloat(p1)
        % Use the numerictype and fimath of fi object with DataType 'double' or 'single' to prevent eml_fi_constructor_helper from thinking these are builtin double or single.
        [T,F,val,fiisautoscaled,pvpairsetdata,isfimathlocal] = coder.const(@eml_fi_constructor_helper,maxWL,slDTOStr,slDTOAppliesToStr,emlInputFimath,size(p1),p1,p1.numerictype,p1,fimath,p2); %#ok extra args needed to prevent coder optimizations
    else
    [T,F,val,fiisautoscaled,pvpairsetdata,isfimathlocal] = coder.const(@eml_fi_constructor_helper,maxWL,slDTOStr,slDTOAppliesToStr,emlInputFimath,size(p1),p1,p2); %#ok extra args needed to prevent coder optimizations
    end
    hfi = eml_fi_checkforerror(val,T,F,isfimathlocal);
else % ~eml_is_const(p1)
    eml_invariant((isnumeric(p1) || islogical(p1)), ...
        'fixed:coder:fiVar1InpMustBeNumericOrAConstant');
    eml_invariant(eml_is_const(p2),'fixed:coder:fiVar2MustBeConstant');
    eml_invariant(isnumerictype(p2) || isfimath(p2),...
        'fixed:coder:fiVar2MustBeTorF');
    if isfimath(p2)
        eml_invariant((isfi(p1) && ~(p1.numerictype.isscalingunspecified)) || isinteger(p1), ...
            'fixed:coder:fiVar2toNInpArgsMustBeConstant');
    end
    if isnumerictype(p2) && coder.const(p2.isscalingunspecified)
        eml_invariant(0,'fixed:coder:fiVar2TofUnspecifiedScalingNotSupported');
    end
    [data,datasz] = local_createConstDataFromInput(p1);

    if isfi(data) && isfloat(data)
        % Use the numerictype and fimath of fi object with DataType 'double' or 'single' to prevent eml_fi_constructor_helper from thinking these are builtin double or single.
        [T1,F1,val,fiisautoscaled,pvpairsetdata,isfimathlocal] = coder.const(@eml_fi_constructor_helper,maxWL,slDTOStr,slDTOAppliesToStr,emlInputFimath,datasz,data,data.numerictype,data.fimath,p2); %#ok
    else
    [T1,F1,val,fiisautoscaled,pvpairsetdata,isfimathlocal] = coder.const(@eml_fi_constructor_helper,maxWL,slDTOStr,slDTOAppliesToStr,emlInputFimath,datasz,data,p2); %#ok extra args needed to prevent coder optimizations
    end
    hfi = eml_fi_checkforerror(p1,T1,F1,isfimathlocal);
end

function hfi = eml_fi_case3(maxWL,slDTOStr,slDTOAppliesToStr,emlInputFimath, p1, p2, p3)
eml_allow_mx_inputs;
coder.inline('always');
coder.internal.prefer_const(maxWL);
coder.internal.prefer_const(slDTOStr);
coder.internal.prefer_const(slDTOAppliesToStr);
coder.internal.prefer_const(emlInputFimath);
coder.internal.prefer_const(p1, p2, p3);
coder.extrinsic('eml_fi_constructor_helper');
coder.extrinsic('getString');
coder.extrinsic('message');

if ~eml_is_const(p1)
    eml_invariant((isnumeric(p1) || islogical(p1)), ...
        'fixed:coder:fiVar1InpMustBeNumericOrAConstant');
    if isnumerictype(p2) && coder.const(p2.isscalingunspecified)
        eml_invariant(0,'fixed:coder:fiVar2TofUnspecifiedScalingNotSupported');
    end
end
hfi = fi_helper(maxWL,slDTOStr, slDTOAppliesToStr,emlInputFimath, p1, p2, p3);


%------------------------------------------------------------------------------------------------
function [data,datasz] = local_createConstDataFromInput(varin)
% Create const data from varin (a variable input).
% If varin is a fi return a fi of value 0 with the same type a& fimath
% Other wise just return 0.

data = cast(0,'like',varin);

if eml_is_const(size(varin))
    datasz = size(varin);
else
    datasz = [1 1];
end

%--------------------------------------------------------------------------------------------
function [data,T,F] = local_getInfoFromConstInput(inp)
% Return data with right value and cast, and also return numerictype and
% fimath of data

eml_transient;

T = numerictype(inp);
F = fimath(inp);

dType = eml_fi_getDType(inp);
data  = eml_cast(inp,dType);
%--------------------------------------------------------------------------------------------
function [data,datasz,T,F] = local_getInfoFromNonConstInput(inp)
% Return data with right value and cast, and also return numerictype and
% fimath of data

eml_transient;

T = numerictype(inp);
F = fimath(inp);
data  = 0;
if eml_is_const(size(inp))
    datasz = size(inp);
else
    datasz = [1 1];
end

%--------------------------------------------------------------------------------------------
function hfi = fi_helper(maxWL,slDTOStr, slDTOAppliesToStr, emlInputFimath, varargin)
eml_allow_mx_inputs;
coder.inline('always');
coder.internal.prefer_const(maxWL);
coder.internal.prefer_const(slDTOStr);
coder.internal.prefer_const(slDTOAppliesToStr);
coder.internal.prefer_const(emlInputFimath);
coder.internal.prefer_const(varargin);
coder.extrinsic('eml_fi_constructor_helper');
coder.extrinsic('getString');
coder.extrinsic('message');

if (nargin < 6) || isempty(varargin)
    % fi() with no input arguments
    data = [];
    Tvar1 = numerictype;
    Fvar1 = fimath;
    isfimathlocal = false;
    [T,F] = ...
        coder.const(@eml_fi_constructor_helper,...
        maxWL,...
        slDTOStr,...
        slDTOAppliesToStr,...
        emlInputFimath,size(data),data,Tvar1,Fvar1,varargin{:});
    hfi = eml_fi_checkforerror(data,T,F,isfimathlocal);
elseif eml_is_const(varargin{1})
    if length(varargin) > 1
        eml_fi_checkforconst(varargin{2:end});
    end
    if isfi(varargin{1}) && isfloat(varargin{1})
        [data,Tvar1,Fvar1] = local_getInfoFromConstInput(varargin{1});
        [T,F,val,~,~,isfimathlocal] = coder.const(@eml_fi_constructor_helper,...
            maxWL,slDTOStr,slDTOAppliesToStr,emlInputFimath,size(data),data,Tvar1,Fvar1,varargin{2:end});
    else
        data = varargin{1};
        [T,F,val,~,~,isfimathlocal] = coder.const(@eml_fi_constructor_helper,maxWL,slDTOStr,slDTOAppliesToStr,emlInputFimath,size(data),data,varargin{2:end});
    end
    hfi = eml_fi_checkforerror(val,T,F,isfimathlocal);
else % ~eml_is_const(varargin{1})
    eml_invariant((isnumeric(varargin{1}) || islogical(varargin{1})), ...
        'fixed:coder:fiVar1InpMustBeNumericOrAConstant');
    if length(varargin) > 1
        eml_fi_checkforconst(varargin{2:end});
    end
    % Check to see if var2-varN give a numerictype
    % Create some temp data
    if isfi(varargin{1}) && isfloat(varargin{1})
        [data,datasz,Tvar1,Fvar1] = local_getInfoFromNonConstInput(varargin{1});
        [T,F,val,isautoscaled,pvpairsetdata,isfimathlocal] = coder.const(@eml_fi_constructor_helper,maxWL,slDTOStr,slDTOAppliesToStr,emlInputFimath,datasz,data,Tvar1,Fvar1,varargin{2:end});
    else
        [data,datasz] = local_createConstDataFromInput(varargin{1});
        [T,F,val,isautoscaled,pvpairsetdata,isfimathlocal] = coder.const(@eml_fi_constructor_helper,maxWL,slDTOStr,slDTOAppliesToStr,emlInputFimath,datasz,data,varargin{2:end});
    end
    % If the data in the fi was finally set by a PV pair then its basically a const, so create the
    % fi using val instead of varargin{1}
    if pvpairsetdata
        hfi = eml_fi_checkforntype(val,T,F,isautoscaled,isfimathlocal);
    else
        hfi = eml_fi_checkforntype(varargin{1},T,F,isautoscaled,isfimathlocal);
    end
end
