function svipreadavifile(varargin)
%SVIPREADAVIFILE Video Processing Blockset Read AVI File Block Level-2 MATLAB S-function

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

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;
block_data = get_param(blkh,'UserData');
filename = get_param(blkh,'filename');
outputEOF = strcmp(get_param(blkh,'outputEOF'), 'on');

try
    block_data.aviInfo = aviinfo(filename);
catch aviinfoException
    error('vipblks:svipreadavifile:lastError', aviinfoException.message);
end
dims = [block_data.aviInfo.Height block_data.aviInfo.Width];

isTrueColor = isFileTrueColor(filename);

numDataOutports = 0;
if isTrueColor,
    numDataOutports = numDataOutports + 3;
    block_data.isRGB = true;
else
    numDataOutports = numDataOutports + 1;
    block_data.isRGB = false;
end

totalNumOutports = numDataOutports;

if outputEOF,
    totalNumOutports = totalNumOutports + 1;
    block_data.eofOutportIndex = totalNumOutports;
else
    block_data.eofOutportIndex = -1;
end

block.NumOutputPorts = totalNumOutports;

block.NumInputPorts  = 0;

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

dTypeID = getDTypeFromString(blkh, get_param(blkh, 'dataType'));

% Override output port properties
for i=1:numDataOutports,
    block.OutputPort(i).Complexity = 0;  % Real
    block.OutputPort(i).SamplingMode = 0; % non-frame
    block.OutputPort(i).Dimensions = dims;
    block.OutputPort(i).DatatypeID = dTypeID;
end

if outputEOF,
    block.OutputPort(block_data.eofOutportIndex).Complexity = 0;  % Real
    block.OutputPort(block_data.eofOutportIndex).SamplingMode = 0; % non-frame
    block.OutputPort(block_data.eofOutportIndex).Dimensions = [1 1];
    block.OutputPort(block_data.eofOutportIndex).DatatypeID = 8; % boolean flag
end

block_data.desiredDataTypeID = dTypeID;

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

% sampling mode
block.SampleTimes = [1/block_data.aviInfo.FramesPerSecond 0];

% Specify if Accelerator should use TLC or call back into MATLAB file
block.SetAccelRunOnTLC(false);
block.SetSimViewingDevice(true);% no TLC required

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

% Reg methods
block.RegBlockMethod('CheckParameters',          @mdlCheckParameters);
block.RegBlockMethod('Start',                    @mdlStart);
if block_data.isRGB,
    block.RegBlockMethod('Outputs',                  @mdlOutputsRGB);
else
    block.RegBlockMethod('Outputs',                  @mdlOutputsIntensity);
end
block.RegBlockMethod('Terminate',                @mdlTerminate);

if strcmp(get_param(bdroot(blkh), 'Lock'), 'off')
    set_param(blkh,'UserData', block_data);
end

% see code in mdlStart

%% ---------------------------------------------------------------
function isTrueColor = isFileTrueColor(filename)
info = aviinfo(filename, 'Robust');
if strcmp(info.VideoFrameHeader.ImageType, 'grayscale') == 1,
    isTrueColor = false;
elseif strcmp(info.VideoFrameHeader.ImageType, 'indexed') == 1,
    isTrueColor = false;
elseif strcmp(info.VideoFrameHeader.ImageType, 'truecolor') == 1,
    isTrueColor = true;
else
    error('vipblks:svipreadavifile:invalidImageType', ...
          '%s is not a supported image type', ...
          info.VideoFrameHeader.ImageType);
end


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

blkh = block.BlockHandle;

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

% whether or not the input file should be looped
params.loopOrNot   = get_param(blkh,'loopOrNot');

% number of times to output the input file's contents
params.numLoops    = get_param(blkh,'numLoops');

% desired data type of the output images
params.dataType    = get_param(blkh,'dataType');

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

params = GetMaskParameters(block);
msg = CheckParams(block.BlockHandle,params);
if ~isempty(msg)
    error('vipblks:svipreadavifile:invalidParameter', msg);
end

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

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

thisModel = bdroot(blkh);
if isequal(get_param(thisModel,'buildingrtwcode'),'on')
    error('vipblks:svipreadavifile:codeGenNotSupported', ...
          'The Read AVI file block does not support code generation');
end

warning('vipblks:svipreadavifile:obsolete', ...
        'The Read AVI file block is obsolete.  Please use the From Multimedia File block instead.');

