function svipwriteavifile(varargin)
%SVIPWRITEAVIFILE Video Processing Blockset Write AVI File Block Level-2
%MATLAB S-function

% Copyright 1995-2008 The MathWorks, Inc.
% $Revision: 1.1.6.1 $  $Date: 2010/08/30 02:55:22 $

if nargin==1,
    mdlInitializeSizes(varargin{1});  % block
else
    %    varargin{4} => CopyFcn       = BlockCopy;
    %                   DeleteFcn     = BlockDelete;
    %                   NameChangeFcn = BlockNameChange; 
    %                   MaskParameterChangeCallback = DialogApply(params,blkh);
    feval(varargin{4:end});% GUI callback
end 

% -----------------------------------------------------------
function mdlInitializeSizes(block)

blkh = block.BlockHandle; %#ok

% Register number of ports
if isSeparateRGB(block),
    block.NumInputPorts  = 3;
else
    block.NumInputPorts  = 1;
end
block.NumOutputPorts = 0;

% Setup port properties to be inherited or dynamic
block.SetPreCompInpPortInfoToDynamic;

% Override input port properties
for i=1:block.NumInputPorts,
    block.InputPort(i).Complexity   = 0;  % Real
end

% Register parameters
block.NumDialogPrms = 0; % coming from mask

% sampling mode
block.SampleTimes = [-1 0]; %Port-based sample time

% Specify if Accelerator should use TLC or call back into MATLAB file
block.SetAccelRunOnTLC(false);
block.SetSimViewingDevice(true);% no TLC required
set(block, 'AllowSignalsWithMoreThan2D', 1);    % enable N-D signal

% Set the block simStateCompliance to DisallowSimState because this is a
% sink block
block.SimStateCompliance = 'DisallowSimState';

% Reg methods
block.RegBlockMethod('CheckParameters',          @mdlCheckParameters);

block.RegBlockMethod('SetInputPortSamplingMode', @mdlSetInputPortFrameData);
block.RegBlockMethod('SetInputPortDimensions',   @mdlSetInputPortDimensions);
block.RegBlockMethod('SetInputPortDataType',     @mdlSetInputPortDataType);

block.RegBlockMethod('PostPropagationSetup',    @mdlPostPropSetup); %C-Mex: mdlSetWorkWidths

block.RegBlockMethod('Start',                   @mdlStart);
block.RegBlockMethod('Terminate',               @mdlTerminate);

% see code in mdlStart

%% ---------------------------------------------------------------
function params = GetMaskParameters(block)

blkh = block.BlockHandle;

% output filename
params.filename    = get_param(blkh,'filename');

% RGB, Intensity, or Obsolete
params.inputType   = get_param(blkh,'inputType');

% One multidimensional signal, or Separate color signals
params.imagePorts  = get_param(blkh, 'imagePorts');

%% ---------------------------------------------------------------
function  mdlCheckParameters(block)

params = GetMaskParameters(block);
msg = CheckParams(block.BlockHandle,params); %#ok

blkh = block.BlockHandle;
parentMdl = get(blkh,'parent');
if (isFxptGUItreuDoubleOrTrueSingle(parentMdl))
    error('vipblks:svipwriteavifile:trueDoubleSingleUnsupported',...
           ['This block does not support True doubles or True singles'... 
           ' for the Data type override parameter in the'...                       
           ' Simulink Fixed-Point interface. To access this'...                     
           ' parameter, click Tools->Fixed-Point->Fixed-Point Tool.']);
end

%% ------------------------------------------------
function mdlSetInputPortFrameData(block, idx, fd) %#ok

block.InputPort(idx).SamplingMode = fd;

%% ------------------------------------------------
function mdlSetInputPortDataType(block, idx, dtid) %#ok

block.InputPort(idx).DatatypeID = dtid;


%% ------------------------------------------------
function mdlSetInputPortDimensions(block,idx,di) %#ok

block.InputPort(idx).Dimensions = di;

%% ------------------------------------------------
function mdlPostPropSetup(block)

blkh = block.BlockHandle;
block_data = get_param(blkh,'UserData');

