function [cfg] = ft_definetrial(cfg)

% FT_DEFINETRIAL defines the trials or segments of data that will be used for further
% processing and analysis, i.e. the pieces of data that will be read in by
% FT_PREPROCESSING. Trials are defined by their begin and end sample in the data file
% and each trial has an offset that defines where the relative t=0 point (usually the
% sample at which the trigger is detected) is for that trial or segment.
%
% Use as
%   [cfg] = ft_definetrial(cfg)
% where the configuration structure should contain
%   cfg.trialdef       = structure with the details of trial definition, see below
%   cfg.trialfun       = string with the function name, see below (default = 'ft_trialfun_general')
%   cfg.representation = 'numeric' or 'table', determines how the trial definition is returned (default is automatic)
% and furthermore
%   cfg.dataset        = string with the filename
% or
%   cfg.headerfile     = string with the filename
%   cfg.datafile       = string with the filename
% and optionally
%   cfg.headerformat   = string, see FT_FILETYPE (default is automatic)
%   cfg.dataformat     = string, see FT_FILETYPE (default is automatic)
%   cfg.eventformat    = string, see FT_FILETYPE (default is automatic)
%
% In general, a call to FT_DEFINETRIAL results in the trial definition "trl" being
% added to the output configuration. The trials are defined on the basis of events or
% triggers by a user-specified MATLAB function that is subsequently referred to as
% the trial function. The user can specify their own custom function tailored to the
% experimental paradigm, or use one of the default trial functions (see below).
%
% Simple trial definitions (for example based on a single trigger) are supported by
% FT_TRIALFUN_GENERAL, which is specified as the default. It supports the following
% options
%   cfg.trialdef.eventtype  = string, or cell-array with strings
%   cfg.trialdef.eventvalue = number, string, or list with numbers or strings
%   cfg.trialdef.prestim    = number, latency in seconds (optional)
%   cfg.trialdef.poststim   = number, latency in seconds (optional)
%
% To read all data from a continuous file in a single or in multiple segments,
% FT_TRIALFUN_GENERAL understands the following options
%    cfg.trialdef.triallength = duration in seconds (can also be 1 or Inf)
%    cfg.trialdef.ntrials     = number of trials (can also be 1 or Inf)
%    cfg.trialdef.overlap     = number between 0 and 1 (exclusive) specifying the fraction of overlap between snippets (0 = no overlap)
%
% To display a list with the events in your data on screen, you can use
% FT_TRIALFUN_SHOW. This is useful for diagnostics; no actual trials will be defined.
%
% To display a graphical user interface dialog that allows you to select events of
% interest, you can use FT_TRIALFUN_GUI.
%
% The trial definition "trl" is an Nx3 matrix or table, where N is the number of
% trials. The first column contains the sample-indices of the start of each trial
% relative to the start of the raw data, the second column contains the sample
% indices of the end of each trial, and the third column contains the offset of the
% trigger with respect to the trial. An offset of 0 means that the first sample of
% the trial corresponds to the trigger. A positive offset indicates that the first
% sample is later than the trigger, and a negative offset indicates that the trial
% begins before the trigger.
%
% Besides the required three columns in the trial definition "trl" that represent
% start, end and offset, it can have contain additional columns . These additional
% columns can be used by a custom trialfun to provide  information about each trial,
% such as trigger codes, response latencies, trial type, and response correctness.
% After FT_PREPROCESSING these additional columns of the "trl" matrix will be
% represented in the "trialinfo" field.
%
% If FT_TRIALFUN_GENERAL or FT_TRIALFUN_GUI has been used to generate the "trl"
% matrix or table, the function may return a fourth column that refers to the
% event-code for the corresponding trial. Whether or not this column is returned
% depends on the acquisition system. In general, this fourth column is generated by
% default if the event codes are represented numerically, or as a string starting
% with 'S' or 'R' (for BrainVision data).
%
% If you need to define the segments of interest on the basis of a conditional
% sequence of events (e.g. stimulus trigger followed by a correct response) or on
% basis of some signal feature that needs to be detected in the data, you should
% supply in cfg.trialfun the name of a function that you wrote yourself and that
% FT_DEFINETRIAL will call. The function receives the cfg structure as input and
% should return a NxM matrix in the same format as "trl" as the output. You can add
% extra custom fields to cfg.trialdef to pass to your own trialfun. See below for
% pointers to some examples.
%
% See also FT_PREPROCESSING, FT_READ_HEADER, FT_READ_EVENT, FT_TRIALFUN_GENERAL,
% FT_TRIALFUN_GUI, FT_TRIALFUN_SHOW, FT_TRIALFUN_BIDS, FT_TRIALFUN_EXAMPLE1,
% FT_TRIALFUN_EXAMPLE2

% Undocumented local options:
% cfg.datafile
% cfg.dataset
% cfg.event
% cfg.trl
% cfg.version

% Copyright (C) 2003-2021, Robert Oostenveld, FCDC
%
% This file is part of FieldTrip, see http://www.fieldtriptoolbox.org
% for the documentation and details.
%
%    FieldTrip is free software: you can redistribute it and/or modify
%    it under the terms of the GNU General Public License as published by
%    the Free Software Foundation, either version 3 of the License, or
%    (at your option) any later version.
%
%    FieldTrip is distributed in the hope that it will be useful,
%    but WITHOUT ANY WARRANTY; without even the implied warranty of
%    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
%    GNU General Public License for more details.
%
%    You should have received a copy of the GNU General Public License
%    along with FieldTrip. If not, see <http://www.gnu.org/licenses/>.
%
% $Id$