filename = block_data.aviInfo.Filename;

infoStruct = vipaviread([], filename);

block_data.videoFrameIndex = 1;
block_data.numVideoFrames = block_data.aviInfo.NumFrames;

block_data.loop = strcmp(get_param(blkh,'loopOrNot'), 'on') == 1;
block_data.numLoops = str2double(get_param(blkh,'numLoops'));
block_data.loopIndefinitely = isinf(block_data.numLoops);
block_data.currentLoop = 1;
block_data.outputtingBlack =false;

dims = block.OutputPort(1).Dimensions;

if block_data.isRGB
    % truecolor will always be 24-bit RGB returned as uint8
    %uint8
    block_data.conversionFcn = getConversionFcn(block_data.desiredDataTypeID);
    block_data.blackFrame = block_data.conversionFcn(uint8(zeros(dims(1), dims(2),3)));
else %intensity
    %uint8
    block_data.conversionFcn = getConversionFcn(block_data.desiredDataTypeID);
    block_data.blackFrame = block_data.conversionFcn(uint8(zeros(dims(1), dims(2))));
end

% TODO figure out why the heck infoStruct.fid becomes invalid
try
    ftell(infoStruct.fid);
catch
    infoStruct.fid = fopen(filename,'r','l');
end

block_data.infoStruct = infoStruct;

set_param(blkh,'UserData', block_data);


%% -----------------------------------------------------------
function dTypeID = getDTypeIDFromBitDepth(filename) %#ok
info = aviinfo(filename, 'Robust');
bits = info.VideoFrameHeader.BitDepth;
switch bits
    case 24
        dTypeID = 3;    %truecolor RGB as 3 planes of uint8
    case 8
        dTypeID = 3;    %8-bit grayscale as uint8
    otherwise
        dTypeID = -1;
end


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

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

if block_data.outputtingBlack,
    block.OutputPort(1).Data = block_data.blackFrame(:,:,1);
    block.OutputPort(2).Data = block_data.blackFrame(:,:,2);
    block.OutputPort(3).Data = block_data.blackFrame(:,:,3);
    if block_data.eofOutportIndex ~= -1,
        block.OutputPort(block_data.eofOutportIndex).Data = false;
    end
    return;
end

block_data.infoStruct = vipaviread(block_data.infoStruct, block_data.videoFrameIndex);
if block_data.eofOutportIndex ~= -1,
    block.OutputPort(block_data.eofOutportIndex).Data = ...
        block_data.videoFrameIndex == block_data.numVideoFrames;
end
block_data.videoFrameIndex =  block_data.videoFrameIndex + 1;
if  block_data.videoFrameIndex > block_data.numVideoFrames
    if block_data.loop
        if block_data.loopIndefinitely || ... 
                block_data.currentLoop < block_data.numLoops
            block_data.videoFrameIndex = 1; % loop
            block_data.currentLoop = block_data.currentLoop + 1;
        else
            block_data.outputtingBlack = true;
            set_param(blkh,'UserData', block_data);
            mdlOutputsRGB(block);
            return;
        end
    else
        block_data.outputtingBlack = true;
        set_param(blkh,'UserData', block_data);
        mdlOutputsRGB(block);
        return;
    end
end

videoFrame = block_data.infoStruct.lastFrame;
block_data.convertedData = block_data.conversionFcn(videoFrame);
block.OutputPort(1).Data = block_data.convertedData(:,:,1);
block.OutputPort(2).Data = block_data.convertedData(:,:,2);
block.OutputPort(3).Data = block_data.convertedData(:,:,3);

set_param(blkh,'UserData', block_data);


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

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

if block_data.outputtingBlack,
    block.OutputPort(1).Data = block_data.blackFrame;
    if block_data.eofOutportIndex ~= -1,
        block.OutputPort(block_data.eofOutportIndex).Data = false;
    end    
    return;
end

block_data.infoStruct = vipaviread(block_data.infoStruct, block_data.videoFrameIndex);
if block_data.eofOutportIndex ~= -1,
    block.OutputPort(block_data.eofOutportIndex).Data = ...
        block_data.videoFrameIndex == block_data.numVideoFrames;
