% Clear the workspace
close all;
clear;
sca;

%--------------------------------------------------------------------------
%   Code shows a basic Posner cuing tak. A location to the left of right of
%   fixation is "cued" by a framed square. Subsequently, after a
%   "cue-target-onset-asynchrony" a gabor "target" is presenetd at one of the
%   same left of right locations. The cue and target can either be in the
%   same location ("contingent") or different locations ("non-contingent").
%   the time taken to respond and whether you were correct is recorded and
%   saved to file.
%
% Not my area of research, but apparently this style of task is used
% extremely widely.
%
% Note that here I have used for loops for the stimulus presentation, even
% though not strickly needed. See "WaitFrameDemo" for an explanation of why
% I do this and a demonstration of an alternative way.
%--------------------------------------------------------------------------

%--------------------
%   Screen setup
%--------------------

% Setup PTB with some default values
PsychDefaultSetup(2);

% Skip sync tests ** This is for demo purposes only ** It should not be
% done in a real experiment.
Screen('Preference', 'SkipSyncTests', 2);

% Set the random number generator so we get random numbers, not the same
% sequency if we restart Matlab
rng('shuffle');

% Set the screen number to the external secondary monitor if there is one
% connected
screenNumber = max(Screen('Screens'));

% Define black, white and grey
white = WhiteIndex(screenNumber);
grey = white / 2;
black = BlackIndex(screenNumber);

% Open the screen
[window, windowRect] = PsychImaging('OpenWindow', screenNumber, grey, []);

% Flip to clear
Screen('Flip', window);

% Query the frame duration
ifi = Screen('GetFlipInterval', window);

% Set the text size
Screen('TextSize', window, 40);

% Query and set the maximum priority level
topPriorityLevel = MaxPriority(window);
Priority(topPriorityLevel);

% Get the centre coordinate of the window
[xCenter, yCenter] = RectCenter(windowRect);


%----------------------------
% Gabor target information
%----------------------------

% Dimension of the region where will draw the Gabor in pixels
gaborDimPix = 300;

% Sigma of Gaussian
sigma = gaborDimPix / 7;

% Obvious Parameters
orientation = 90;
contrast = 1;
aspectRatio = 1.0;

% Spatial Frequency (Cycles Per Pixel)
% One Cycle = Grey-Black-Grey-White-Grey i.e. One Black and One White Lobe
numCycles = 8;
freq = numCycles / gaborDimPix;

% Build a procedural gabor texture
gabortex = CreateProceduralGabor(window, gaborDimPix, gaborDimPix, [],...
    [0.5 0.5 0.5 0.0], 1, 0.5);

% Our Gabor target will always be vertical
gaborAngle = 0;


%----------------------
% Box cue information
%----------------------

% Colour and width of the line for the box
boxColour = black;
boxLineWidth = 4;


%-----------------------------
% Fixation point information
%-----------------------------

% Colour and size of the fixation point
dotSizePix = 8;
dotColor = black;


%----------------------------------
% Positions to the left and right
%----------------------------------

% We will position the the target and Gabor 1/4 or 3/4 the screen width
leftPosX = windowRect(3) * 0.25;
rightPosX = windowRect(3) * 0.75;

% Rectangular regions for the Gabor target and cue box
leftRect = CenterRectOnPointd([0 0 gaborDimPix gaborDimPix], leftPosX, yCenter);
rightRect = CenterRectOnPointd([0 0 gaborDimPix gaborDimPix], rightPosX, yCenter);


%----------------------------------------------------------------------
%                       Timings
%----------------------------------------------------------------------

% These are pretty much random - you will need to look at the literature to
% see what is used. Also, note the use of "round" which would be
% dangerous to blindly use in an experiment.

% Fixation point time in seconds and frames
fixTimeSecs = 0.5;
fixTimeFrames = round(fixTimeSecs / ifi);

% Cue point time in seconds and frames
cueTimeSecs = 0.15;
cueTimeFrames = round(cueTimeSecs / ifi);

% Cue point time in seconds and frames
targetTimeSecs = 0.15;
targetTimeFrames = round(targetTimeSecs / ifi);

% Intertrial interval time
isiTimeSecs = 0.2;
isiTimeFrames = round(isiTimeSecs / ifi);

% Time between the cue and the target
cueTargetTimeSecs = 0.3;
cueTargetTimeFrames = round(isiTimeSecs / ifi);

% Frames to wait before redrawing
waitframes = 1;


%----------------------------------------------------------------------
%                       Keyboard information
%----------------------------------------------------------------------

% Define the keyboard keys that are listened for. We will be using the left
% and right arrow keys as response keys for the task and the escape key as
% a exit/reset key
escapeKey = KbName('ESCAPE');
leftKey = KbName('LeftArrow');
rightKey = KbName('RightArrow');

% Hide the mouse cursor
HideCursor;


%----------------------------------------------------------------------
%                             Procedure
%----------------------------------------------------------------------

% Gabor target and square cue will appear to the left and right of the
% fixation. Left will be signalled by a zero and right a one. They can be
% "contingent" (cue and target in same location) or non-contingent (cue and
% target in different locaitons). We create a matrix with the four possible
% cue and target positions. Upper line will be the cue position, lower line
% the target position.
baseMat = [0 0 1 1; 0 1 0 1];

% Repeat this matrix a certain number of times. We repeat the whole matrix
% to ensure the same number of trials per cue target position
numReps = 2;
cueTargetMat = repmat(baseMat, 1, numReps);