if ~isBlockConnected(block)
  block_data.blockConnected = false;
  set_param(blkh,'UserData', block_data);
  return;
else
  block_data.blockConnected = true;
  set_param(blkh,'UserData', block_data);
end

checkDimsAndConnectedness(block);
% get the data again because it was modified by checkDimsAndConnectedness
block_data = get_param(blkh,'UserData');

if ~isDataTypeSupported(block_data.validDType)
  error('vipblks:svipwriteavifile:invalidDataType', 'Unsupported input data type');
end 

orgDType = block.InputPort(1).DataTypeID;
isNotScaledDouble = 1;
if (orgDType > 8) % non-builtin data type
   [dInfo, isScaledDouble]=fixdt(block.InputPort(1).Datatype);
   isNotScaledDouble = ~isScaledDouble;
end

if isSeparateRGB(block)
    if any(~block_data.portsConnected)
        if (isNotScaledDouble)
           block.RegBlockMethod('Outputs',                  @mdlOutputsRGB);
        else
           block.RegBlockMethod('Outputs',                  @mdlOutputsRGB_SD);
        end
    else
        if (isNotScaledDouble)
            block.RegBlockMethod('Outputs',                  @mdlOutputsRGB_FullConnect);
        else
            block.RegBlockMethod('Outputs',                  @mdlOutputsRGB_FullConnect_SD);
        end
    end
else
    if (isNotScaledDouble)
       block.RegBlockMethod('Outputs',                  @mdlOutputsIntensity);
    else
       block.RegBlockMethod('Outputs',                  @mdlOutputsIntensity_SD);
    end
end


%%----------------------------------------------------------------
function checkDimsAndConnectedness(block)
blkh = block.BlockHandle;
block_data = get_param(blkh,'UserData');

% check input dimensions and connectedness
validDims = [];
validDType = [];
validDTypeID = []; %#ok
differingDims = 0;
differingDTypes = 0;
firstValidPort = [];
portsConnected = [];

for i=1:block.NumInputPorts,
    dims = block.InputPort(i).Dimensions;
    dType = block.InputPort(i).DataTypeID;
    if (dType > 8) % non-builtin data type
        [dInfo, isScaledDouble]=fixdt(block.InputPort(i).Datatype);
        if isScaledDouble
            dType = getNonOverrideDTIDforScaledDbl(dInfo);
        end
    end
    connected = ~isscalar(dims);
    if ~ connected,
        portsConnected(i) = false;
    else
        % yes, connected
        portsConnected(i) = true;
        % check dims
        firstValidPort = i;
        if isempty(validDims),
            validDims = dims;
        elseif ~isequal(validDims,dims),
            differingDims = differingDims + 1;
        end
        
        %check data type
        if isempty(validDType),
            validDType = dType;
        elseif ~isequal(validDType, dType),
            differingDTypes = differingDTypes + 1;
        end
    end
end

blockName = get(blkh, 'Name');
% if connected, all input dimensions and data types need to be the same

if all(~portsConnected),
  error('vipblks:svipwriteavifile:noPortConnected', ...
          'At least one port must be connected in block %s', blockName);
end

if differingDims > 0,
    error('vipblks:svipwriteavifile:invalidInputDimension', ...
            'All input data to block %s must be of the same dimensions', blockName);
end
if differingDTypes > 0,
    error('vipblks:svipwriteavifile:invalidInputDataType', ...
            'All input data to block %s must be of the same data type', blockName);
end

if any(portsConnected) > 0,
    block_data.anyDisconnected = true;
else
    block_data.anyDisconnected =false;
end

block_data.validDType = validDType;
block_data.validDims = validDims;
block_data.firstValidPort = firstValidPort;
block_data.portsConnected = portsConnected;

set_param(blkh, 'UserData', block_data);


%% -----------------------------------------------------------
function mdlStart(block)

% Updates block_data
blkh = block.BlockHandle;
block_data = get_param(blkh,'UserData');

thisModel = bdroot(blkh);
if isequal(get_param(thisModel,'buildingrtwcode'),'on')
    warning('vipblks:svipwriteavifile:codeGenUnsupported', ...
                'The Write AVI file block does not support code generation - no file will be created');