end
block_data.videoFrameIndex =  block_data.videoFrameIndex + 1;
if  block_data.videoFrameIndex > block_data.numVideoFrames
    if block_data.loop
        if block_data.loopIndefinitely || ... 
                block_data.currentLoop < block_data.numLoops
            block_data.videoFrameIndex = 1; % loop
            block_data.currentLoop = block_data.currentLoop + 1;
        else
            block_data.outputtingBlack = true;
            set_param(blkh,'UserData', block_data);
            mdlOutputsIntensity(block);
            return;
        end
    else
        block_data.outputtingBlack = true;
        set_param(blkh,'UserData', block_data);
        mdlOutputsIntensity(block);
        return;
    end
end

videoFrame = block_data.infoStruct.lastFrame;
block_data.convertedData = block_data.conversionFcn(videoFrame);
block.OutputPort(1).Data = block_data.convertedData;

set_param(blkh,'UserData', block_data);



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

if ~ ischar(params.filename)
    msg = 'Filename must be a string';
    return;
end

numLoops = str2double(params.numLoops);
if strcmp(params.loopOrNot, 'on')
    if ~ isnumeric(numLoops) || ~ isscalar(numLoops) || ~ isreal(numLoops)
        msg = 'Number of times to play file must be a real scalar number';
        return;
    end
    if floor(numLoops) ~= numLoops
        msg = 'Number of times to play file must be a positive integer or INF';
        return;
    end
end

%info = aviinfo(params.filename);

msg = '';


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

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

if isfield(block_data, 'infoStruct')
    vipaviread(block_data.infoStruct);
end


% ---------------------------------------------------------------
function conv = getConversionFcn(outputDType)
uint8Fcns = getUint8ConvFcns;
conv = uint8Fcns{outputDType + 1};



% ---------------------------------------------------------------
function uint8Fcns = getUint8ConvFcns
uint8Fcns{1} = @convertUint8ToDouble;
uint8Fcns{2} = @convertUint8ToSingle;
uint8Fcns{3} = @convertUint8ToInt8;
uint8Fcns{4} = @convertUint8ToUint8;    %no-op
uint8Fcns{5} = @convertUint8ToInt16;
uint8Fcns{6} = @convertUint8ToUint16;
uint8Fcns{7} = @convertUint8ToInt32;
uint8Fcns{8} = @convertUint8ToUint32;

% ---------------------------------------------------------------
function dTypeID = getDTypeFromString(blkh, dTypeString) %#ok
if strcmp(dTypeString, 'double')
    dTypeID = 0;
    return;
elseif strcmp(dTypeString, 'single')
    dTypeID = 1;
    return;
elseif strcmp(dTypeString, 'int8')
    dTypeID = 2;
    return;
elseif strcmp(dTypeString, 'uint8')
    dTypeID = 3;
    return;
elseif strcmp(dTypeString, 'int16')
    dTypeID = 4;
    return;
elseif strcmp(dTypeString, 'uint16')
    dTypeID = 5;
    return;
elseif strcmp(dTypeString, 'int32')
    dTypeID = 6;
    return;
elseif strcmp(dTypeString, 'uint32')
    dTypeID = 7;
    return;
else
    % native data type for 24-bit truecolor and 8-bit intensity is uint8
    dTypeID = 3;
    return;
end


% FROM UINT8 CONVERSIONS

% ---------------------------------------------------------------
function convertedData = convertUint8ToDouble(inputData)
convertedData = double(inputData) / 255.0;

% ---------------------------------------------------------------
function convertedData = convertUint8ToSingle(inputData)
convertedData = single(inputData) / 255.0;

% ---------------------------------------------------------------
function convertedData = convertUint8ToInt8(inputData)
convertedData = int8(single(inputData) - 128);

% ---------------------------------------------------------------
function convertedData = convertUint8ToUint8(inputData)
%no-op
convertedData = inputData;

% ---------------------------------------------------------------
function convertedData = convertUint8ToInt16(inputData)
convertedData = int16(single(inputData) * 257 - 32768);

% ---------------------------------------------------------------
function convertedData = convertUint8ToUint16(inputData)
convertedData = uint16(single(inputData) * 257);

% ---------------------------------------------------------------
function convertedData = convertUint8ToInt32(inputData)
convertedData = int32(single(inputData) * 16843009.0 - 2147483648.0);

% ---------------------------------------------------------------
function convertedData = convertUint8ToUint32(inputData)
convertedData = uint32(single(inputData) * 16843009.0);

% ------------------------------------------------------------
% [EOF] svipreadavifile.m

