function DCM = spm_dcm_erp_dipfit(DCM, save_vol_sens)
% Prepare structures for ECD forward model (EEG, MEG and LFP)
% FORMAT DCM = spm_dcm_erp_dipfit(DCM, save_vol_sens)
% DCM           - DCM structure
% save_vol_sens - optional argument indicating whether to perform
%                 the time consuming step required for actually using
%                 the forward model to compute lead fields (1, default)
%                 or skip it if the function is only called for
%                 verification of the input (0).
%
% Input DCM structure requires:
%       DCM.xY.Dfile
%       DCM.xY.Ic
%       DCM.Lpos
%       DCM.options.spatial - 'ERP', 'LFP' or 'IMG'
%
% fills in:
%
%       DCM.M.dipfit
%
%    dipfit.location - 0 or 1 for source location priors
%    dipfit.symmetry - 0 or 1 for symmetry constraints on sources
%    dipfit.modality - 'EEG', 'MEG', 'MEGPLANAR' or 'LFP'
%    dipfit.type     - 'ECD', 'LFP' or 'IMG''
%    dipfit.symm     - distance (mm) for symmetry constraints (ECD)
%    dipfit.Lpos     - x,y,z source positions (mm)            (ECD)
%    dipfit.Nm       - number of modes                        (Imaging)
%    dipfit.Ns       - number of sources
%    dipfit.Nc       - number of channels
%
%    dipfit.vol      - volume structure (for M/EEG)
%    dipfit.datareg  - registration structure (for M/EEG)
%__________________________________________________________________________
 
% Karl Friston
% Copyright (C) 2005-2022 Wellcome Centre for Human Neuroimaging

 
% Get data filename and good channels
%--------------------------------------------------------------------------
try
    DCM.xY.Dfile;
    DCM.M.dipfit.Ic = DCM.xY.Ic;
catch
    spm('alert*','Please specify data');
    error('Data not specified.')
end
 
if nargin == 1
  save_vol_sens = 0;
end
    

% D - SPM data structure
%--------------------------------------------------------------------------
try
    D  = spm_eeg_load(DCM.xY.Dfile);
catch
    D  = spm_eeg_load(spm_file(DCM.xY.Dfile,'filename'));
end
 
 
% set options in dipfit
%--------------------------------------------------------------------------
try, spatial  = DCM.options.spatial;  catch, spatial  = 'IMG'; end
try, location = DCM.options.location; catch, location = 0;     end
try, symmetry = DCM.options.symmetry; catch, symmetry = 0;     end

DCM.M.dipfit.type     = spatial;
DCM.M.dipfit.location = location;
DCM.M.dipfit.symmetry = symmetry;


% Get source locations if MEG or EEG
%--------------------------------------------------------------------------
switch DCM.xY.modality
 
    % get source priors for EEG or MEG
    %----------------------------------------------------------------------
    case{'EEG','MEG','MEGPLANAR'}
 
        [D, ok] = check(D, '3d');
        if ~ok
            spm('alert*',{'File not ready for source reconstruction.',...
                'Use Prepare to specify sensors and fiducials.'});
        end
 
        try
            DCM.M.dipfit.Lpos = DCM.Lpos;
        catch
            spm('alert*',{'Please specify source locations','in DCM.Lpos'})
        end
 
        DCM.M.dipfit.modality = DCM.xY.modality;
        DCM.M.dipfit.Ns       = length(DCM.Sname);
        DCM.M.dipfit.Nc       = length(DCM.xY.Ic);
 
        % otherwise assume LFP
        %------------------------------------------------------------------
    otherwise
 
        DCM.M.dipfit.modality = 'LFP';
        DCM.M.dipfit.Ns       = length(DCM.Sname);
        DCM.M.dipfit.Nc       = length(DCM.xY.Ic);
        return
end

% Detect silent sources for non-LFP type from GUI source name text
%--------------------------------------------------------------------------
DCM.M.dipfit.silent_source  = strfind(DCM.Sname,'silent');

% If not LFP, get electromagnetic forward model
%==========================================================================
if ~isfield(D, 'val'), D.val = 1; end

