function [TabDat,xSVC] = spm_VOI(SPM,xSPM,hReg,xY)
% List of local maxima and adjusted p-values for a small Volume of Interest
% FORMAT [TabDat,xSVC] = spm_VOI(SPM,xSPM,hReg,[xY])
%
% SPM    - Structure containing analysis details (see spm_spm)
%
% xSPM   - Structure containing SPM, distribution & filtering details
%          Required fields are:
% .swd     - SPM working directory - directory containing current SPM.mat
% .Z       - minimum of n Statistics {filtered on u and k}
% .n       - number of conjoint tests
% .STAT    - distribution {Z, T, X or F}
% .df      - degrees of freedom [df{interest}, df{residual}]
% .u       - height threshold
% .k       - extent threshold {resels}
% .XYZ     - location of voxels {voxel coords}
% .XYZmm   - location of voxels {mm}
% .S       - search Volume {voxels}
% .R       - search Volume {resels}
% .FWHM    - smoothness {voxels}
% .M       - voxels -> mm matrix
% .VOX     - voxel dimensions {mm}
% .DIM     - image dimensions {voxels} - column vector
% .Vspm    - mapped statistic image(s)
% .Ps      - uncorrected P values in searched volume (for voxel FDR)
% .Pp      - uncorrected P values of peaks (for peak FDR)
% .Pc      - uncorrected P values of cluster extents (for cluster FDR)
% .uc      - 0.05 critical thresholds for FWEp, FDRp, FWEc, FDRc
%
% hReg   - Handle of results section XYZ registry (see spm_results_ui.m)
% xY     - VOI structure
%
% TabDat - Structure containing table data (see spm_list.m)
% xSVC   - Thresholded xSPM data (see spm_getSPM.m)
%__________________________________________________________________________
%
% spm_VOI is  called by the SPM results section and takes variables in
% SPM to compute p-values corrected for a specified volume of interest.
%
% The volume of interest may be defined as a box or sphere centred on
% the current voxel or by a mask image.
%
% If the VOI is defined by a mask this mask must have been defined
% independently of the SPM (e.g. using a mask based on an orthogonal
% contrast).
%
% External mask images should be in the same orientation as the SPM
% (i.e. as the input used in stats estimation). The VOI is defined by
% voxels with values greater than 0.
%
% See also: spm_list
%__________________________________________________________________________

% Karl Friston
% Copyright (C) 1999-2022 Wellcome Centre for Human Neuroimaging


%-Parse arguments
%--------------------------------------------------------------------------
if nargin < 2, error('Not enough input arguments.'); end
if nargin < 3, hReg = []; end
if nargin < 4, xY = []; end

Num = spm_get_defaults('stats.results.svc.nbmax');   % maxima per cluster
Dis = spm_get_defaults('stats.results.svc.distmin'); % distance among maxima {mm}

%-Title
%--------------------------------------------------------------------------
spm('FigName',['SPM{',xSPM.STAT,'}: Small Volume Correction']);

%-Get current location {mm}
%--------------------------------------------------------------------------
try
    xyzmm  = xY.xyz;
catch
    xyzmm  = spm_results_ui('GetCoords');
end
    
%-Specify search volume
%--------------------------------------------------------------------------
if isfield(xY,'def')
    switch xY.def
        case 'sphere'
            SPACE = 'S';
        case 'box'
            SPACE = 'B';
        case 'mask'
            SPACE = 'I';
        otherwise
            error('Unknown VOI type.');
    end
else
    str    = sprintf(' at [%.0f,%.0f,%.0f]',xyzmm(1),xyzmm(2),xyzmm(3));
    SPACE  = spm_input('Search volume...',-1,'m',...
             {['Sphere',str],['Box',str],'Image'},['S','B','I']);
end

%-Voxels in entire search volume {mm}
%--------------------------------------------------------------------------
XYZmm      = SPM.xVol.M(1:3,:)*[SPM.xVol.XYZ; ones(1, SPM.xVol.S)];
Q          = ones(1,size(xSPM.XYZmm,2));
O          = ones(1,size(     XYZmm,2));
FWHM       = xSPM.FWHM;


