function dataType = slDataTypeAndScale(unevaledContainerTypeStr, unevaluedScalingStr, context)
%  SLDATATYPEANDSCALE compatibility support for Simulink data type parameters
%
%   This function maps a container type and, if needed, fixed-point scaling to
%   a fully specified data type. The purpose of this function is to support
%   backwards compatibility of Simulink models. This function is intended
%   for use only within parameters on Simulink block dialogs.
%
%   To remove unnecessary calls to SLDATATYPEANDSCALE within a Simulink model 
%   and replace them with an equivalent built-in or user-defined
%   fully-specified data type, e.g. fixdt, use SLREMOVEDATATYPEANDSCALE.
%
%   We no longer support the usage of this function where the first argument
%   evaluates to either a Simulink.AliasType object or a Simulink.NumericType object
%   with its property IsAlias set to true. 
%
%   Usage
%    dataType = slDataTypeAndScale( unevaledContainerTypeStr, unevaluedScalingStr )    
%    dataType = slDataTypeAndScale( unevaledContainerTypeStr, unevaluedScalingStr, context )    
%
%   In older versions of Simulink, some blocks specified
%   a data type using multiple dialog parameters.  These parameters could 
%   include a data type edit parameter and a scaling edit parameter.  
%   Current versions of these blocks have combined the multiple parameters
%   into just one parameter.  SLDATATYPEANDSCALE supports backwards
%   compatibility in the face of this parameter count reduction.
%   SLDATATYPEANDSCALE takes the contents of the old data type and scaling
%   edit fields as its first and second arguments, respectively.  The first
%   argument specifies the container type and possibly the scaling too.  
%   If the first argument does not provide scaling, then the scaling is
%   obtained from the second argument.
%
%   Simulink block parameters are generally stored as strings.  These
%   parameters are passed to this function as unevaluated strings for two
%   reasons.  First, the parameter strings may contain comments at the end.
%   Passing the original parameter as an unevaluated string prevents
%   errors due to end comments.  The second reason is that 
%   evaluating the parameters can cause errors. SLDATATYPEANDSCALE only uses
%   the second argument conditionally.  Passing this argument as a string avoids
%   needless evaluation errors. This string will be evaluated only if it is really 
%   needed.
%
%   The old data type and scaling arguments are resolved in the context of CONTEXT
%   if CONTEXT is provided. Otherwise, they are evaluated in the caller's workspace.
% 
%   Example 1: needs the scaling and returns fixdt(1,16,7)
%
%     slDataTypeAndScale( 'fixdt(1,16)', '2^-7' )
%
%   Example 2: ignores the scaling and returns fixdt(1,16,9)
%
%     slDataTypeAndScale( 'fixdt(1,16,9)', '2^-7' )
%   
%   Example 3: ignores the scaling and returns 'single'
%
%     slDataTypeAndScale( '''single''', 'bad text' )
%
%   See also slRemoveDataTypeAndScale.

% Copyright 2006-2011 The MathWorks, Inc.
%   
% 

if nargin < 3
    isBlockParameter = false;
    containerType = evalin('caller', unevaledContainerTypeStr);
else
    isBlockParameter = true;
    containerType = slResolve(unevaledContainerTypeStr, context);
end

if isa(containerType, 'Simulink.Bus')
    if isBlockParameter
        blkHandle = get_param(context, 'handle');
    else
        blkHandle = [];
    end
    throw(l_MSLException('Simulink:fixedandfloat:slDTnScaleBusDT', ...
                         blkHandle, ...
                         unevaledContainerTypeStr));
end

dataTypeDeterminesScaling = getdatatypespecs(containerType,[],0,0,3);

if dataTypeDeterminesScaling

    dataType = containerType;    
    errType = 0; % Indicate whether we should throw error and, 
                 % if we should, what error to throw.

    % Throw error if data type is a (fully specified) Simulink.NumericType 
    % object with IsAlias set to true
    if isa(dataType, 'Simulink.NumericType') && dataType.IsAlias
        errType = 1;
    end

    % Throw error if data type is a Simulink.AliasType object 
    if isa(dataType, 'Simulink.AliasType')
        errType = 2;
    end

    if (errType > 0)
        if isBlockParameter
            bdName = l_getBDNameFromBlockPath(context);
            blkHandle = get_param(context, 'handle');

            % Model Advisor upgrade task
            maUpgradeTask = 'Upgrading to the Current Simulink Version'; 
        
            % Message suggesting user to run Model Advisor upgrade check
            runMAUpgradeTaskMsg = DAStudio.message('Simulink:fixedandfloat:slDTnScaleUpgrade', ...
                                                   bdName, ...
                                                   maUpgradeTask, ...
                                                   bdName);
        else
            blkHandle = [];
            runMAUpgradeTaskMsg = '';
        end

        switch errType,
          case 1,
            throw(l_MSLException('Simulink:fixedandfloat:slDTnScaleNumDTWithAliasOn', ...
                                 blkHandle, ...
                                 unevaledContainerTypeStr, ...
                                 runMAUpgradeTaskMsg));
          case 2,
            throw(l_MSLException('Simulink:fixedandfloat:slDTnScaleAliasDT', ...
                                 blkHandle, ...
                                 unevaledContainerTypeStr, ...
                                 runMAUpgradeTaskMsg));
          otherwise,
            assert('Invalid error type');
        end
    end
else
    if (nargin < 3)
        scaling = evalin('caller',unevaluedScalingStr);
    else
        scaling = slResolve(unevaluedScalingStr, context);
    end
    dataType = getdatatypespecs(containerType,scaling,0,0,1);
end    
end

function me = l_MSLException(msgid, objHandle, varargin)
%Constructs an MSLException object whose error message is determined by msgid.
    me = MSLException(objHandle, message(msgid, varargin{:})); 
end

function bdName = l_getBDNameFromBlockPath(blockPath)
    blockObject = get_param(blockPath, 'Object');
    parent = blockObject.getParent;
    while ~isa(parent, 'Simulink.BlockDiagram')
        parent = parent.getParent;
    end
    bdName = parent.Name;
end