% these are used by the ft_preamble/ft_postamble function and scripts
ft_revision = '$Id$';
ft_nargin   = nargin;
ft_nargout  = nargout;

% do the general setup of the function
ft_defaults
ft_preamble init
ft_preamble debug
ft_preamble provenance

% the ft_abort variable is set to true or false in ft_preamble_init
if ft_abort
  return
end

% check if the input cfg is valid for this function
cfg = ft_checkconfig(cfg, 'dataset2files', 'yes');

% this is deprecated as of 26 Sept 2021, it can be removed in a few years
if issubfield(cfg, 'trialdef.eventtype')
  if isequal(cfg.trialdef.eventtype, '?')
    ft_warning('cfg.trialdef.eventtype=''?'' is deprecated, please specify cfg.trialfun=''ft_trialfun_show''');
    cfg.trialfun = 'ft_trialfun_show';
    cfg.trialdef = rmfield(cfg.trialdef, 'eventtype');
  elseif isequal(cfg.trialdef.eventtype, 'gui')
    ft_warning('cfg.trialdef.eventtype=''gui'' is deprecated, please specify cfg.trialfun=''ft_trialfun_gui''');
    cfg.trialfun = 'ft_trialfun_gui';
    cfg.trialdef = rmfield(cfg.trialdef, 'eventtype');
  end
end % default based on eventtype

if issubfield(cfg, 'trialdef.eventvalue')
  if isequal(cfg.trialdef.eventvalue, '?')
    ft_warning('cfg.trialdef.eventvalue=''?'' is deprecated, please specify cfg.trialfun=''ft_trialfun_show''');
    cfg.trialfun = 'ft_trialfun_show';
    cfg.trialdef = rmfield(cfg.trialdef, 'eventvalue');
  elseif isequal(cfg.trialdef.eventvalue, 'gui')
    ft_warning('cfg.trialdef.eventvalue=''gui'' is deprecated, please specify cfg.trialfun=''ft_trialfun_gui''');
    cfg.trialfun = 'ft_trialfun_gui';
    cfg.trialdef = rmfield(cfg.trialdef, 'eventvalue');
  end
end % default based on eventvalue

% set the default options
cfg.trialfun        = ft_getopt(cfg, 'trialfun', 'ft_trialfun_general'); % the default might already be set above
cfg.representation  = ft_getopt(cfg, 'representation'); % this can 'numeric' or 'table'

if isfield(cfg, 'trl')
  % the trial definition is already part of the configuration
  ft_info('retaining existing trial definition\n');
  trl = cfg.trl;
  if isfield(cfg, 'event')
    ft_info('retaining existing event information\n');
    event = cfg.event;
  else
    event = [];
  end
  
else
  % try to locate the trialfun
  trialfunSpecified = cfg.trialfun;
  cfg.trialfun = ft_getuserfun(cfg.trialfun, 'trialfun');
  
  if isempty(cfg.trialfun)
    ft_error('the specified trialfun ''%s'' was not found', trialfunSpecified);
  else
    ft_info('evaluating trial function ''%s''\n', func2str(cfg.trialfun));
  end
  
  % determine the number of outpout arguments of the user-supplied trial function
  try
    % the nargout function in MATLAB 6.5 and older does not work on function handles
    num = nargout(cfg.trialfun);
  catch
    num = 1;
  end
  if num==1
    % the user-defined function only gives back the trial definition
    trl   = feval(cfg.trialfun, cfg);
    event = [];
  else
    % the user-defined function also gives back detailed information about
    % conditions, reaction time or any other information
    [trl, event] = feval(cfg.trialfun, cfg);
  end
  
end % if trl is present or trialfun specified

if size(trl,1)<1
  if isequal(cfg.trialfun, 'ft_trialfun_show') || isequal(cfg.trialfun, str2func('ft_trialfun_show'))
    % give a gentle message instead of an error
    ft_notice('no trials have been defined yet, see FT_DEFINETRIAL for further help\n');
  else
    % give an error in all other cases
    ft_error('no trials were defined, see FT_DEFINETRIAL for help');
  end
elseif size(trl,2) < 3
  ft_error('trl must have at least 3 columns, see FT_DEFINETRIAL for help');
end

% set trl to requested type
if isempty(cfg.representation)
  % keep the format as is
elseif strcmp(cfg.representation, 'numeric') && istable(trl)
  % convert from table to numeric
  trl = table2array(trl);
elseif strcmp(cfg.representation, 'table') && isnumeric(trl)
  % convert the numeric array to a table
  trl = array2table(trl);
  % the 3 first columns are named begsample, endsample and offset, and the rest get default names
  trl.Properties.VariableNames(1:3) = {'begsample', 'endsample', 'offset'};
end

% add the events to the output configuration
if isstruct(event)
  ft_info('found %d events\n', length(event));
  cfg.event = event;
elseif istable(event)
  ft_info('found %d events\n', size(event, 1));
  cfg.event = event;
end

% add the trial definition to the output configuration
if size(trl,1)>0
  ft_info('created %d trials\n', size(trl,1));
  cfg.trl = trl;
end

% do the general cleanup and bookkeeping at the end of the function
ft_postamble provenance
ft_postamble debug