% Randomise the trials
cueTargetMatShuff = Shuffle(cueTargetMat, 2);

% How many trials are we doing in total
numTrials = size(cueTargetMatShuff, 2);

% Make our response matrix which will save the RT and correctness of the
% location choice. We preallocate the matrix with nans.
dataMat = nan(numTrials, 2);


%----------------------------------------------------------------------
%                       Experimental loop
%----------------------------------------------------------------------

% Animation loop: we loop for the total number of trials
for trial = 1:numTrials

    % Randomise the phase of the Gabor for this trial
    phase = rand .* 360;
    propertiesMat = [phase, freq, sigma, contrast, aspectRatio, 0, 0, 0]';

    % Cue and target position
    cuePos = cueTargetMatShuff(1, trial);
    targetPos = cueTargetMatShuff(2, trial);

    % Logic to assign the correct position to the cue and target
    if cuePos == 0
        cueRect = leftRect;
    elseif cuePos == 1
        cueRect = rightRect;
    end

    if targetPos == 0
        targetRect = leftRect;
    elseif targetPos == 1
        targetRect = rightRect;
    end

    % Set the blend funciton for a nice antialiasing
    Screen('BlendFunction', window, GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);

    % If this is the first trial we present a start screen and wait for a
    % key-press
    if trial == 1

        % Draw the instructions
        DrawFormattedText(window, 'Press Any Key To Begin', 'center', 'center', black);

        % Flip to the screen
        Screen('Flip', window);

        % Wait for a key press
        KbStrokeWait(-1);

        % Flip the screen grey
        Screen('FillRect', window, grey);
        vbl = Screen('Flip', window);
        WaitSecs(0.5);

    end

    % Present the fixation point only (a nicely antialiased dot)
    for i = 1:fixTimeFrames
        Screen('DrawDots', window, [xCenter; yCenter], dotSizePix, dotColor, [], 2);
        vbl = Screen('Flip', window, vbl + (waitframes - 0.5) * ifi);
    end

    % Present the box cue and the fixation point
    for i = 1:cueTimeFrames
        Screen('DrawDots', window, [xCenter; yCenter], dotSizePix, dotColor, [], 2);
        Screen('FrameRect', window, boxColour, cueRect, boxLineWidth);
        vbl = Screen('Flip', window, vbl + (waitframes - 0.5) * ifi);
    end

    % Present a grey screen for the cue-target gap
    for i = 1:cueTargetTimeFrames
        Screen('FillRect', window, grey);
        Screen('DrawDots', window, [xCenter; yCenter], dotSizePix, dotColor, [], 2);
        vbl = Screen('Flip', window, vbl + (waitframes - 0.5) * ifi);
    end

    % Present the Gabor target and fixation point: note the swicth between
    % blend functions needed for drawing the fixation point and Gabor
    for i = 1:targetTimeFrames
        Screen('BlendFunction', window, GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
        Screen('DrawDots', window, [xCenter; yCenter], dotSizePix, dotColor, [], 2);
        Screen('BlendFunction', window, 'GL_ONE', 'GL_ZERO');
        Screen('DrawTextures', window, gabortex, [], targetRect, gaborAngle, [], [], [], [],...
            kPsychDontDoRotation, propertiesMat);
        vbl = Screen('Flip', window, vbl + (waitframes - 0.5) * ifi);
    end

    % Clear the screen ready for a response
    Screen('FillRect', window, grey);
    vbl = Screen('Flip', window, vbl + (1 - 0.5) * ifi);

    % Now we wait for a keyboard button signaling the observers response.
    % The left arrow key signals a "left" response and the right arrow key
    % a "right" response. You can also press escape if you want to exit the
    % program
    respToBeMade = true;
    startResp = GetSecs;
    while respToBeMade
        [keyIsDown,secs, keyCode] = KbCheck(-1);
        if keyCode(escapeKey)
            ShowCursor;
            sca;
            return
        elseif keyCode(leftKey)
            response = 0;
            respToBeMade = false;
        elseif keyCode(rightKey)
            response = 1;
            respToBeMade = false;
        end
    end
    endResp = GetSecs;
    rt = endResp - startResp;

    % Work out if the location of the gabpr target was identified corrcetly
    if targetPos == response
        correctness = 1;
    elseif targetPos ~= repsonse
        correctness = 0;
    end

    % Save out the data after having added the data to the data matrix. We
    % save to the same directory as the code as a tab dilimited text file
    dataMat(trial, :) = [rt correctness];
    writematrix(dataMat, [cd filesep 'posnerData.txt'], 'Delimiter', '\t')

    % Inter trial interval black screen. Note that the timestamp for the
    % initial frame will be missed due to the first vbl being "old" due to
    % the response loop. I leave it as an excercise to the reader as to how
    % one could fix this simply.
    for i = 1:isiTimeFrames
        Screen('FillRect', window, grey);
        vbl = Screen('Flip', window, vbl + (waitframes - 0.5) * ifi);
    end

    % If this is the last trial we present screen saying that the experimet
    % is over.
    Screen('BlendFunction', window, GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
    if trial == numTrials

        % Draw the instructions: in reality the person could press any of
        % the listened to keys to exist. But they do not know that.
        DrawFormattedText(window, 'Experiment Complete: press ESCAPE to exit', 'center', 'center', black);

        % Flip to the screen
        vbl = Screen('Flip', window, vbl + (waitframes - 0.5) * ifi);

        % Wait for a key press
        KbStrokeWait(-1);

    end


end

% Done! Clear up and leave the building
disp('Experiment Finished')
sca