function [Data, Config, PerformanceCheck] = Code(s, Config)

%General Initializing
Data = struct('TrainingLevel',[],'ITI',[],'TrialStart',[],'LedValue',[],'StimOnTime',[],'StimOffTime',[],'Outcome',[],'TrialCount',[],'TrialEnd',[],'ResponseTime',[],'Sensor',[],'Perseverative',[],'PerseverativeTime',[],'MotorOnTime',[],'MotorNumber',[],'ReactTime',[],'ReactTimeBin',[],'RewardTime',[],'TimeoutAns',[],'TimeoutAnsTime',[],'TotalNumPrematureResponses',[],'TotalNumTimeOut',[],'TotalNumPanelPushesITI',[],'TotalNumPerseverativeResponsesIncorrect',[],'TotalNumPerseverativeResponsesDimmed',[],'TotalNumPerseverativeResponsesCorrect',[],'TotalRuntimeMinutes',[]); %initializes the Data structure
flush(s);
Config.Speaker.LED.Note = 1; %Note C4 (defined in arduino script)
Config.Speaker.Nosepoke.Note = 2; % Note C6 (defined in arduino script)
stop_trigger = 0;
response_trigger = 0;
reward_trigger = 0;
led_trigger = 0;
speaker_trigger = 0;
timeout_trigger = 0;
omission_trigger = 0;
ledReset_trigger = 0;
LoadData = 0;
Config.LED.Values = zeros(Config.Experiment.NumTrials, Config.LED.N);
Config.Staircase.CurrentLevel = Config.Staircase.StartLevel;
start_marker = uint8(60);
stop_marker = uint8(62);
led_reset = uint8([start_marker, 1, 0, 0, 0, 0, 0, stop_marker]); %variable to turn off all light stimuli
trial_count = 0; %trial_count, will be resettet after every level update
i = 0; %total trial count, won't be resetted!
premature_trigger = 0;
tempdata.NumPrematureResponses = 0;
tempdata.NumTimeOut = 0;
tempdata.NumPanelPushesITI = 0;
tempdata.NumPersvCorrect = 0;
tempdata.NumPersvDimmed = 0;
tempdata.NumPersvIncorrect = 0;
RewardCollected = 0;
trial_trigger = 0;
Config_trigger = 0;
end_trigger = 0;
perseverative_trigger = 0;
perseverativeArray = [];
perseverativeTimeArray = [];
timeoutArray = [];
timeoutTimeArray = [];
PerformanceCheck = struct();
PerformanceCheck.NumCorrect = NaN;
PerformanceCheck.NumOmission = NaN;
PerformanceCheck.TotalResponses = NaN;
PerformanceCheck.Accuracy = NaN;
PerformanceCheck.PercentOmission = NaN;
group = Config.Staircase.Group;
Data.TrainingLevel = 0;
KbQueueCreate();
KbQueueStart();
escapeKeyIdx = KbName('esc'); %initializes key to press to end the experiment
firstAnswer = 0;
sensorArray = [];
sensorTimeArray = [];
tStart = datetime('now', 'Format', 'HH:mm:ss:SSS'); %initialization in case 'esc' is pressed before 1st trial
tStop = datetime('now', 'Format', 'HH:mm:ss:SSS'); %initialization in case 'esc' is pressed before 1st trial

%Habituation protocol
if Config.Habituation.Function == 1
    flush(s);
    fprintf('Starting Habituation Protocol... \n');
    tempLEDHighLevel = round(Config.Habituation.LedBrightness * 255); %conversion of configuration from Matlab to Arduino
    Habituation_trigger = true;
    write(s, uint8([start_marker, 0, tempLEDHighLevel, stop_marker]), "uint8") %Habituation
    while Habituation_trigger == 1
        tStop = datetime('now', 'Format', 'HH:mm:ss:SSS'); %at the start of each new trial, retakes the current time
        [~, firstPress] = KbQueueCheck(); %continuosly checks for ESC key
        if (s.NumBytesAvailable > 0)
            Sensor = read(s, 1, "uint8"); %gets number of nose poked aperture and/or flap door
            if Sensor <= 5
                fprintf('Sensor %i. \n', Sensor);
            elseif Sensor == 11
                fprintf('Magazine door opened. \n');
            elseif Sensor == 12
                fprintf('Magazine door closed. \n');
            end
        end
        if firstPress(escapeKeyIdx) %if ESC key was pressed
            tStop = datetime('now', 'Format', 'HH:mm:ss:SSS'); %gets end time
            [~, m, sec] = hms(tStop-tStart);            
            fprintf('\n \n************************************************************** \n');
            fprintf('Escape Key was pressed. \n');
            fprintf('Habituation runtime is %i minutes or %.0f seconds. \n', m, sec);
            prompt = 'Would you like to end the habituation protocol? Y/N: ';
            str = input(prompt,'s');
            fprintf('************************************************************** \n');
            switch str
                case {'n','N'}
                    %do nothing
                case {'y','Y'}
                    write(s, uint8([start_marker, 6, stop_marker]), "uint8") %turns all lights off
                    Habituation_trigger = false;
                    break; %break out of while loop
            end
        end
        if (stop_trigger == 0 && (tStop - tStart) >= duration(0, Config.Habituation.TimeLimit, 0))
            tStop = datetime('now', 'Format', 'HH:mm:ss:SSS'); %gets end time
            [~, m, sec] = hms(tStop-tStart);
            beep;
            fprintf('\n \n************************************************************** \n');
            fprintf('Habituatuon limit was reached. \n');
            fprintf('Habituation runtime is %i minutes or %.0f seconds. \n', m, sec);
            prompt = 'Would you like to end the habituation? Y/N: ';
            str = input(prompt,'s');
            fprintf('************************************************************** \n');
            switch str
                case {'n','N'}
                    stop_trigger = 1; %The limits will be ignored from now on!
                case {'y','Y'}
                    break; %break out of while loop
            end
        end
    end
    Data.TotalRuntimeMinutes = minutes(tStop - tStart); %get duration of habituation protocol
    KbQueueRelease(); %release key check
    return;
end