switch SPACE

    case 'S' %-Sphere
    %----------------------------------------------------------------------
    if ~isfield(xY,'spec')
        D  = spm_input('radius of VOI {mm}',-2);
    else
        D  = xY.spec;
    end
    str    = sprintf('%0.1fmm sphere',D);
    j      = find(sum((xSPM.XYZmm - xyzmm*Q).^2) <= D^2);
    k      = find(sum((     XYZmm - xyzmm*O).^2) <= D^2);
    D      = D./xSPM.VOX;


    case 'B' %-Box
    %----------------------------------------------------------------------
    if ~isfield(xY,'spec')
        D  = spm_input('box dimensions [k l m] {mm}',-2);
    else
        D  = xY.spec;
    end
    if length(D)~=3, D = ones(1,3)*D(1); end
    str    = sprintf('%0.1f x %0.1f x %0.1f mm box',D(1),D(2),D(3));
    j      = find(all(abs(xSPM.XYZmm - xyzmm*Q) <= D(:)*Q/2));
    k      = find(all(abs(     XYZmm - xyzmm*O) <= D(:)*O/2));
    D      = D./xSPM.VOX;


    case 'I' %-Mask Image
    %----------------------------------------------------------------------
    if ~isfield(xY,'spec')
        [VM,sts] = spm_select([1 Inf],'image','Image defining search volume');
        if ~sts, TabDat = []; xSVC = []; return; end
    else
        VM = xY.spec;
    end
    D      = spm_vol(VM);
    if numel(D) > 1
        fprintf('Computing union of all masks.\n');
        spm_check_orientations(D);
        D2 = struct(...
            'fname',   ['virtual_SVC_mask' spm_file_ext],...
            'dim',     D(1).dim,...
            'dt',      [spm_type('uint8') spm_platform('bigend')],...
            'mat',     D(1).mat,...
            'n',       1,...
            'pinfo',   [1 0 0]',...
            'descrip', 'SVC mask');
        D2.dat     = false(D2.dim);
        for i=1:numel(D)
            D2.dat = D2.dat | spm_read_vols(D(i));
        end
        D2.dat     = uint8(D2.dat);
        D  = D2;
    end
    str    = spm_file(D.fname,'short30');
    str    = regexprep(str, {'\\' '\^' '_' '{' '}'}, ...
        {'\\\\' '\\^' '\\_' '\\{' '\\}'}); % Escape TeX special characters
    str    = sprintf('image mask: %s',str); 
    VOX    = sqrt(sum(D.mat(1:3,1:3).^2));
    FWHM   = FWHM.*(xSPM.VOX./VOX);
    XYZ    = D.mat \ [xSPM.XYZmm; ones(1, size(xSPM.XYZmm, 2))];
    j      = find(spm_sample_vol(D, XYZ(1,:), XYZ(2,:), XYZ(3,:),0) > 0);
    XYZ    = D.mat \ [     XYZmm; ones(1, size(     XYZmm, 2))];
    k      = find(spm_sample_vol(D, XYZ(1,:), XYZ(2,:), XYZ(3,:),0) > 0);

end

xSPM.S     = length(k);
xSPM.R     = spm_resels(FWHM,D,SPACE);
xSPM.Z     = xSPM.Z(j);
xSPM.XYZ   = xSPM.XYZ(:,j);
xSPM.XYZmm = xSPM.XYZmm(:,j);

%-Restrict FDR to the search volume
%--------------------------------------------------------------------------
df         = xSPM.df;
STAT       = xSPM.STAT;
DIM        = xSPM.DIM;
R          = xSPM.R;
n          = xSPM.n;
Vspm       = xSPM.Vspm;
u          = xSPM.u;
S          = xSPM.S;

try, xSPM.Ps = xSPM.Ps(k); end
if STAT ~= 'P'
    [up, xSPM.Pp]     = spm_uc_peakFDR(0.05,df,STAT,R,n,Vspm,k,u);
    uu                = spm_uc(0.05,df,STAT,R,n,S);
end
try % if STAT == 'T'
    V2R               = 1/prod(xSPM.FWHM(DIM>1));
    [uc, xSPM.Pc, ue] = spm_uc_clusterFDR(0.05,df,STAT,R,n,Vspm,k,V2R,u);
catch
    uc                = NaN;
    ue                = NaN;
    xSPM.Pc           = [];
end
try, xSPM.uc          = [uu up ue uc]; end

%-Tabulate p values
%--------------------------------------------------------------------------
str        = sprintf('search volume: %s',str);
if any(strcmp(SPACE,{'S','B'}))
    str = sprintf('%s at [%.0f,%.0f,%.0f]',str,xyzmm(1),xyzmm(2),xyzmm(3));
end

TabDat     = spm_list('List',xSPM,hReg,Num,Dis,str);

if nargout > 1, xSVC = xSPM; end

%-Reset title
%--------------------------------------------------------------------------
spm('FigName',['SPM{',xSPM.STAT,'}: Results']);
