function varargout = xspectrogram(x,y,varargin)
%XSPECTROGRAM Cross-spectrogram using Short-Time Fourier Transforms (STFT) for the GPU.
%   S = XSPECTROGRAM(X,Y)
%   S = XSPECTROGRAM(X,Y,WINDOW)
%   S = XSPECTROGRAM(X,Y,WINDOW,NOVERLAP)
%   S = XSPECTROGRAM(X,Y,WINDOW,NOVERLAP,NFFT)
%   S = XSPECTROGRAM(X,Y,WINDOW,NOVERLAP,NFFT,Fs)
%   S = XSPECTROGRAM(...,SPECTRUMTYPE)
%   [S,F,T] = XSPECTROGRAM(...)
%   [S,F,T] = XSPECTROGRAM(X,Y,WINDOW,NOVERLAP,F)
%   [S,F,T] = XSPECTROGRAM(X,Y,WINDOW,NOVERLAP,F,Fs)
%   [S,F,T,C] = XSPECTROGRAM(...)
%   [...] = XSPECTROGRAM(...,FREQRANGE)
%   [...] = XSPECTROGRAM(...,'OutputTimeDimension',TIMEDIMENSION)
%   XSPECTROGRAM(...)
%   XSPECTROGRAM(...,FREQLOCATION)
%   
%   LIMITATION:
%   The WINDOW length must not be greater than NFFT or the length of F.
%
%   See also GPUARRAY, SPECTROGRAM, CPSD, MSCOHERE.

%   Copyright 2021-2023 The MathWorks, Inc.

narginchk(2,13);
nargoutchk(0,4);

% Validate input x and y
validateattributes(x,{'single','double'},...
    {'vector','nonempty','nonsparse','nonnan','finite'},'xspectrogram','X',1);
validateattributes(y,{'single','double'},...
    {'vector','nonempty','nonsparse','nonnan','finite'},'xspectrogram','Y',2);

% If the window is provided, it is the third argument. Check if it is a
% gpuArray and non-scalar (only non scalar windows can determine
% gpuArray-ness of output). If the NFFT is provided and it is a vector,
% check if it is a gpuArray (only non scalar NFFT can determine
% gpuArray-ness of output).
isNFFTSecondArg = false;
windowOrNFFTOnGPUAndNonScalar = ...
    signal.internal.gpu.utilities.isWindowNFFTOnGPU(isNFFTSecondArg,varargin{:});

% If the data or the window or the NFFT is not on the GPU, then gather and
% call CPU implementation
if ~isgpuarray(x) && ~isgpuarray(y) && ~windowOrNFFTOnGPUAndNonScalar
    
    % Gather all extra arguments
    [varargin{:}] = gather(varargin{:});

    varargout = cell(1, nargout);
    [varargout{:}] = xspectrogram(x, y, varargin{:});

    return;
end

% Check for unsupported gpuArray inputs
if (nxGreaterThanNFFT(varargin{:}))
    error(message("signal:spectrogram:WindowLengthLargerThanNFFT"));
end

% Ensure both data inputs are on the GPU
x = gpuArray(x);
y = gpuArray(y);

% Legacy parsing - need to convert strings to chars
[varargin{:}] = convertContainedStringsToChars(varargin{:});

% A plot is returned if no output argument is requested
varargout = cell(1, nargout);
[varargout{:}] = pspectrogram({x,y},'xspect',varargin{:});

end

function check = nxGreaterThanNFFT(varargin)
% Nx is derived from the 3rd Input (1st of varargin). NFFT is derived from
% the 5th Input (3rd of varargin).

if numel(varargin) < 3 || ~isnumeric(varargin{1}) || ~isnumeric(varargin{3})
    check = false;
    return;
end

if isscalar(varargin{1})
    nx = varargin{1};
else
    nx = length(varargin{1});
end

if isscalar(varargin{3})
    NFFT = varargin{3};
else
    NFFT = length(varargin{3});
end

% Check NX > NFFT
check = NFFT < nx;
end