%Automatically Load Data & set up the experiment
if Config.Experiment.AutoLoad == 1
    cd(Config.Experiment.Directory) %enter given Directory
    if ~isfolder(Config.Experiment.Subject) %if subject folder doesn't exist
        mkdir(Config.Experiment.Subject) %create folder for the subject
        cd(Config.Experiment.Subject)
    else %cd to already existing subject folder
        cd(Config.Experiment.Subject)
        switch Config.Experiment.AutoLoadData
            case 'latest'
                Datadir = dir('*.mat'); %get all mat Files
                dd = zeros(length(Datadir)); %initializing
                for j = 1:length(Datadir) %loop through all files
                    dd(j) = datenum(Datadir(j).date); %get the datenum of all files
                end
                [~, iii] = max(dd); %get the latest file (biggest datenum)
                if ~isempty(iii)
                    oldfilename = Datadir(iii(1, 1)).name; %get the filename
                    PreviousData = load(oldfilename, 'Data'); %load previous data
                    if isfield(PreviousData,'Data')
                        LoadData = 1; %Previous data is available
                    end
                end
            otherwise %take exact dataset
                ending = '.mat'; %only workspace data!
                filename = strcat(Config.Experiment.AutoLoadData, ending); %concatenate the strings
                if isfile(filename)
                    PreviousData = load(filename, 'Data'); %load Data from previous file
                else
                    error('No such file or directory.');
                end
                LoadData = 1; %Previous data is available
        end
    end
    if LoadData == 1
        idxLvl = max([PreviousData.Data.TrainingLevel]); %find max level used last session
        trialsDone = sum([PreviousData.Data.TrainingLevel] == idxLvl); %find total number of trials with that max level
        if ischar(Config.Experiment.NumCalculatedTrials) %if 'all', min trials to do in current day is the set to Config.Experiment.MinNumTrials
            trialsToDo = Config.Experiment.MinNumTrials;
        else
            trialsToDo = Config.Experiment.NumCalculatedTrials - trialsDone; %calculate how many trials needs to be done before updating level
        end
        if trialsToDo < Config.Experiment.MinNumTrials
            trialsToDo = Config.Experiment.MinNumTrials; %In current experiment enough trials will be made before updating
        end
        tempIdx = find([PreviousData.Data.TrainingLevel] == idxLvl); %get indices for the trials done in that max level
        tempStruct = [PreviousData.Data(tempIdx).Outcome]; %get the outcome of those trials
        fprintf('Number of Correct Responses in previous session: %i. \n', sum([tempStruct == 1])); %sum the number of correct answers
        Config.Staircase.StartLevel = idxLvl; %automatically updates the start level to that max level
        Config.Staircase.CurrentLevel = Config.Staircase.StartLevel; %automatically updates the current level to that max level
    end
end

if (Config.Staircase.Function == 1 && Config.Staircase.StartLevel ~= 0) || (LoadData == 1) %If startlevel was chosen or if level should be updated based on last experiment
    Config_trigger = 2; %so that update in staircase function happens without a performance check
    [Config, trial_count, Config_trigger, ~] = Staircase(Config, PerformanceCheck, trial_count, Config_trigger, group);
    Config_trigger = 0;
end