% detects old version of the struct 
%--------------------------------------------------------------------------
if ~isfield(D, 'inv') || ~iscell(D.inv) ||...
        ~(isfield(D.inv{D.val}, 'forward') && isfield(D.inv{D.val}, 'datareg')) ||...
        ~isa(D.inv{D.val}.mesh.tess_ctx, 'char') 
    D = spm_eeg_inv_mesh_ui(D, D.val);
    D = spm_eeg_inv_datareg_ui(D, D.val);
    D = spm_eeg_inv_forward_ui(D, D.val);
    save(D);
end

% fill in dipfit
%--------------------------------------------------------------------------
for m = 1:numel(D.inv{D.val}.forward)
    if strncmp(DCM.xY.modality, D.inv{D.val}.forward(m).modality, 3)
        DCM.M.dipfit.vol      = D.inv{D.val}.forward(m).vol;
        DCM.M.dipfit.datareg  = D.inv{D.val}.datareg(m);
        if isfield(D.inv{D.val}.forward(m), 'siunits') && D.inv{D.val}.forward(m).siunits
            DCM.M.dipfit.sens     = D.inv{D.val}.forward(m).sensors;
            DCM.M.dipfit.siunits  = true;
        else            
            DCM.M.dipfit.sens     = DCM.M.dipfit.datareg.sensors;
            DCM.M.dipfit.siunits  = false;
        end
    end
end


% channels
%--------------------------------------------------------------------------
if save_vol_sens
    if ischar(DCM.M.dipfit.vol)
        DCM.M.dipfit.vol = ft_read_headmodel(DCM.M.dipfit.vol);
    end

    [DCM.M.dipfit.vol, DCM.M.dipfit.sens] = ft_prepare_vol_sens(DCM.M.dipfit.vol, ...
        DCM.M.dipfit.sens, 'channel', D.chanlabels(DCM.xY.Ic));
end

switch DCM.options.spatial
 
    % Imaging (distributed source reconstruction)
    %----------------------------------------------------------------------
    case{'IMG'}
 
        % Load Gain or Lead field matrix
        %------------------------------------------------------------------
        DCM.val = D.val;
        [L,D]   = spm_eeg_lgainmat(D, [], D.chanlabels(DCM.xY.Ic));
        
        % centers
        %------------------------------------------------------------------
        xyz = DCM.M.dipfit.Lpos;
        Np  = size(xyz,2);
 
        % parameters
        %==================================================================
 
        % defaults: Nm = 6; number of modes per region
        %------------------------------------------------------------------
        try, rad  = DCM.M.dipfit.radius; catch, rad  = 16;    end
        try, Nm   = DCM.M.dipfit.Nm;     catch, Nm   = 6;     end
        
 
        % Compute spatial basis (eigenmodes of lead field)
        %==================================================================
 
        % create MSP spatial basis set in source space
        %------------------------------------------------------------------
        vert   = D.inv{D.val}.mesh.tess_mni.vert;
        for i  = 1:Np
            Dp = sum([vert(:,1) - xyz(1,i), ...
                      vert(:,2) - xyz(2,i), ...
                      vert(:,3) - xyz(3,i)].^2,2);
 
            % nearest mesh points
            %--------------------------------------------------------------
            Ip = find(Dp < rad^2);
            if length(Ip) < Nm
                [y,Ip] = sort(Dp);
                Ip     = Ip(1:Nm);
            end
 
            % left hemisphere
            %--------------------------------------------------------------
            U                  = spm_svd(L(:,Ip)',0);
            U                  = U(:,1:Nm);
            DCM.M.dipfit.G{i}  = L(:,Ip)*U;
            DCM.M.dipfit.U{i}  = U;
            DCM.M.dipfit.Ip{i} = Ip;
        end
 
        % Save results
        %==================================================================
        DCM.M.dipfit.radius  = rad;                   % VOI (XYZ, Radius)
        DCM.M.dipfit.Nm      = Nm;                    % modes per region
        DCM.M.dipfit.Nd      = length(vert);          % number of dipoles
        DCM.M.dipfit.gainmat = D.inv{D.val}.gainmat;  % Lead field filename
 
    otherwise
 
end