end

if ~block_data.blockConnected
  return;
end

block_data.isSeparateRGB = isSeparateRGB(block);
dType = block.InputPort(1).DatatypeID; %#ok

isNotScaledDouble = 1;
isScaledDouble = 0;
if (dType > 8) % non-builtin data type
   [dInfo, isScaledDouble]=fixdt(block.InputPort(1).Datatype);
   isNotScaledDouble = ~isScaledDouble;
end
if (isScaledDouble)
   nonOverDType = getNonOverrideDTIDforScaledDbl(dInfo);
   block_data.castFcn = getCastFunction(nonOverDType);
end
block_data.aviFileObj = avifile(get_param(blkh,'filename'), ...
    'Fps', 1.0 / block.SampleTimes(1), ...
    'Compression', 'None', ...
    'Quality', 100);

block_data.convertFcn = getConversionFunction(block_data.validDType);

% TODO 'real' data types
dims = block.InputPort(1).Dimensions;
isColorImage = block_data.isSeparateRGB ...
    || (~block_data.isSeparateRGB && length(dims)==3);
if isColorImage
    block_data.tempImage = zeros(block_data.validDims(1), ...
                                 block_data.validDims(2), ...
                                 3, ...
                                 'uint8');
else
    block_data.aviFileObj = set(block_data.aviFileObj, 'Colormap', gray(256));  % TODO other colormaps?
    z = zeros(block_data.validDims(1), block_data.validDims(2));
    block_data.tempImage = zeros(block_data.validDims(1), ...
                                 block_data.validDims(2), ...
                                 'uint8');
end

set_param(blkh, 'UserData', block_data);

% ---------------------------------------------------------------
function msg = CheckParams(blkh, params) %#ok

msg = '';
value = params.imagePorts;
name = 'image signal parameter';

if isempty(value)
    msg = ['The ', name, ' is undefined.'];
    return;
end

if ~isnumeric(value)
    msg = ['The ', name, ' must be a numeric value.'];
    return;
end

if ~isreal(value)
    msg = ['The ', name, ' must be real valued.'];
    return;
end

if ~isempty(find(isnan(value)|isinf(value), 1))
    msg = ['The ', name, ' cannot be NaN or Inf.'];
    return;
end

if issparse(value)
    msg = ['The ', name, ' cannot be a sparse matrix.'];
    return;
end

if floor(value) ~= value
    msg = ['The ', name, ' must be an integer.'];
    return;
end

if value < 1 || value > 2
    msg = ['The ', name, ' has invalid value.'];
    return;
end
    
if length(value) ~= 1
    msg = ['The ', name, ' has invalid dimensionality.'];
    return;
end

% ---------------------------------------------------------------
function mdlOutputsRGB(block)

blkh = block.BlockHandle;
block_data = get_param(blkh,'UserData');

if ~block_data.blockConnected
  return;
end

if block_data.portsConnected(1)
    block_data.tempImage(:,:,1) = block_data.convertFcn(block.InputPort(1).Data);
end

if block_data.portsConnected(2)
    block_data.tempImage(:,:,2) = block_data.convertFcn(block.InputPort(2).Data);
end

if block_data.portsConnected(3)
    block_data.tempImage(:,:,3) = block_data.convertFcn(block.InputPort(3).Data);
end

block_data.aviFileObj = addframe(block_data.aviFileObj, block_data.tempImage);

set_param(blkh, 'UserData', block_data);

% ---------------------------------------------------------------
function mdlOutputsRGB_SD(block)

blkh = block.BlockHandle;
block_data = get_param(blkh,'UserData');

if ~block_data.blockConnected
  return;
end

if block_data.portsConnected(1)
    block_data.tempImage(:,:,1) = block_data.convertFcn(block.InputPort(1).DataAsDouble);
end

if block_data.portsConnected(2)
    block_data.tempImage(:,:,2) = block_data.convertFcn(block.InputPort(2).DataAsDouble);
end

if block_data.portsConnected(3)
    block_data.tempImage(:,:,3) = block_data.convertFcn(block.InputPort(3).DataAsDouble);
