clear all; close all;

rng('shuffle'); % Randomize seed of random number generator

%% Set up general parameters
Gparams = [];
Gparams = setupGParams(Gparams);

%% Hide Cursor and set priority
HideCursor;
priorityLevel = MaxPriority(Gparams.pWindow);
Priority(priorityLevel);
RestrictKeysForKbCheck([KbName('Space')]);

%% Generate block and trial structure
nBlocks = 2;

feedback        = true; % indicate whether participants will receive feedback about their decision

for iBlock = 1:nBlocks
   
    if iBlock == 1
        
        % Easy trials only, in 4 mini blocks
        coh(:,1) = repmat([-30 -20 -10 10 20 30]'/100,4,1);
        coh(:,1) = coh(randperm(length(coh(:,1))),1);
        
        coh(:,2) = repmat([-30 -20 -10 10 20 30]'/100,4,1);
        coh(:,2) = coh(randperm(length(coh(:,2))),1);
                
        trialMatrix.relative_coherence(iBlock,:) = reshape(coh,1,size(coh,1)*size(coh,2));
               
    elseif iBlock == 2
        
        % Easiest stimulus set
        coh = repmat(Gparams.dots.relative_coherence_easy,1,4)';
        trialMatrix.relative_coherence(iBlock,:) = coh(randperm(length(coh)));
    
    end
    
    % Directions of the standard and test stimuli
    trialMatrix.direction(iBlock,:) = zeros(1,length(trialMatrix.relative_coherence(iBlock,:)));
    trialMatrix.direction(iBlock,1) = rand(1)*360;

    for iTrial = 2:length(trialMatrix.relative_coherence(iBlock,:))
        trialMatrix.direction(iBlock,iTrial) = mod(trialMatrix.direction(iBlock,iTrial-1) + (rand(1)*300+30),360); % Each trial is at least 30 degrees offset from the previous trial direction
    end
    
    trialMatrix.responseMapping(iBlock,:) = ones(size(trialMatrix.relative_coherence(iBlock,:))) * (mod(Gparams.subjectNumber,2) + 1);   % Response mapping depends on subject number
    
    clear coh
end

if size(trialMatrix.relative_coherence) ~= size(trialMatrix.responseMapping)
   error('Trialsequence dimensions are not equal')   
end

% Save the trialmatrix
save([Gparams.DataPath 'practice_without_confidence_trialmatrix_subj-' num2str(Gparams.subjectNumber) '_ses-' num2str(Gparams.sessionNumber) '_' datestr(now, 'yyyy-mm-dd_HH-MM-SS') '.mat'],'trialMatrix','-v7.3');

nTrials = length(trialMatrix.relative_coherence);

%% Allocate space for flip and response data

results                 = [];

results.responseTime    = NaN(nBlocks,nTrials);
results.responseKey     = NaN(nBlocks,nTrials);
results.responseDir     = NaN(nBlocks,nTrials);
results.responseCorrect = NaN(nBlocks,nTrials);

flip                    = [];

flip.itiOnset.VBL       = NaN(nBlocks,nTrials);
flip.itiOnset.StimOnset = NaN(nBlocks,nTrials);
flip.itiOnset.FlipTS    = NaN(nBlocks,nTrials);
flip.itiOnset.Missed    = NaN(nBlocks,nTrials);
flip.itiOnset.BeamPos   = NaN(nBlocks,nTrials);

flip.standardStim.VBL       = NaN(nBlocks,nTrials,Gparams.timing.nframes);
flip.standardStim.StimOnset = NaN(nBlocks,nTrials,Gparams.timing.nframes);
flip.standardStim.FlipTS    = NaN(nBlocks,nTrials,Gparams.timing.nframes);
flip.standardStim.Missed    = NaN(nBlocks,nTrials,Gparams.timing.nframes);
flip.standardStim.BeamPos   = NaN(nBlocks,nTrials,Gparams.timing.nframes);

flip.isiOnset.VBL         = NaN(nBlocks,nTrials);
flip.isiOnset.StimOnset   = NaN(nBlocks,nTrials);
flip.isiOnset.FlipTS      = NaN(nBlocks,nTrials);
flip.isiOnset.Missed      = NaN(nBlocks,nTrials);
flip.isiOnset.BeamPos     = NaN(nBlocks,nTrials);

flip.testStim.VBL       = NaN(nBlocks,nTrials,Gparams.timing.nframes);
flip.testStim.StimOnset = NaN(nBlocks,nTrials,Gparams.timing.nframes);
flip.testStim.FlipTS    = NaN(nBlocks,nTrials,Gparams.timing.nframes);
flip.testStim.Missed    = NaN(nBlocks,nTrials,Gparams.timing.nframes);
flip.testStim.BeamPos   = NaN(nBlocks,nTrials,Gparams.timing.nframes);

flip.testStimOffset.VBL       = NaN(nBlocks,nTrials);
flip.testStimOffset.StimOnset = NaN(nBlocks,nTrials);
flip.testStimOffset.FlipTS    = NaN(nBlocks,nTrials);
flip.testStimOffset.Missed    = NaN(nBlocks,nTrials);
flip.testStimOffset.BeamPos   = NaN(nBlocks,nTrials);

%% Instructions

% Instruction screen
text = ['TASK SUMMARY\n\n\n\n' ...
    'In the task you will always see two stimuli:\n\n' ...
    'a standard stimulus, followed by a second stimulus.\n\n' ...
    'You are asked to respond to the coherence difference,\n\n' ...
    'using the "j" and "k" keys.\n\n' ...
    'You have only ' num2str(Gparams.timing.responseTimeDirectionOnly) ' seconds to respond.\n\n'...
    'Try to answer as quickly and accurately as possible.\n\n' ...
    'Tip: you can respond as soon as the second stimulus appears.\n\n\n\n' ...
    'You will get feedback about the correctness of your response.\n\n\n\n' ...
    'Please remember to always maintain fixation on the cross\n\n' ...
    'in the center of the screen.\n\n\n\n' ...
    'Press "space" to continue.'];
showtext(text,Gparams);
    
text = ['START PRACTICE\n\n\n\n' ...
    'Now you will practice the task for a couple of rounds.\n\n\n\n'...
    'The practice will start with some rather easy rounds\n\n' ...
    'and will then become slightly more difficult.\n\n\n\n'...
    'At the start of the round you will see a reminder\n\n' ...
    'of which button ("j" and "k") to use for which response.\n\n\n\n' ...
    'Press "space" to start the practice round.\n\n' ...
    'The round takes approximately 5 minutes.'];
showtext(text,Gparams);


%% run blocks
for iBlock = 1:nBlocks
    
    DrawFormattedText(Gparams.pWindow, 'Loading stimuli...', 'center', 'center', 255);
    Screen('Flip', Gparams.pWindow);
    
    % preload all the dot coordinates - overwrite this block!
    testStimCoord = nan(nTrials, Gparams.timing.nframes, 2, Gparams.dots.nDots);
    standardStimCoord = nan(nTrials, Gparams.timing.nframes, 2, Gparams.dots.nDots);
    
    for iTrial = 1:nTrials
        testStimCoord(iTrial, :, :, :) = dots(iTrial,iBlock,trialMatrix,0,Gparams);
        standardStimCoord(iTrial, :, :, :) = dots(iTrial,iBlock,trialMatrix,1,Gparams);
    end
    
    % Save the coordinates
    save([Gparams.DataPath 'dots_subj-' num2str(Gparams.subjectNumber) '_ses-' num2str(Gparams.sessionNumber) '_block-' num2str(iBlock) '_' datestr(now, 'yyyy-mm-dd_HH-MM-SS') '.mat'],'testStimCoord','-v7.3');
    save([Gparams.DataPath 'dots_subj-' num2str(Gparams.subjectNumber) '_ses-' num2str(Gparams.sessionNumber) '_block-' num2str(iBlock) '_' datestr(now, 'yyyy-mm-dd_HH-MM-SS') '.mat'],'standardStimCoord','-v7.3');
    
    % Instruction screen
    RestrictKeysForKbCheck([KbName('Space')]);
    
    text = ['START PRACTICE - round ' num2str(iBlock) ' of ' num2str(nBlocks) '\n\n\n\n' ...
    'Press "space" to see the reminder\n\n' ...
    'of which button ("j" and "k") to use for which response\n\n' ...
	'and start the practice round.\n\n' ...
    'The round takes approximately 5 minutes.'];
    showtext(text,Gparams);
    
    % Show response mapping for this block
    if mean(trialMatrix.responseMapping(iBlock,:)) == 1
        DrawFormattedText(Gparams.pWindow, 'LOWER (j)                     HIGHER (k)', 'center', 'center', 255);
    elseif mean(trialMatrix.responseMapping(iBlock,:)) == 2
        DrawFormattedText(Gparams.pWindow, 'HIGHER (j)                     LOWER (k)', 'center', 'center', 255);       
    end
        drawFixation(Gparams);
        Screen('Flip', Gparams.pWindow);
        WaitSecs(3);

    
    for iTrial = 1:length(trialMatrix.relative_coherence)
        
        %% Fixation
        drawFixation(Gparams);
        [flip.itiOnset.VBL(iBlock,iTrial),...
            flip.itiOnset.StimOnset(iBlock,iTrial),...
            flip.itiOnset.FlipTS(iBlock,iTrial),...
            flip.itiOnset.Missed(iBlock,iTrial),...
            flip.itiOnset.BeamPos(iBlock,iTrial)] = Screen('Flip', Gparams.pWindow);
        
        
        %% Standard stimulus
        presentationTime = flip.itiOnset.VBL(iBlock,iTrial) + Gparams.timing.itiDirectionOnly - Gparams.frameDuration - Gparams.flipLag;
        % subtract frameDuration to compensate for frame spent between end
        % and beginning of for-loop (over trials)
        
        for iFrame = 1:Gparams.timing.nframes
            
            drawDots(squeeze(standardStimCoord(iTrial,iFrame, :, :)),Gparams);
            drawFixation(Gparams);
            [flip.standardStim.VBL(iBlock,iTrial,iFrame),...
                flip.standardStim.StimOnset(iBlock,iTrial,iFrame),...
                flip.standardStim.FlipTS(iBlock,iTrial,iFrame),...
                flip.standardStim.Missed(iBlock,iTrial,iFrame),...
                flip.standardStim.BeamPos(iBlock,iTrial,iFrame)] = ...
                Screen('Flip', Gparams.pWindow,presentationTime);
            
            presentationTime = flip.standardStim.VBL(iBlock,iTrial,iFrame) + Gparams.frameDuration-Gparams.flipLag;
            
        end
        
        %% ISI
        drawFixation(Gparams);
        [flip.isiOnset.VBL(iBlock,iTrial),...
            flip.isiOnset.StimOnset(iBlock,iTrial),...
            flip.isiOnset.FlipTS(iBlock,iTrial),...
            flip.isiOnset.Missed(iBlock,iTrial),...
            flip.isiOnset.BeamPos(iBlock,iTrial)] = Screen('Flip', Gparams.pWindow,presentationTime);
        
        
        %% Test Stimulus
        keyIsDown1 = 0;
        
        % Only allow direction buttons and escape
        RestrictKeysForKbCheck([Gparams.responseKeys.left ...
            Gparams.responseKeys.right ...
            Gparams.responseKeys.escape]);
        
        presentationTime = flip.isiOnset.VBL(iBlock,iTrial) + Gparams.timing.isi - Gparams.flipLag;
        
        for iFrame = 1:Gparams.timing.nframes
            
            drawDots(squeeze(testStimCoord(iTrial,iFrame, :, :)),Gparams);
            drawFixation(Gparams);
            [flip.testStim.VBL(iBlock,iTrial,iFrame),...
                flip.testStim.StimOnset(iBlock,iTrial,iFrame),...
                flip.testStim.FlipTS(iBlock,iTrial,iFrame),...
                flip.testStim.Missed(iBlock,iTrial,iFrame),...
                flip.testStim.BeamPos(iBlock,iTrial,iFrame)] = ...
                Screen('Flip', Gparams.pWindow,presentationTime);
            
            presentationTime = flip.testStim.VBL(iBlock,iTrial,iFrame) + Gparams.frameDuration-Gparams.flipLag;
            
            if ~keyIsDown1
                [keyIsDown1, secs1, keyCode1]  = KbCheck();
            end
             
        end
        
        %% Response (continued)
        
        % Draw fixation again
        drawFixation(Gparams);
        [flip.testStimOffset.VBL(iBlock,iTrial),...
            flip.testStimOffset.StimOnset(iBlock,iTrial),...
            flip.testStimOffset.FlipTS(iBlock,iTrial),...
            flip.testStimOffset.Missed(iBlock,iTrial),...
            flip.testStimOffset.BeamPos(iBlock,iTrial)] = ...
            Screen('Flip', Gparams.pWindow, presentationTime);
        
        % Allow people to still respond
        
        iTime = GetSecs;
        
        while iTime < presentationTime + Gparams.timing.responseTimeDirectionOnly
            
             if ~keyIsDown1
                [keyIsDown1, secs1, keyCode1]  = KbCheck();
            end
            
            iTime = GetSecs;
            
        end
                
        % Write the response into results
        
        if keyIsDown1
            
            % Log RT
            results.responseTime(iBlock, iTrial) = secs1 - flip.testStim.VBL(iBlock,iTrial,1);  % time from stimulus onset
            
            % Log button press
            % If both buttons are pressed simultaneously, record as invalid
            % response
            if length(find(keyCode1)) > 1
                results.responseKey(iBlock, iTrial) = NaN;
            else  
                switch KbName(find(keyCode1))
                    case 'j'
                        results.responseKey(iBlock, iTrial) = -1;
                    case 'k'
                        results.responseKey(iBlock, iTrial) = 1;
                    case 'esc'
                        Gparams.cancel = true;
                        results.responseKey(iBlock, iTrial) = NaN;
                    case 'ESCAPE' % if escape is pressed, exit the experiment
                        Gparams.cancel = true;
                        results.responseKey(iBlock, iTrial) = NaN;
                end
                
            end
            
            % Log response direction
            if ~isnan(results.responseKey(iBlock, iTrial))
               
                switch trialMatrix.responseMapping(iBlock,iTrial)
                    case 1
                        results.responseDir(iBlock,iTrial) = ...
                            results.responseKey(iBlock, iTrial);
                    case 2
                        results.responseDir(iBlock,iTrial) = ...
                            results.responseKey(iBlock, iTrial) * -1;
                end
                
            end
              
        end
        
        % Compute correctness of response
        if isnan(results.responseDir(iBlock,iTrial))
            results.responseCorrect(iBlock,iTrial) = NaN; 
        elseif results.responseDir(iBlock,iTrial) == sign(trialMatrix.relative_coherence(iBlock,iTrial))
            results.responseCorrect(iBlock,iTrial) = true;
        else
            results.responseCorrect(iBlock,iTrial) = false;    
        end    
            
        %% Escape trial loop
        if Gparams.cancel == true
            break
            warning('experiment was manually terminated');      
        end
        
        %% Feedback
        
        if ~keyIsDown1
            PsychPortAudio('SetLoop',Gparams.audio.h, Gparams.sound.tonepos(3,1), Gparams.sound.tonepos(3,2));
        elseif results.responseCorrect(iBlock,iTrial) == true
            PsychPortAudio('SetLoop',Gparams.audio.h, Gparams.sound.tonepos(1,1), Gparams.sound.tonepos(1,2));
        else 
            PsychPortAudio('SetLoop',Gparams.audio.h, Gparams.sound.tonepos(2,1), Gparams.sound.tonepos(2,2));
        end    
        PsychPortAudio('Start', Gparams.audio.h);
        
    end
         
    %% Escape block loop
    if Gparams.cancel == true
            break
            warning('experiment was manually terminated');      
    end
                    
    %% End of block message
    RestrictKeysForKbCheck([KbName('Space')]);
    
    if (iBlock < nBlocks)
        showtext(sprintf('You just finished round %g of %g.\n\n\n You had %.2f percent correct\n\nand you responded on average after %.2f seconds.\n\n You missed %g responses.\n\n\n\nFeel free to take a short break.\n\n\nWhen you are ready press "space" to continue...', ...
            iBlock, nBlocks, nanmean(results.responseCorrect(iBlock,:))*100, nanmean(results.responseTime(iBlock,:)), sum(isnan(results.responseDir(iBlock,:)))), Gparams);
    elseif iBlock == nBlocks
        showtext(sprintf('You have finished the practice rounds.\n\n\n In this last round you had %.2f percent correct\n\nand you responded on average after %.2f seconds.\n\n You missed %g responses.\n\n\n\n Please report to the experimenter.', ...
            nanmean(results.responseCorrect(iBlock,:))*100, nanmean(results.responseTime(iBlock,:)), sum(isnan(results.responseDir(iBlock,:)))), Gparams);
    end
    
    
    %% Print stats
    fprintf('\nBLOCK %g\n\nPERFORMANCE\n\nLevel 1:\n%.2f percent correct\nRT = %.2f s\nMissed responses: %d\n\nLevel 2:\n%.2f percent correct\nRT = %.2f s\nMissed responses: %d\n\n', ...
    iBlock,...
    nanmean(results.responseCorrect(iBlock,1:24))*100, nanmean(results.responseTime(iBlock,1:24)),sum(isnan(results.responseTime(iBlock,1:24))),...
    nanmean(results.responseCorrect(iBlock,25:48))*100, nanmean(results.responseTime(iBlock,25:48)),sum(isnan(results.responseTime(iBlock,25:48))));
    
end

%% Save data of the experiment
datetime = datestr(now, 'yyyy-mm-dd_HH-MM-SS');
filename = [Gparams.DataPath 'practice_without_confidence_data_subj-' num2str(Gparams.subjectNumber) '_ses-' num2str(Gparams.sessionNumber) '_' datetime '.mat'];
save(filename,'Gparams','trialMatrix','results','flip');

%% Cleaning up
Screen('CloseAll');
ShowCursor;
ListenChar(0);
Priority(0);