function y = movSumProdOrMean(op,x,kleft,kright,dim,nanflag,ep,fillval,ytype)
%MATLAB Code Generation Library Function

%    * OP must be 'sum', 'prod', or 'mean'.
%    * KLEFT and KRIGHT are the elements of the equivalent 2-element vector
%      input for the window size.
%    * DIM is the dimension to operate along.
%    * NANFLAG must be 'o' for 'omitnan' or 'i' for 'includenan'. Longer
%      strings are OK, but only the first element is referenced.
%    * EP is the "endpoints" option to the mov* function. It must be 'f',
%      's', 'd', or a numeric or logical scalar that can be castLike X.
%    * AUTOFILLVALUE is a scalar of the same type as X. It is used when ep
%      == 'f' to fill missing values for windows that overlap the edges of
%      the data.
%    * YTYPE sets the type of Y. It must match the type returned by WFUN.

% Copyright 2016-2020 The MathWorks, Inc.
%#codegen

narginchk(3,inf);
coder.internal.prefer_const(op,kleft,kright,dim,nanflag,ep,fillval,ytype);
coder.internal.assert(~isreal(x) || isreal(ep), ...
    'MATLAB:movfun:complexFillValue');
nx = coder.internal.indexInt(size(x,dim));
if ep == 'd' % Discard
    ny = nx - kleft - kright;
    if ny < 0
        ny = coder.internal.indexInt(0);
    elseif ny > nx
        ny = nx;
    end
else
    ny = coder.internal.indexInt(size(x,dim));
end
omitnan = nanflag(1) == 'o';
vfun = @(x)vmovfun(op,x,nx,kleft,kright,omitnan,ep,fillval,ytype);
y = coder.internal.applyVectorFunction(vfun,x,dim,ny,ytype);

%--------------------------------------------------------------------------

function y = vmovfun(op,x,nx,kleft,kright,omitnan,ep,fillval,ytype)
coder.internal.prefer_const(op,kleft,kright,omitnan,ep,fillval,ytype);
coder.internal.allowEnumInputs;
% endopt must be a scalar numeric value or
%'s' for "shrink",
%'f' for "fill", or
%'d' for "discard".
nw = kleft + kright + 1;
if ischar(ep) && ep == 'd' % Discard
    ny = nx - nw + 1;
    if ny < 0
        ny(1) = 0;
    elseif ny > nx
        ny = nx;
    end
    if coder.internal.isConstTrue(isrow(x))
        y = zeros(1,ny,'like',ytype);
    else
        y = zeros(ny,1,'like',ytype);
    end
else
    ny = nx;
    y = zeros(size(x),'like',ytype);
end
assert(ny <= nx); %<HINT>
coder.internal.treatAsParfor;
for k = 1:ny
    y(k) = processWindow(op,k,x,kleft,kright,ep,fillval,omitnan);
end

%--------------------------------------------------------------------------

function y = processWindow(op,i,x,kleft,kright,endopt,fillval,omitnan)
coder.internal.prefer_const(kleft,kright,endopt,fillval,omitnan);
coder.internal.allowEnumInputs;
ONE = coder.internal.indexInt(1);
nx = coder.internal.indexInt(numel(x));
[iLeft,iRight,iPadLeft,iPadRight] = movfunWindow(i,nx,kleft,kright,endopt);
vlen = iRight - iLeft + 1;
YZERO = coder.const(mean(coder.internal.scalarEg(x)));
% The compiler will issue an error if op is not recognized.
if coder.const(strcmp(op,'mean'))
    [y,count] = combineVectorElements(@plus,x(:),YZERO,ONE,omitnan,vlen,iLeft);
    if iPadLeft > 0
        count = count + iPadLeft;
        y(1) = y(1) + cast(fillval,'like',y)*cast(iPadLeft,'like',real(y));
    end
    if iPadRight > 0
        count = count + iPadRight;
        y(1) = y(1) + cast(fillval,'like',y)*cast(iPadRight,'like',real(y));
    end
    y = y/cast(count,'like',y);
elseif coder.const(strcmp(op,'prod'))
    YONE = ones('like',YZERO);
    y = combineVectorElements(@times,x(:),YONE,ONE,omitnan,vlen,iLeft);
    if iPadLeft > 0
        y(1) = y(1)*power(cast(fillval,'like',y),cast(iPadLeft,'like',real(y)));        
    end
    if iPadRight > 0
        y(1) = y(1)*power(cast(fillval,'like',y),cast(iPadRight,'like',real(y)));        
    end
elseif coder.const(strcmp(op,'sum'))
    y = combineVectorElements(@plus,x(:),YZERO,ONE,omitnan,vlen,iLeft);
    if iPadLeft > 0
        y(1) = y(1) + cast(fillval,'like',y)*cast(iPadLeft,'like',real(y));
    end
    if iPadRight > 0
        y(1) = y(1) + cast(fillval,'like',y)*cast(iPadRight,'like',real(y));
    end
end

%--------------------------------------------------------------------------