end

block_data.aviFileObj = addframe(block_data.aviFileObj, block_data.tempImage);

set_param(blkh, 'UserData', block_data);

% ---------------------------------------------------------------
function mdlOutputsRGB_FullConnect(block)

blkh = block.BlockHandle;
block_data = get_param(blkh,'UserData');

block_data.tempImage(:,:,1) = block_data.convertFcn(block.InputPort(1).Data);
block_data.tempImage(:,:,2) = block_data.convertFcn(block.InputPort(2).Data);
block_data.tempImage(:,:,3) = block_data.convertFcn(block.InputPort(3).Data);

block_data.aviFileObj = addframe(block_data.aviFileObj, block_data.tempImage);

set_param(blkh, 'UserData', block_data);

% ---------------------------------------------------------------
function mdlOutputsRGB_FullConnect_SD(block)

blkh = block.BlockHandle;
block_data = get_param(blkh,'UserData');

block_data.tempImage(:,:,1) = block_data.convertFcn(block.InputPort(1).DataAsDouble);
block_data.tempImage(:,:,2) = block_data.convertFcn(block.InputPort(2).DataAsDouble);
block_data.tempImage(:,:,3) = block_data.convertFcn(block.InputPort(3).DataAsDouble);

block_data.aviFileObj = addframe(block_data.aviFileObj, block_data.tempImage);

set_param(blkh, 'UserData', block_data);

% ---------------------------------------------------------------
function mdlOutputsIntensity(block)

blkh = block.BlockHandle;
block_data = get_param(blkh,'UserData');

if ~block_data.blockConnected
  return;
end

block_data.tempImage = block_data.convertFcn(block.InputPort(1).Data);

block_data.aviFileObj = addframe(block_data.aviFileObj, block_data.tempImage);

set_param(blkh, 'UserData', block_data);

% ---------------------------------------------------------------
function mdlOutputsIntensity_SD(block)

blkh = block.BlockHandle;
block_data = get_param(blkh,'UserData');

if ~block_data.blockConnected
  return;
end

%%block_data.tempImage = block_data.convertFcn(uint8(block.InputPort(1).DataAsDouble));
block_data.tempImage = block_data.convertFcn(block_data.castFcn(block.InputPort(1).DataAsDouble));

block_data.aviFileObj = addframe(block_data.aviFileObj, block_data.tempImage);

set_param(blkh, 'UserData', block_data);

% ---------------------------------------------------------------
function mdlTerminate(block)
% TERMINATE Clean up any remaining items

blkh = block.BlockHandle;
block_data = get_param(blkh,'UserData');

if isfield(block_data, 'aviFileObj'),
    block_data.aviFileObj = close(block_data.aviFileObj);
end


% ---------------------------------------------------------------
function result = isSeparateRGB(block)
params = GetMaskParameters(block);
result = (strcmp(params.inputType, 'Obsolete') ...
    && strcmp(params.imagePorts, 'Separate color signals')) ...
    || strcmp(params.inputType, 'RGB');


% ---------------------------------------------------------------
function supported = isDataTypeSupported(dataType)
if dataType >=0 && dataType <= 8,
    supported =true;
else
    supported = false;
end


% ---------------------------------------------------------------
function convFunc = getConversionFunction(dataType)
if dataType == 0, 
    convFunc = @convertDataFromDouble;
elseif dataType == 1, 
    convFunc = @convertDataFromSingle;
elseif dataType == 2, 
    convFunc = @convertDataFromInt8;
elseif dataType == 3, 
    convFunc = @noConversion;    
elseif dataType == 4,
    convFunc = @convertDataFromInt16;
elseif dataType == 5,
    convFunc = @convertDataFromUint16;    
elseif dataType == 6,
    convFunc = @convertDataFromInt32;
elseif dataType == 7,
    convFunc = @convertDataFromUint32;
elseif dataType == 8,
    convFunc = @convertDataFromBoolean;
else
    error('vipblks:svipwriteavifile:unsupportedDataType', ...
            'Unsupported data type');
end