while i >= trial_count
    flush(s);
    tStop = datetime('now', 'Format', 'HH:mm:ss:SSS'); %at the start of each new trial, retakes the current time
    [~, m, sec] = hms(tStop-tStart); %get the duration

    [~, firstPress] = KbQueueCheck(); %Before each Trial, check for Key Press
    if firstPress(escapeKeyIdx) %if escape key was pressed
        tStop = datetime('now', 'Format', 'HH:mm:ss:SSS');
        [~, m, sec] = hms(tStop-tStart);
        fprintf('\n \n************************************************************** \n');
        fprintf('Escape Key was pressed. \n');
        fprintf('Number of completed trials: %i \n', i);
        fprintf('Experiment runtime is %i minutes or %.0f seconds. \n', m, sec);
        prompt = 'Would you like to end the experiment? Y/N: ';
        str = input(prompt,'s');
        fprintf('************************************************************** \n');
        switch str
            case {'n','N'} %do nothing
            case {'y','Y'}
                break; %break out of while loop
        end
    end

    if (Config.Experiment.PerfCheck == 1) %Performance Check
        idx = find([Data.TrainingLevel] == Config.Staircase.CurrentLevel); %index in data pertaining to current level
        if (i ~= 0) && (premature_trigger == 0) && (mod(sum([Data.Outcome] ~= 4), Config.Experiment.PerfCheckAfterNumTrial) == 0) %after every PerfCheckAfterNumTrial
            if (LoadData == 1) && (length(idx) >= trialsToDo) %if old data was uploaded and TrialsToDo in current session are done
                tempPerfCheck = [tempStruct, [Data(idx).Outcome]]; %merge old outcome with current outcome in an array
                if ischar(Config.Experiment.NumCalculatedTrials) %disregard NumCalculatedTrials and take all outcomes of both days
                    PerformanceCheck.NumCorrect = sum(tempPerfCheck == 1);
                    PerformanceCheck.NumOmission = sum(tempPerfCheck == 0);
                    PerformanceCheck.TotalResponses = sum(tempPerfCheck == 1) + sum(tempPerfCheck == 3);
                    PerformanceCheck.Accuracy = PerformanceCheck.NumCorrect / PerformanceCheck.TotalResponses * 100;
                    PerformanceCheck.PercentOmission = PerformanceCheck.NumOmission / (PerformanceCheck.TotalResponses + PerformanceCheck.NumOmission) * 100;
                    fprintf('Total Number of Correct Responses of both days: %i. \n', PerformanceCheck.NumCorrect);
                    fprintf('Total Accuracy percentage of both days: %0.2f. \n', PerformanceCheck.Accuracy);
                    fprintf('Total Omission percentage of both days: %0.2f. \n', PerformanceCheck.PercentOmission);
                elseif ~ischar(Config.Experiment.NumCalculatedTrials) && (length(tempPerfCheck) >= Config.Experiment.NumCalculatedTrials) %outcomes set by NumCalculatedTrials
                    PerformanceCheck.NumCorrect = sum(tempPerfCheck(end -(Config.Experiment.NumCalculatedTrials - 1):end) == 1);
                    PerformanceCheck.NumOmission = sum(tempPerfCheck(end -(Config.Experiment.NumCalculatedTrials - 1):end) == 0);
                    PerformanceCheck.TotalResponses = sum(tempPerfCheck(end -(Config.Experiment.NumCalculatedTrials - 1):end) == 1) + sum(tempPerfCheck(end -(Config.Experiment.NumCalculatedTrials - 1):end) == 3); %here: add dimmed responses
                    PerformanceCheck.Accuracy = PerformanceCheck.NumCorrect / PerformanceCheck.TotalResponses * 100;
                    PerformanceCheck.PercentOmission = PerformanceCheck.NumOmission / (PerformanceCheck.TotalResponses + PerformanceCheck.NumOmission) * 100;
                    fprintf('Total Number of Correct Responses of both days: %i. \n', PerformanceCheck.NumCorrect);
                    fprintf('Total Accuracy percentage of both days: %0.2f. \n', PerformanceCheck.Accuracy);
                    fprintf('Total Omission percentage of both days: %0.2f. \n', PerformanceCheck.PercentOmission);
                end
            elseif ischar(Config.Experiment.NumCalculatedTrials) && (LoadData == 0) %no data was uploaded, take all outcomes
                PerformanceCheck.NumCorrect = sum([Data(idx).Outcome] == 1);
                PerformanceCheck.NumOmission = sum([Data(idx).Outcome] == 0);
                PerformanceCheck.TotalResponses = sum([Data(idx).Outcome] == 1) + sum([Data(idx).Outcome] == 3);
                PerformanceCheck.Accuracy = PerformanceCheck.NumCorrect / PerformanceCheck.TotalResponses * 100;
                PerformanceCheck.PercentOmission = PerformanceCheck.NumOmission / (PerformanceCheck.TotalResponses + PerformanceCheck.NumOmission) * 100;
                fprintf('Total Number of Correct Responses: %i. \n', PerformanceCheck.NumCorrect);
                fprintf('Total Accuracy percentage: %0.2f. \n', PerformanceCheck.Accuracy);
                fprintf('Total Omission percentage: %0.2f. \n', PerformanceCheck.PercentOmission);
            elseif ~ischar(Config.Experiment.NumCalculatedTrials) && (LoadData == 0) && (length(Data(idx)) >= Config.Experiment.NumCalculatedTrials) %no data was uploaded, outcomes set by NumCalculatedTrials
                PerformanceCheck.NumCorrect = sum([Data(idx(end -(Config.Experiment.NumCalculatedTrials - 1):end)).Outcome] == 1);
                PerformanceCheck.NumOmission = sum([Data(idx(end -(Config.Experiment.NumCalculatedTrials - 1):end)).Outcome] == 0);
                PerformanceCheck.TotalResponses = sum([Data(idx(end -(Config.Experiment.NumCalculatedTrials - 1):end)).Outcome] == 1) + sum([Data(idx(end -(Config.Experiment.NumCalculatedTrials - 1):end)).Outcome] == 3); %here: add dimmed responses
                PerformanceCheck.Accuracy = PerformanceCheck.NumCorrect / PerformanceCheck.TotalResponses * 100;
                PerformanceCheck.PercentOmission = PerformanceCheck.NumOmission / (PerformanceCheck.TotalResponses + PerformanceCheck.NumOmission) * 100;
                fprintf('Total Number of Correct Responses: %i. \n', PerformanceCheck.NumCorrect);
                fprintf('Total Accuracy percentage: %0.2f. \n', PerformanceCheck.Accuracy);
                fprintf('Total Omission percentage: %0.2f. \n', PerformanceCheck.PercentOmission);
            end
            if (Config.Staircase.Function == 1) %calls the Staircase function
                if Config.Staircase.CurrentLevel < Config.Staircase.NumLevels %there are more levels than the current one
                    [Config, trial_count, Config_trigger, UpdateTrigger] = Staircase(Config, PerformanceCheck, trial_count, Config_trigger, group);
                    if (LoadData == 1) && (UpdateTrigger == 1) %if the current level was updated using uploaded data
                        LoadData = 0; %make sure the uploaded data won't be included anymore
                    end
                end
            end
        end
    end
    
    if (i ~= 0) && (stop_trigger == 0) && (Config.Experiment.ExperimentLimitTrigger == 1) %checks if Experiment limits were surpassed
        if Config.Trial.RedoPremature == 0 %premature answers count as trials
            if ((i >= Config.Experiment.NumTrials) && (premature_trigger == 0)) || ((tStop - tStart) >= duration(0, Config.Experiment.TimeLimit, 0))
                [~, m, sec] = hms(tStop-tStart);
                beep;
                fprintf('\n \n************************************************************** \n');
                fprintf('Experiment limit was reached. \n');
                fprintf('Number of completed trials: %i \n', i);
                fprintf('Experiment runtime is %i minutes or %.0f seconds. \n', m, sec);
                prompt = 'Would you like to end the experiment? Y/N: ';
                str = input(prompt,'s');
                fprintf('************************************************************** \n');
                switch str
                    case {'n','N'}
                        stop_trigger = 1; %The limits will be ignored from now on!
                    case {'y','Y'}
                        break; %break out of while loop
                end
            end
        elseif Config.Trial.RedoPremature == 1 %premature answers don't count as trials
            if (sum([Data.Outcome] ~= 4) >= Config.Experiment.NumTrials) && (premature_trigger == 0) || ((tStop - tStart) >= duration(0, Config.Experiment.TimeLimit, 0))
                [~, m, sec] = hms(tStop-tStart);
                beep;
                fprintf('\n \n************************************************************** \n');
                fprintf('Experiment limit was reached. \n');
                fprintf('Number of completed trials: %i \n', (sum([Data.Outcome] == 0 | [Data.Outcome] == 1 | [Data.Outcome] == 2 | [Data.Outcome] == 3)));
                fprintf('Experiment runtime is %i minutes or %.0f seconds. \n', m, sec);
                prompt = 'Would you like to end the experiment? Y/N: ';
                str = input(prompt,'s');
                fprintf('************************************************************** \n');
                switch str
                    case {'n','N'}
                        stop_trigger = 1; %The limits will be ignored from now on!
                    case {'y','Y'}
                        break; %break out of while loop
                end
            end
        end
    end

    i = i + 1; %update i for next row in Data
    trial_count = trial_count + 1;
    if premature_trigger == 1
       premature_trigger = 0; %resettet trigger
    end
    
    if Config.Speaker.LED.System == 1
        tempSpeakerLEDVolume = round(Config.Speaker.LED.Volume*255); %conversion of configuration from Matlab to Arduino
    end
    if Config.Speaker.Nosepoke.System == 1
        tempSpeakerNosepokeVolume = round(Config.Speaker.Nosepoke.Volume*255);%conversion of configuration from Matlab to Arduino
    end
    tempLEDHighLevel = round(Config.LED.HighLevel * 255); %conversion of configuration from Matlab to Arduino
    tempLEDDimmedLevel = round(Config.LED.DimmedLevel * 255); %conversion of configuration from Matlab to Arduino

    if (i == 1) && (tempdata.NumTimeOut == 0) %initializing the 1st trial, descriminates against repeated 1st trial (case of premature responses)
        tStart = datetime('now', 'Format', 'HH:mm:ss:SSS'); %retakes start time
        tStop = datetime('now', 'Format', 'HH:mm:ss:SSS'); %retakes stop time
        Config_trigger = 1; %set a trigger to know when to shuffle the LEDs
        if (Config.Trial.FreePellet == 1) %free pellet is wanted
            write(s, uint8([start_marker, 2, stop_marker]), "uint8") %turn Foodlights on
            write(s, uint8([start_marker, 3, Config.Reward.CorrectNumPellet, Config.Reward.CorrectFeeder, stop_marker]), "uint8"); %turn motor on
            flush(s);
            while (RewardCollected == 0) %wait until reward is collected
                tStop = datetime('now', 'Format', 'HH:mm:ss:SSS'); %keeps retaking the time
                if (s.NumBytesAvailable > 0) %if Arduino sends some information
                    Sensor = read(s, 1, "uint8"); %get one value as the aperture number
                    if Sensor == 12 %if food magazine flap door was closed
                        write(s, uint8([start_marker, 2, stop_marker]), "uint8") %turn Foodlights off
                        RewardCollected = 1; %trigger for reward collected
                        Sensor = 0; %delete the aperture number
                    end
                elseif (stop_trigger == 0) && (tStop - tStart) >= duration(0, Config.Experiment.TimeLimit, 0) %checks if Experiment limits were surpassed
                    [~, m, sec] = hms(tStop-tStart);
                   beep;
                    fprintf('\n \n************************************************************** \n');
                    fprintf('Experiment limit was reached. \n');
                    fprintf('Number of completed trials: %i \n', i);
                    fprintf('Experiment runtime is %i minutes or %.0f seconds. \n', m, sec);
                    prompt = 'Would you like to end the experiment? Y/N: ';
                    str = input(prompt,'s');
                    fprintf('************************************************************** \n');
                    switch str
                        case {'n','N'}
                            stop_trigger = 1; %The limits will be ignored from now on!
                        case {'y','Y'}
                            write(s, uint8([start_marker, 2, stop_marker]), "uint8") %turn Foodlights off since it was on awaiting for magazine entry
                            end_trigger = 1;
                            break;
                    end
                end
            end
        end
    else
        tStop = datetime('now', 'Format', 'HH:mm:ss:SSS'); %checks how long the experiment runs
    end

    if end_trigger == 1  %checks if experiment needs to be prematurely over (subject doesn't get the free pellet)
       break;
    end

    while trial_trigger == 0  %begin trial
        if (Config.Trial.MagazineEntry == 1)
            if (RewardCollected == 1) %checks if magazine entry was already made (either as free pellet or reward acquisition after last trial)
                fprintf('Trial start. \n');
                fprintf('Magazine Entry done. \n');
                Data(i).TrialStart = datetime('now', 'Format', 'HH:mm:ss:SSS'); %get time of trial start
                fprintf('ITI Start. \n')
                ITI_trigger = 1;
                RewardCollected = 0;
                trial_trigger = 1;
                break;
            else
                write(s, uint8([start_marker, 2, stop_marker]), "uint8") %turn Foodlights on after Time Out
                fprintf('Awaiting Magazine Entry. \n');
                while (RewardCollected == 0)
                    tStop = datetime('now', 'Format', 'HH:mm:ss:SSS');
                    if (s.NumBytesAvailable > 0) %receives Info from Arduino
                        Sensor = read(s, 1, "uint8"); %get aperture number
                        if Sensor <= 5 %one of the 5 apertures
                            fprintf('Sensor %i. \n', Sensor);
                        elseif Sensor == 11
                            fprintf('Magazine foor opened. \n');
                        elseif Sensor == 12 %food magazine flap door was pushed and closed
                            write(s, uint8([start_marker, 2, stop_marker]), "uint8") %turn Foodlights off after magazine entry
                            trial_trigger = 1;
                            fprintf('Trial start. \n');
                            Data(i).TrialStart = datetime('now', 'Format', 'HH:mm:ss:SSS');
                            fprintf('ITI Start. \n')
                            Sensor = 0;
                            ITI_trigger = 1;
                            break;
                        end
                    elseif (stop_trigger == 0) && (tStop - tStart) >= duration(0, Config.Experiment.TimeLimit, 0) %checks if Experiment limits were surpassed
                        [~, m, sec] = hms(tStop-tStart);
                        beep;
                        fprintf('\n \n************************************************************** \n');
                        fprintf('Experiment limit was reached. \n');
                        fprintf('Number of completed trials: %i \n', i);
                        fprintf('Experiment runtime is %i minutes or %.0f seconds. \n', m, sec);
                        prompt = 'Would you like to end the experiment? Y/N: ';
                        str = input(prompt,'s');
                        fprintf('************************************************************** \n');
                        switch str
                            case {'n','N'}
                                stop_trigger = 1; %The limits will be ignored from now on!
                            case {'y','Y'}
                                write(s, uint8([start_marker, 2, stop_marker]), "uint8") %turn Foodlights off after magazine entry
                                end_trigger = 1;
                                trial_trigger = 1;
                                break;
                        end
                    end
                end
            end
        else
            fprintf('Trial start. \n');
            Data(i).TrialStart = datetime('now', 'Format', 'HH:mm:ss:SSS');
            fprintf('ITI Start. \n')
            ITI_trigger = 1;
            trial_trigger = 1;
            break;
        end
    end

    if end_trigger == 1  %checks if experiment needs to be prematurely over (subject doesn't do magazine entry)
       break;
    end
    
    switch Config.LED.StimSequence %LED Configuration
        case 'random' %LED sequence will be randomly chosen
            if (i == 1) && (Config_trigger == 1) %only shuffles the sequence when it's the first trial (disregards any premature responses)
                Config.LED.sequence = randi([1, Config.LED.N], 1, round(Config.Experiment.NumTrials/Config.LED.N)); %random led sequence from 1 to 5
                Config_trigger = 0; %LEDs won't be shuffled again (case of more premature responses)
            elseif (mod(i - 1, round(Config.Experiment.NumTrials/Config.LED.N)) == 0) && (i ~= 1) %calculate new sequence for each multiples of N trials
                temp_led_sequence = randi([1, Config.LED.N], 1, round(Config.Experiment.NumTrials/Config.LED.N)); %new sequence
                Config.LED.sequence = cat(2, Config.LED.sequence, temp_led_sequence); %concatenate both strings and add to LED sequence
                %fprintf('New LED sequence randomized. \n'); %optional, to let the user know
            end
        case 'pseudorandom' %LED sequence takes into account that each aperture lights up with the same frequency
            if (i == 1) && (Config_trigger == 1) %only shuffles the sequence when it's the first trial (disregards any premature responses)
                %Config.LED.sequence = repmat(1:Config.LED.N, 1,round(Config.Experiment.NumTrials/(Config.LED.N)^2));
                Config.LED.sequence = repmat(1:Config.LED.N, 1,round(Config.Experiment.NumTrials/Config.LED.N));
                Config.LED.sequence = Config.LED.sequence(randperm(length(Config.LED.sequence))); %randomize
                temp_original_LED_sequence = Config.LED.sequence; %save original sequence in order to randomize it again later
                Config_trigger = 0; %LEDs won't be shuffled again (case of more premature responses)
            elseif (mod(i - 1, round(Config.Experiment.NumTrials/Config.LED.N)) == 0) && (i ~= 1) %calculate new sequence for each multiples of N trials
                temp_led_sequence = temp_original_LED_sequence(randperm(length(temp_original_LED_sequence))); %new sequence
                Config.LED.sequence = cat(2, Config.LED.sequence, temp_led_sequence); %concatenate both strings and add to LED sequence
                %fprintf('New LED sequence randomized \n'); %optional, to let the user know
            end
    end
    switch Config.LED.StimType %what LEDs are on/dimmed
        case 'single' %one LED bright, rest off
            Config.LED.Values(i, Config.LED.sequence(i)) = tempLEDHighLevel;
        case 'neighbour' %one or more LEDS high, one or more neighbouring LEDs dimmed
            Config.LED.Values(i, Config.LED.sequence(i)) = tempLEDHighLevel;
            if Config.LED.NumHighLED > 1 %more than one neighbouring bright LED
                if Config.LED.sequence(i) == 1
                    Config.LED.Values(i, Config.LED.sequence(i)+(1:Config.LED.NumHighLED-1)) = tempLEDHighLevel;
                elseif Config.LED.sequence(i) == Config.LED.N %surpresses creating new elements
                    Config.LED.Values(i, Config.LED.sequence(i)-(1:Config.LED.NumHighLED-1)) = tempLEDHighLevel;
                else
                    Config.LED.Values(i, Config.LED.sequence(i)-(1:((Config.LED.NumHighLED-1)/2))) = tempLEDHighLevel;
                    Config.LED.Values(i, Config.LED.sequence(i)+(1:((Config.LED.NumHighLED-1)/2))) = tempLEDHighLevel;
                end
                if Config.LED.NumDimmedLED == (Config.LED.N-Config.LED.NumHighLED) %replace all empty indexes with dimmed level
                    Config.LED.Values(i,find(~Config.LED.Values(i,:))) = tempLEDDimmedLevel; %#ok<FNDSB>
                elseif Config.LED.NumDimmedLED < (Config.LED.N-Config.LED.NumHighLED)
                    idxx = find(Config.LED.Values(i,:)); %find indexes of the non-zero elements
                    Config.LED.Values(i, idxx(1)-(1:(Config.LED.NumDimmedLED/2))) = tempLEDDimmedLevel;
                    Config.LED.Values(i, idxx(end)+(1:(Config.LED.NumDimmedLED/2))) = tempLEDDimmedLevel;
                end
            elseif Config.LED.NumHighLED == 1 %only one bright LED
                if Config.LED.sequence(i) == 1
                    Config.LED.Values(i, Config.LED.sequence(i)+(1:Config.LED.NumDimmedLED)) = tempLEDDimmedLevel;
                elseif Config.LED.sequence(i) == Config.LED.N %surpresses creating new elements
                    Config.LED.Values(i, Config.LED.sequence(i)-(1:Config.LED.NumDimmedLED)) = tempLEDDimmedLevel;
                else
                    Config.LED.Values(i, Config.LED.sequence(i)-(1:(Config.LED.NumDimmedLED/2))) = tempLEDDimmedLevel;
                    Config.LED.Values(i, Config.LED.sequence(i)+(1:(Config.LED.NumDimmedLED/2))) = tempLEDDimmedLevel;
                end
            end
        case 'shifted' %one high, variable number of neighbours dimmed (NOTE: distribution will be shifted!), rest off
            Config.LED.Values(i, Config.LED.sequence(i)) = tempLEDHighLevel; %chosen LED as bright
            middle = ceil(Config.LED.N/2); %get index for middle position
            difpos = Config.LED.sequence(i)-middle; %get difference between the random i and middle index
            shiftpos = Config.LED.N-difpos; %calculate amount of shifting
            Config.LED.Values(i,:) = circshift(Config.LED.Values(i,:),shiftpos); %shift
            Config.LED.Values(i,(middle+(1:Config.LED.NumDimmedLED/2))) = tempLEDDimmedLevel; %set half of the dimmed leds
            Config.LED.Values(i,(middle-(1:Config.LED.NumDimmedLED/2))) = tempLEDDimmedLevel; %set other half of the dimmed leds
            Config.LED.Values(i,:) = circshift(Config.LED.Values(i,:),-shiftpos); %shift back to original position
        case 'all' %all LEDs are bright
            Config.LED.Values(i,:) = tempLEDHighLevel;
        otherwise
            error('LED not defined.')
    end
    Data(i).LedValue = Config.LED.Values(i-tempdata.NumPrematureResponses, :); %save LED values of the trial in Data
    Data(i).LedValue = round(Data(i).LedValue/255,2);
    
    switch Config.Trial.ITIType %ITI Configuration
        case 'defined' %set time
            ITI_RandomTime = 0;
        case 'random' %randomized
            ITI_RandomTime = Config.Trial.ITIRandomInterval(1) + (Config.Trial.ITIRandomInterval(2) - Config.Trial.ITIRandomInterval(1)) * rand(1, 1);
    end
    ITI = Config.Trial.ITIFixedTime + ITI_RandomTime; %random number in interval added to set time
    Data(i).ITI = ITI; %saves the ITI of the trial

    while ITI_trigger == 1 %start ITI
        count_ITI = datetime('now', 'Format', 'HH:mm:ss:SSS');
        if (count_ITI-Data(i).TrialStart) <= duration(0,0,Data(i).ITI) %while ITI is running
            if (s.NumBytesAvailable > 0)
                Sensor = read(s, 1, "uint8"); %get any premature answer
                if Sensor == 12 %flap door doesn't count as premature
                    tempdata.NumPanelPushesITI = tempdata.NumPanelPushesITI + 1;
                elseif Sensor <= 5 %premature response in an aperture
                    fprintf('Premature Sensor %i. \n', Sensor)
                    Data(i).ResponseTime = datetime('now', 'Format', 'HH:mm:ss:SSS');
                    Data(i).Outcome = 4;
                    Data(i).Sensor = Sensor; %save the aperture number
                    tempdata.NumPrematureResponses = tempdata.NumPrematureResponses + 1; %count premature answers in current session
                    Sensor = 0;
                    ITI_trigger = 0;
                    timeout_trigger = 1;
                    premature_trigger = 1;
                end
            end
            if (milliseconds(count_ITI-Data(i).TrialStart) >= milliseconds(duration(0,0,Data(i).ITI-(Config.Speaker.LED.TimeWindow/1000)))) && (Config.Speaker.LED.System == 1) && speaker_trigger == 0
                write(s, uint8([start_marker, 6, tempSpeakerLEDVolume, Config.Speaker.LED.Note, Config.Speaker.LED.Duration, stop_marker]), "uint8") %Resistance, Note, Duration
                speaker_trigger = 1;
                continue;
            end
        elseif (count_ITI-Data(i).TrialStart) > duration(0,0,Data(i).ITI) %ITI is over
            led_trigger = 1;
            ITI_trigger = 0;
            speaker_trigger = 0;
        end
    end

    if led_trigger == 1 %Stimulus is turned on
        led_arduino = uint8([start_marker, 1, [Config.LED.Values(i-tempdata.NumPrematureResponses,:)] , stop_marker]);
        write(s, led_arduino, "uint8");
        fprintf(' %s\n',num2str(Data(i).LedValue)); %optional, for the user to see what LEDs are on
        Data(i).StimOnTime = datetime('now', 'Format', 'HH:mm:ss:SSS'); %get time when Leds light up
        led_trigger = 0;
        while omission_trigger == 0 %wait for nose poke
            currentTime = datetime('now', 'Format', 'HH:mm:ss:SSS');
            if currentTime-Data(i).StimOnTime <= duration(0,0,Config.LED.StimDuration + Config.Trial.LimitedHold) %while stimulus duration and limited hold is running
                if (s.NumBytesAvailable > 0) %get any nose pokes
                    Sensor = read(s, 1, "uint8");
                    if Sensor <= 5
                        Data(i).Sensor = Sensor; %save the aperture number
                        Data(i).ResponseTime = datetime('now', 'Format', 'HH:mm:ss:SSS'); %save the response time
                        if ledReset_trigger == 0 %not to overwrite the time taken in Limited Hold
                            write(s, led_reset, "uint8"); %resets the leds when a choice was made
                            Data(i).StimOffTime = datetime('now', 'Format', 'HH:mm:ss:SSS'); %get the time when Leds are turned off
                        end
                        response_trigger = 1;
                        fprintf('Response Sensor %i. \n', Sensor)
                        Sensor = 0;
                        ledReset_trigger = 0; %needed for Limited hold in next trial
                        perseverative_trigger = 1;
                        break;
                    end
                elseif (currentTime-Data(i).StimOnTime >= duration(0,0,Config.LED.StimDuration)) && (ledReset_trigger == 0) %Stimulus duration is over
                    write(s, led_reset, "uint8"); %resettet die LEDs
                    Data(i).StimOffTime = datetime('now', 'Format', 'HH:mm:ss:SSS');
                    ledReset_trigger = 1; %so that the time of the leds going out doesn't get rewritten
                    fprintf('Limited Hold. \n');
                    continue;
                end
            elseif currentTime-Data(i).StimOnTime > duration(0,0,Config.LED.StimDuration + Config.Trial.LimitedHold) %Limited Hold is over
                ledReset_trigger = 0;
                omission_trigger = 1;
            end
        end
    end

    if omission_trigger == 1 %Check for omission
        Data(i).Outcome = 0;
        fprintf('Omission. \n')
        timeout_trigger = 1;
        omission_trigger = 0;
    end

    if ~ischar(Config.Trial.PerseverativeTime) %if perseverative Time Window was defined
        while perseverative_trigger == 1
            count_persv = datetime('now', 'Format', 'HH:mm:ss:SSS');
            if (s.NumBytesAvailable > 0)
                Sensor = read(s, 1, "uint8");
                Data(i).Perseverative = {Sensor};
%                 time = datetime('now', 'Format', 'HH:mm:ss:SSS');
%                 Data(5).PerseverativeTimeExcel = cellstr(time)
                Data(i).PerseverativeTime = {datetime('now', 'Format', 'HH:mm:ss:SSS')}; %get the time of the perseverative answer
                if Config.LED.Values(i-tempdata.NumPrematureResponses, Data(i).Sensor) == tempLEDHighLevel %perseverative in correct aperture
                    tempdata.NumPersvCorrect = tempdata.NumPersvCorrect + 1;
                elseif Config.LED.Values(i-tempdata.NumPrematureResponses, Data(i).Sensor) == tempLEDDimmedLevel %perseverative in dimmed aperture
                    tempdata.NumPersvDimmed = tempdata.NumPersvDimmed + 1;
                else
                    tempdata.NumPersvIncorrect = tempdata.NumPersvIncorrect + 1; %perseverative in incorrect aperture
                end
                fprintf('Perseverative Sensor %i. \n',Sensor);
                if Config.Trial.PerseverativeTimeout == 1 %perseverative answers calls for a time out
                    response_trigger = 0; %don't reward
                    timeout_trigger = 1;
                    perseverative_trigger = 0;
                    break;
                else
                    perseverative_trigger = 0; %no time out
                    break;
                end
            elseif (seconds(count_persv - Data(i).ResponseTime) >= Config.Trial.PerseverativeTime) %perseverative time window is over
                perseverative_trigger = 0;
                Sensor = 0;
                break;
            end
        end
    end

    if response_trigger == 1 %Start with reward 
        if (Data(i).Sensor ~= 0) && (Data(i).Sensor < 6)
            switch Config.Reward.System
                case 'binary' %only reward bright LED
                    switch Config.Reward.RTDependence
                        case 'dependent' %Reward is dependent on Reaction Time
                            bin = 0;
                            ii = 0;
                            ReactTime = seconds(Data(i).ResponseTime-Data(i).StimOnTime); %get the Reaction Time
                            if Config.LED.Values(i-tempdata.NumPrematureResponses, Data(i).Sensor) == tempLEDHighLevel %bright aperture was nose poked
                                for ii = 1:length(Config.Reward.CorrectReactionTimeBins) %go through bin configurated in User script
                                    if ReactTime <= Config.Reward.CorrectReactionTimeBins(1, ii) %reaction time is smaller/equal to one of the bins
                                        bin = ii; %save corresponding bin
                                        break;
                                    elseif ReactTime > Config.Reward.CorrectReactionTimeBins(1, length(Config.Reward.CorrectReactionTimeBins)) %last bin
                                        bin = length(Config.Reward.CorrectReactionTimeBins); %save corresponding bin
                                    else %reaction time is bigger than the current bin
                                        continue; %next bin
                                    end
                                end
                                if Config.Speaker.Nosepoke.System == 1
                                   write(s, uint8([start_marker, 6, tempSpeakerNosepokeVolume, Config.Speaker.Nosepoke.Note, Config.Speaker.Nosepoke.Duration, stop_marker]), "uint8") %Speaker
                                end
                                write(s, uint8([start_marker, 2, stop_marker]), "uint8") %turn Foodlights on
                                write(s, uint8([start_marker, 3, Config.Reward.CorrectReactionTimeBins(3, bin), Config.Reward.CorrectReactionTimeBins(2, bin), stop_marker]), "uint8") %turn motor on with bin configuration
                                Data(i).MotorOnTime = datetime('now', 'Format', 'HH:mm:ss:SSS');
                                Data(i).MotorNumber = Config.Reward.CorrectReactionTimeBins(2, bin);
                                Data(i).ReactTime = ReactTime;
                                Data(i).ReactTimeBin = bin;
                                Data(i).Outcome = 1;
                                reward_trigger = 1;
                            elseif Config.LED.Values(i-tempdata.NumPrematureResponses, Data(i).Sensor) == tempLEDDimmedLevel %nose poke in dimmed aperture but in binary configuration
                                timeout_trigger = 1;
                                Data(i).Outcome = 3;
                            elseif Config.LED.Values(i-tempdata.NumPrematureResponses, Data(i).Sensor) == 0 %nose poke in wrong aperture
                                timeout_trigger = 1;
                                Data(i).Outcome = 3;
                            end
                        case 'non-dependent' %reward is not dependent on reaction time
                            if Config.LED.Values(i-tempdata.NumPrematureResponses, Data(i).Sensor) == tempLEDHighLevel %nose poke in bright aperture
                                if Config.Speaker.Nosepoke.System == 1
                                    write(s, uint8([start_marker, 6, tempSpeakerNosepokeVolume, Config.Speaker.Nosepoke.Note, Config.Speaker.Nosepoke.Duration, stop_marker]), "uint8") %Speaker
                                end
                                write(s, uint8([start_marker, 2, stop_marker]), "uint8") %turn Foodlights on
                                write(s, uint8([start_marker, 3, Config.Reward.CorrectNumPellet, Config.Reward.CorrectFeeder, stop_marker]), "uint8")
                                Data(i).MotorOnTime = datetime('now', 'Format', 'HH:mm:ss:SSS');
                                Data(i).MotorNumber = Config.Reward.CorrectFeeder;
                                Data(i).Outcome = 1;
                                reward_trigger = 1;
                            elseif Config.LED.Values(i-tempdata.NumPrematureResponses, Data(i).Sensor) == tempLEDDimmedLevel %nose poke in dimmed aperture but in binary configuration
                                timeout_trigger = 1;
                                Data(i).Outcome = 3;
                            elseif Config.LED.Values(i-tempdata.NumPrematureResponses, Data(i).Sensor) == 0 %nose poke in wrong aperture
                                timeout_trigger = 1;
                                Data(i).Outcome = 3;
                            end
                        otherwise
                            error('Reward type not defined.')
                    end
                case 'non-binary' %nose poke in dimmed apertures are also rewarded
                    switch Config.Reward.RTDependence
                        case 'dependent' %Reward dependent on Reaction Time
                            bin = 0;
                            ii = 0;
                            ReactTime = seconds(Data(i).ResponseTime-Data(i).StimOnTime);
                            if Config.LED.Values(i-tempdata.NumPrematureResponses, Data(i).Sensor) == tempLEDHighLevel %nose poke in bright aperture
                                for ii = 1:length(Config.Reward.CorrectReactionTimeBins) %go through bin configuration
                                    if ReactTime <= Config.Reward.CorrectReactionTimeBins(1, ii) %reaction time matches one of the bins
                                        bin = ii; %save corresponding bin
                                        break;
                                    elseif ReactTime > Config.Reward.CorrectReactionTimeBins(1, length(Config.Reward.CorrectReactionTimeBins)) %last bin
                                        bin = length(Config.Reward.CorrectReactionTimeBins); %save corresponding bin
                                    else %reaction time is bigger than current ii
                                        continue; %next bin
                                    end
                                end
                                if Config.Speaker.Nosepoke.System == 1
                                    write(s, uint8([start_marker, 6, tempSpeakerNosepokeVolume, Config.Speaker.Nosepoke.Note, Config.Speaker.Nosepoke.Duration, stop_marker]), "uint8") %Speaker
                                end
                                write(s, uint8([start_marker, 2, stop_marker]), "uint8") %turn Foodlights on
                                write(s, uint8([start_marker, 3, Config.Reward.CorrectReactionTimeBins(3, bin), Config.Reward.CorrectReactionTimeBins(2, bin), stop_marker]), "uint8")
                                Data(i).MotorOnTime = datetime('now', 'Format', 'HH:mm:ss:SSS');
                                Data(i).MotorNumber = Config.Reward.CorrectReactionTimeBins(2, bin);
                                Data(i).ReactTime = ReactTime;
                                Data(i).ReactTimeBin = bin;
                                Data(i).Outcome = 1;
                                reward_trigger = 1;
                            elseif Config.LED.Values(i-tempdata.NumPrematureResponses, Data(i).Sensor) == tempLEDDimmedLevel %nose poke in dimmed aperture
                                for ii = 1:length(Config.Reward.HalfCorrectReactionTimeBins) %go through bin configuration
                                    if ReactTime <= Config.Reward.HalfCorrectReactionTimeBins(1, ii) %reaction time matches one of the bins
                                        bin = ii; %save corresponding bin
                                        break;
                                    elseif ReactTime > Config.Reward.HalfCorrectReactionTimeBins(1, length(Config.Reward.HalfCorrectReactionTimeBins)) %last bin
                                        bin = length(Config.Reward.HalfCorrectReactionTimeBins); %save corresponding bin
                                    else %reaction time is bigger than current ii
                                        continue; %next bin
                                    end
                                end
                                if Config.Speaker.Nosepoke.System == 1
                                    write(s, uint8([start_marker, 6, tempSpeakerNosepokeVolume, Config.Speaker.Nosepoke.Note, Config.Speaker.Nosepoke.Duration, stop_marker]), "uint8") %Speaker
                                end
                                write(s, uint8([start_marker, 2, stop_marker]), "uint8") %turn Foodlights on
                                write(s, uint8([start_marker, 3, Config.Reward.HalfCorrectReactionTimeBins(3, ii), Config.Reward.HalfCorrectReactionTimeBins(2, bin), stop_marker]), "uint8")
                                Data(i).MotorOnTime = datetime('now', 'Format', 'HH:mm:ss:SSS');
                                Data(i).MotorNumber = Config.Reward.HalfCorrectReactionTimeBins(2, bin);
                                Data(i).ReactTime = ReactTime;
                                Data(i).ReactTimeBin = bin;
                                Data(i).Outcome = 2;
                                reward_trigger = 1;
                            elseif Config.LED.Values(i-tempdata.NumPrematureResponses, Data(i).Sensor) == 0 %nose poke in wrong aperture
                                timeout_trigger = 1;
                                Data(i).Outcome = 3;
                            end
                        case 'non-dependent'
                            if Config.LED.Values(i-tempdata.NumPrematureResponses, Data(i).Sensor) == tempLEDHighLevel %nose poke in bright aperture
                                if Config.Speaker.Nosepoke.System == 1
                                    write(s, uint8([start_marker, 6, tempSpeakerNosepokeVolume, Config.Speaker.Nosepoke.Note, Config.Speaker.Nosepoke.Duration, stop_marker]), "uint8") %Speaker
                                end
                                write(s, uint8([start_marker, 2, stop_marker]), "uint8") %turn Foodlights on after motor
                                write(s, uint8([start_marker, 3, Config.Reward.CorrectNumPellet, Config.Reward.CorrectFeeder, stop_marker]), "uint8") %tell arduino the case is 'type', the time and motor 1
                                Data(i).MotorOnTime = datetime('now', 'Format', 'HH:mm:ss:SSS');
                                Data(i).MotorNumber = Config.Reward.CorrectFeeder;
                                Data(i).Outcome = 1;
                                reward_trigger = 1;
                            elseif Config.LED.Values(i-tempdata.NumPrematureResponses, Data(i).Sensor) == tempLEDDimmedLevel %nose poke in dimmed aperture
                                if Config.Speaker.Nosepoke.System == 1
                                    write(s, uint8([start_marker, 6, tempSpeakerNosepokeVolume, Config.Speaker.Nosepoke.Note, Config.Speaker.Nosepoke.Duration, stop_marker]), "uint8") %Speaker
                                end
                                write(s, uint8([start_marker, 2, stop_marker]), "uint8") %turn Foodlights on after motor
                                write(s, uint8([start_marker, 3, Config.Reward.HalfCorrectNumPellet, Config.Reward.HalfCorrectFeeder, stop_marker]), "uint8")
                                Data(i).MotorOnTime = datetime('now', 'Format', 'HH:mm:ss:SSS');
                                Data(i).MotorNumber = Config.Reward.HalfCorrectFeeder;
                                Data(i).Outcome = 2;
                                reward_trigger = 1;
                            elseif Config.LED.Values(i-tempdata.NumPrematureResponses, Data(i).Sensor) == 0 %nose poke in wrong aperture
                                timeout_trigger = 1;
                                Data(i).Outcome = 3;
                            end
                        otherwise
                            error('Reward type not defined.')
                    end
            end
        end
    response_trigger = 0;
    end

    while reward_trigger == 1 %after reward delivery, read the food magazine sensor
        tStop = datetime('now', 'Format', 'HH:mm:ss:SSS');
        if (s.NumBytesAvailable > 0)
            Sensor = read(s, 1, "uint8");
            if ischar(Config.Trial.PerseverativeTime) && (Sensor < 6)%if no perseverative window was defined and a nose poke was made
                if Config.LED.Values(i-tempdata.NumPrematureResponses, Data(i).Sensor) == tempLEDHighLevel %nose poke in bright aperture
                    tempdata.NumPersvCorrect = tempdata.NumPersvCorrect + 1; %counts as correct perseverative answer
                elseif Config.LED.Values(i-tempdata.NumPrematureResponses, Data(i).Sensor) == tempLEDDimmedLevel %nose poke in dimmed aperture
                    tempdata.NumPersvDimmed = tempdata.NumPersvDimmed + 1; %counts as dimmed perseverative answer
                else %nose poke in wrong aperture
                    tempdata.NumPersvIncorrect = tempdata.NumPersvIncorrect + 1; %counts as incorrect perseverative answer
                end
                fprintf('Perseverative Sensor %i. \n', Sensor);
                perseverativeArray = [perseverativeArray Sensor]; %get the aperture number of the perseverative answer
                persvTime = datetime('now', 'Format', 'HH:mm:ss:SSS'); %get the time of the perseverative answer
                perseverativeTimeArray = [perseverativeTimeArray persvTime]; %save time in struct
                Sensor = 0;
                continue;
            elseif Sensor == 12 %if food flap door was closed
                Data(i).RewardTime = datetime('now', 'Format', 'HH:mm:ss:SSS'); %time of reward acquisition
                write(s, uint8([start_marker, 2, stop_marker]), "uint8") %turn Foodlights off following reward acquisition
                if ~isempty(perseverativeArray) %perseverative answers were given
                    Data(i).Perseverative = {perseverativeArray};
                    Data(i).PerseverativeTime = {perseverativeTimeArray};
                    perseverativeTimeArray = []; %clear array for next trial!
                    perseverativeArray = []; %clear array for next trial!
                end
                reward_trigger = 0;
                Sensor = 0;
                RewardCollected = 1;
                trial_trigger = 3;
                break;
            end
        elseif (stop_trigger == 0) && (tStop - tStart) >= duration(0, Config.Experiment.TimeLimit, 0) %checks if Experiment limits were surpassed
            [~, m, sec] = hms(tStop-tStart);
            beep;
            fprintf('\n \n************************************************************** \n');
            fprintf('Experiment limit was reached. \n');
            fprintf('Number of completed trials: %i. \n', i);
            fprintf('Experiment runtime is %i minutes or %.0f seconds. \n', m, sec);
            prompt = 'Would you like to end the experiment? Y/N: ';
            str = input(prompt,'s');
            fprintf('************************************************************** \n');
            switch str
                case {'n','N'}
                    stop_trigger = 1; %The limits will be ignored from now on!
                case {'y','Y'}
                    write(s, uint8([start_marker, 2, stop_marker]), "uint8") %turn Foodlights off
                    end_trigger = 1;
                    break;
            end
        end
    end
    
    if end_trigger == 1  %checks if experiment needs to be over (reward was not retrieved)
       break;
    end
                
    if timeout_trigger == 1 %starts the Time Out
        tempdata.NumTimeOut = tempdata.NumTimeOut + 1; %count the total number of time outs in session
        fprintf('Timeout. \n')
        write(s, uint8([start_marker, 4, stop_marker]), "uint8"); %turn houselight off/dimmed
        TOStop = datetime('now', 'Format', 'HH:mm:ss:SSS'); %initilize time out stop time
        TOStart = datetime('now', 'Format', 'HH:mm:ss:SSS'); %start time out start time
        if Config.Trial.ConsecutiveTimeout == 1 %consecutive Time out
            while timeout_trigger == 1
                TOStop = datetime('now', 'Format', 'HH:mm:ss:SSS'); %initilize time out stop time
                if (TOStop - TOStart) < duration(0,0,Config.Trial.Timeout)
                    if (s.NumBytesAvailable > 0)
                        Sensor = read(s, 1, "uint8");
                        if Sensor <= 5 %nose poke during time out
                            tempdata.NumTimeOut = tempdata.NumTimeOut + 1; %count the total number of time outs in session
                            timeoutArray = [timeoutArray Sensor];
                            timeoutTime = datetime('now', 'Format', 'HH:mm:ss:SSS'); %time the time out answer was given
                            timeoutTimeArray = [timeoutTimeArray timeoutTime];
                            TOStart = datetime('now', 'Format', 'HH:mm:ss:SSS'); %restart TOStart
                            TOStop = datetime('now', 'Format', 'HH:mm:ss:SSS'); %restart TOStop
                            fprintf('consecutive Timeout Sensor %i. \n', Sensor);
                            Sensor = 0;
                        end
                    end
                elseif (TOStop - TOStart) >= duration(0,0,Config.Trial.Timeout) %time out is over
                    write(s, uint8([start_marker, 5, stop_marker]), "uint8"); %turn houselights on
                    fprintf('Timeout over. \n');
                    if ~isempty(timeoutArray) %if time out answers were given
                        Data(i).TimeoutAns = {timeoutArray};
                        Data(i).TimeoutAnsTime = {timeoutTimeArray};
                        timeoutTimeArray = []; %clear array for next trial!
                        timeoutArray = []; %clear array for next trial!
                    end
                    trial_trigger = 3;
                    timeout_trigger = 0;
                end
            end
        else %no consecutive Time out
            while timeout_trigger == 1
              TOStop = datetime('now', 'Format', 'HH:mm:ss:SSS'); %initilize time out stop time
              if (TOStop - TOStart) < duration(0,0,Config.Trial.Timeout)
                if (s.NumBytesAvailable > 0)
                    Sensor = read(s, 1, "uint8");
                    if Sensor <= 5 %nose poke during time out
                        fprintf('Sensor %i during Timeout. \n', Sensor);
                        timeoutTime = datetime('now', 'Format', 'HH:mm:ss:SSS'); %time the time out answer was given
                        timeoutArray = [timeoutArray Sensor];
                        timeoutTimeArray = [timeoutTimeArray timeoutTime];
                        Sensor = 0;
                    end
                end
              elseif (TOStop - TOStart) >= duration(0,0,Config.Trial.Timeout) %time out is over
                fprintf('Timeout over. \n');
                write(s, uint8([start_marker, 5, stop_marker]), "uint8"); %turn houselights on
                if ~isempty(timeoutArray) %if time out asnwers were given
                    Data(i).TimeoutAns = {timeoutArray};
                    Data(i).TimeoutAnsTime = {timeoutTimeArray};
                    timeoutTimeArray = []; %clear array for next trial!
                    timeoutArray = []; %clear array for next trial!
                end
                trial_trigger = 3;
                timeout_trigger = 0;
              end
            end
        end
    end

    if (trial_trigger == 3) %end the trial
        Data(i).TrialCount = trial_count; %optional, for the user to see the number of trials used in the Performance check
        Data(i).TrialEnd = datetime('now', 'Format', 'HH:mm:ss:SSS'); %get time of trial ending
        tStop = datetime('now', 'Format', 'HH:mm:ss:SSS');
        [~, m, sec] = hms(tStop-tStart);
        if (premature_trigger == 1) && (Config.Trial.RedoPremature == 1) %redo premature trial
            fprintf('Redoing Trial nr. %i. \n', (sum([Data.Outcome] ~= 4)+1));
            Data(i).LedValue = NaN(1,Config.LED.N); %deletes the stimulus value
        else   
            %premature_trigger = 0; %resets the premature trigger, premature trials are not to be repeated
            if Config.Staircase.Function == 1
                if (Config.Trial.RedoPremature == 1) %premature answers don't count as trials
                    fprintf('Trial nr. %i done. Currently on level %i. \n', (sum([Data.Outcome] ~= 4)), Config.Staircase.CurrentLevel); %only mention current level if staircase procedure is on
                    fprintf('Runtime is %i minutes and %.0f seconds. \n \n', m, sec);
                else
                    fprintf('Trial nr. %i done. Currently on level %i. \n', i, Config.Staircase.CurrentLevel); %only mention current level if staircase procedure is on
                    fprintf('Runtime is %i minutes and %.0f seconds. \n \n', m, sec);
                end
            else
                if (Config.Trial.RedoPremature == 1) %premature answers don't count as trials
                    fprintf('Trial nr. %i done. \n', (sum([Data.Outcome] ~= 4)));
                    fprintf('Runtime is %i minutes and %.0f seconds. \n \n', m, sec);
                else
                    fprintf('Trial nr. %i done. \n', i);
                    fprintf('Runtime is %i minutes and %.0f seconds. \n \n', m, sec);
                end
            end
        end
        trial_trigger = 0;
        Data(i).TrainingLevel = Config.Staircase.CurrentLevel; %save the trial training level
        flush(s)
        continue; %continue execution with the next iteration
    end

end

KbQueueRelease();
[Data, Config, PerformanceCheck] = DataProc(Data, Config, PerformanceCheck, tempdata, i, tStop, tStart); %call the DataProc function