% ---------------------------------------------------------------
function convFunc = getCastFunction(nonOverDType)
if nonOverDType == 0, 
    convFunc = @double;
elseif nonOverDType == 1, 
    convFunc = @single;
elseif nonOverDType == 2, 
    convFunc = @int8;
elseif nonOverDType == 3, 
    convFunc = @uint8;    
elseif nonOverDType == 4,
    convFunc = @int16;
elseif nonOverDType == 5,
    convFunc = @uint16;    
elseif nonOverDType == 6,
    convFunc = @int32;
elseif nonOverDType == 7,
    convFunc = @uint32;
else
    error('vipblks:svipwriteavifile:unsupportedDataType', ...
            'Unsupported data type');
end

% ---------------------------------------------------------------
function convertString = DTypeIDToDTypeString(dataType)
if dataType == 0, 
    convertString = 'double';
elseif dataType == 1, 
    convertString = 'single';
elseif dataType == 2, 
    convertString = 'int8';
elseif dataType == 3, 
    convertString = 'uint8';    
elseif dataType == 4,
    convertString = 'int16';
elseif dataType == 5,
    convertString = 'uint16';    
elseif dataType == 6,
    convertString = 'int32';
elseif dataType == 7,
    convertString = 'uint32';
elseif dataType == 8,
    convertString = 'uint8';    % boolean in SL is uint8
else
    error('vipblks:svipwriteavifile:unsupportedDataType', ...
            'Unsupported data type');
end

% ------------------------------------------------------------
function convertedData = noConversion(inputData)
convertedData = inputData;

% ------------------------------------------------------------
function convertedData = convertDataFromDouble(inputData)
convertedData = uint8(inputData * 255);

% ------------------------------------------------------------
function convertedData = convertDataFromSingle(inputData)
convertedData = uint8(inputData * 255);

% ------------------------------------------------------------
function convertedData = convertDataFromInt8(inputData)
convertedData = uint8(int16(inputData) + 128);

% ------------------------------------------------------------
function convertedData = convertDataFromInt16(inputData)
convertedData = uint8((single(inputData) + 32768) / 257.0); 

% ------------------------------------------------------------
function convertedData = convertDataFromUint16(inputData)
convertedData = uint8(single(inputData) / 257.0);

% ------------------------------------------------------------
function convertedData = convertDataFromInt32(inputData)
doubleData = (double(inputData) + 2147483648.0) / 4294967295.0;
convertedData = im2uint8(doubleData);

% ------------------------------------------------------------
function convertedData = convertDataFromUint32(inputData)
doubleData = double(inputData) / 4294967295.0;
convertedData = im2uint8(doubleData);

% ------------------------------------------------------------
function convertedData = convertDataFromBoolean(inputData)
convertedData = uint8(single(inputData) * 255);

% ------------------------------------------------------------
function con = isBlockConnected(block)

notConnected = 0;
for i=1:block.NumInputPorts,
  dims = block.InputPort(i).Dimensions;
  connected = ~isscalar(dims);
  if ~ connected,
    notConnected = notConnected + 1;
  end
end

if notConnected == block.NumInputPorts
  con = 0;
else
  con = 1;
end

% ---------------------------------------------------------------
function dType = getNonOverrideDTIDforScaledDbl(dInfo)
FL = dInfo.FractionLength;
WL = dInfo.WordLength;
if (FL ~= 0)
    dType = -1;
    return;
end
if (WL ~= 8 && WL ~= 16 && WL ~= 32)
    dType = -1;
    return;
end 
% 0,1,
if (WL==8)
    dType = 2; %int8
elseif (WL==16) 
    dType = 4; %int16
else
    dType = 6; %int32
end

if ~dInfo.Signed
    dType = dType +1; % unsigned
end

% ---------------------------------------------------------------
function  flag  = isFxptGUItreuDoubleOrTrueSingle(parentMdl)

    overrideMode = get_param(parentMdl,'DataTypeOverride');
    flag  = strcmp(overrideMode,'TrueDoubles') || strcmp(overrideMode,'TrueSingles');

% [EOF] svipwriteavifile.m

