function main
%Matt Jones, Dec 20
%Visual identification experiment with spatial (mouse) response
%Some older parts of this code were inherited from Todd Maddox

clear global %clear all global variables
rng('shuffle') %random seed to random generator

global data %structure holding all relevant data for this subject
global blockLength %trials per block
global errorThreshold %learning criterion
global window %pointer to monitor for Psychtoolbox
global screenRect %coordinates of screen boundaries
global W H stimWidth responseSize stimrect respRadius respBox respX respY %coordinates for drawing objects
global white brown green red bground %colors
global instrSize respSize %font sizes
global wrongTime demoIti iti FBtime postInstrTime %timing parameters
global stimuli %stimulus images

%parameters for experiment duration
blockLength = 36; %trials per block
errorThreshold = 2; %learning criterion: maximum errors in a block to exit

%Timing parameters
iti = .25; %time between feedback offset and following stimulus onset
FBtime = .4; %time between feedback onset and offset
wrongDelay = .05; %for incorrect responses, time between onsets of incorrect and correct feedback
wrongTime = .2; %time for incorrect feedback during demo phase
demoIti = .4; %ITI for demo phase
postInstrTime = .5; %time of blank screen after each phase of instructions

%Set up data structure for this subject
data = struct('version', '12/9/13'); %create structure with field "version" giving date of most recent code update (all files)
data.computer = machineID; %useful if running on multiple computers; must have machineID.m in matlab path
if setup == -1 %call setup.m: inputs subject number, reads in stimulus-response map
    disp 'Experiment Canceled' %a result of 0 is code from setup.m to abort
    return
end

%Load stimulus image files
stimuli = cell(9,1); %cell array; one cell per stimulus
for i=1:9
    imfile = ['stimuli/lizard' num2str(i) '.jpg']; %filename, e.g. stimuli/lizard1.jpg
    stimuli{i} = imread(imfile); %read this image as n-m-3 RGB array; store in cell of stimulus array
end

%Set up psychtoolbox and graphics parameters
%Most Psychtoolbox commands are executed through the Screen function
%The first input to Screen gives the names of the Psychtoolbox command
[window, screenRect] = Screen('OpenWindow',0); %take control of main monitor
    %window is handle for monitor
    %screenRect is vector of coordinates for [top left bottom right] of screen, with top = left = 0
W = screenRect(3); %width of screen
H = screenRect(4); %height of screen
Screen('TextFont', window, 'Verdana');	%set font for drawing text (both Macs and PCs have Verdana)

%Graphical parameters
stimWidth = W/15; %width of stimulus, as proportion of screen width
respSize = stimWidth/2; %pixel radius of response circles
respRadius = respSize*5; %pixel distance of responses from stimulus
responseSize = 30; %font size for response prompts
instrSize = 18; %font size for instructions

%Colors are specified as [R G B] vectors with values in 0:255
white = [250 250 250]; %color for text
brown = [50 30 10]; %color for response circles
red = [150 25 25]; %color for incorrect responses
green = [25 150 25]; %color for correct responses
bground = [0 0 0]; %background (black)

%Compute stimulus and response locations
ratio = size(stimuli{1},1)/size(stimuli{1},2); %stimulus aspect ratio
stimrect = [W/2-floor(stimWidth/2), H/2-floor(stimWidth*ratio/2), W/2+ceil(stimWidth/2), H/2+ceil(stimWidth*ratio/2)]; %[top left bottom right] coordinates for stimulus location
respX = W/2 + respRadius*sin(2*pi*(0:8)/9); %horizontal centers of all response locations (clockwise from top)
respY = H/2 - respRadius*cos(2*pi*(0:8)/9); %vertical centers of all response locations (clockwise from top)
respBox = [respX'-respSize,respY'-respSize,respX'+respSize,respY'+respSize]; %enclosing coordinates for response circles; one row per stimulus

%Some text for display during trials
Screen('TextSize',window,responseSize); %change font to larger size
prompt = 'Where does this lizard live?'; %text to display with stimulus on each trial
[promptWidth,~] = Screen('DrawText',window,prompt,0,0); %draw text in black and return new pen location; a trick to get display width
wrong = 'Wrong';
[wrongWidth,~] = Screen('DrawText',window,wrong,0,0);
right = 'Correct';
[rightWidth,~] = Screen('DrawText',window,right,0,0);

HideCursor %hide mouse

if instruct(1) == -1 %present first set of instructions and check for abort
    abort %experiment aborted; call abort function (below)
    return %end program
end

if demo == -1; %call demo.m and check for abort; walks subject through answers to all stimuli
    abort %experiment aborted; call abort function (below)
    return %end program
end

if instruct(2) == -1 %present second set of instructions and check for abort
    abort %experiment aborted; call abort function (below)
    return %end program
end

%Main task
passed = 0; %for tracking when subject passes learning criterion
while(~passed) %run blocks of trials, until performance meets criterion
    
    %Set up stimuli, responses, etc. for this block
    blockStim = mod(randperm(blockLength)',9)+1; %stimulus sequence for this block; blockLength trials, evenly distributed among 1:9 and randomly ordered
    data.stim = [data.stim;blockStim]; %append list of stimuli for this block to master stimulus sequence
    blockResp = zeros(blockLength,1); %response sequence for this block (to be filled in)
    blockRT = zeros(blockLength,1); %RT sequence for this block (to be filled in)
    Screen(window,'TextSize',responseSize); %set to larger font size, for response prompt and feedback
    
    for trial = 1:blockLength %loop through trials in this block
        
        %Inter-trial interval; response regions remain between trials for a sense of continuity
        Screen('FillRect',window,bground); %clear screen
        for r = 1:9 %loop through responses
            Screen('FillOval',window,brown,respBox(r,:)); %draw region for this response
        end
        Screen('Flip',window); %update display
        WaitSecs(iti); %pause before beginning this trial
        
        %Stimulus presentation
        Screen('FillRect',window,bground); %clear screen
        Screen('DrawText',window,prompt,W/2-promptWidth/2,responseSize*1.5,white); %present response prompt in top-center of screen
        Screen('PutImage', window, stimuli{blockStim(trial)}, stimrect); %draw stimulus
        for r = 1:9 %loop through responses
            Screen('FillOval',window,brown,respBox(r,:)); %draw region for this response
        end
        SetMouse(W/2,H/2,window); %place mouse in center of screen
        Screen('Flip',window); %update display
        
        %Get response
        ShowCursor; %unhide mouse
        x = getResp; %getResp waits for mouse click on a response and returns chosen location and response time
        if x(1) == -1 %experiment aborted; break but save data
            data.aborted = 1; %tag for data analysis (likely would exclude this subject)
            passed = 1; %ends block loop
            break %break trial loop
        end
        blockResp(trial) = x(1); %first component of x indicates the chosen response in 1:9
        blockRT(trial) = x(2); %second component of x indicates RT in seconds
        
        %Feedback
        HideCursor %hide mouse
        Screen('FillRect',window,bground); %clear screen
        Screen('DrawText',window,prompt,W/2-promptWidth/2,responseSize*1.5,white); %present response prompt in top-center of screen
        Screen('PutImage', window, stimuli{blockStim(trial)}, stimrect); %draw stimulus
        for r = 1:9 %loop through responses
            Screen('FillOval',window,brown,respBox(r,:)); %draw region for this response, in brown
        end
        if blockResp(trial)==data.map(blockStim(trial)) %compare response to correct response for current stimulus
            Screen('FillOval',window,green,respBox(blockResp(trial),:)); %highlight chosen/correct response in green
            Screen('DrawText',window,right,W/2-rightWidth/2,H-responseSize*2,green); %written feedback
            Screen('Flip',window); %update display
            WaitSecs(FBtime); %pause while feedback is shown
        else %incorrect response
            Screen('FillOval',window,red,respBox(blockResp(trial),:)); %highlight incorrect response in red
            Screen('DrawText',window,wrong,W/2-wrongWidth/2,H-responseSize*2,red); %written feedback
            Screen('Flip',window); %update display
            WaitSecs(wrongDelay); %brief pause while feedback is shown
            %Next, redraw everything and highlight correct response
            Screen('FillRect',window,bground); %clear screen
            Screen('DrawText',window,prompt,W/2-promptWidth/2,responseSize*1.5,white); %redraw response prompt
            Screen('PutImage', window, stimuli{blockStim(trial)}, stimrect); %redraw stimulus
            for r = 1:9 %loop through responses
                Screen('FillOval',window,brown,respBox(r,:)); %redraw region for this response, in brown
            end
            Screen('FillOval',window,red,respBox(blockResp(trial),:)); %rehighlight incorrect response in red
            Screen('DrawText',window,wrong,W/2-wrongWidth/2,H-responseSize*2,red); %redraw written feedback
            Screen('FillOval',window,green,respBox(data.map(blockStim(trial)),:)); %highlight correct response for current stimulus in green
            Screen('Flip',window); %update display
            WaitSecs(FBtime-wrongDelay); %pause for remainder of feedback time
        end
    end %end of trial loop
    
    data.resp = [data.resp;blockResp]; %append responses for this block to master response sequence
    data.RT = [data.RT;blockRT]; %append RTs for this block to master RT sequence
    
    if data.aborted==0 %skip if experiment was aborted
        errors = sum(blockResp~=data.map(blockStim)); %number of errors in this block
        if errors <= errorThreshold %compare number of errors to the threshold
            passed=1; %learning criterion met, so experiment will end
        else
            if instruct(3,errors) == -1 %show break screen and interim feedback, while checking for escape sequence
                data.aborted=1; %flag for excluding data
                passed=1; %will cause block loop to end
            end
        end
    end
    
end %end of block loop

data.elapsedTime = (clock-data.dateTime)*[0;0;1440;60;1;1/60]; %time in minutes from expt start to end (fails if spanning midnight on a new month)

%Write data
if ~isdir('data') %check whether there is a data directory
    mkdir('data'); %otherwise make one
end
indFile = ['data',num2str(data.sub),'_',machineID]; %name for file that will hold this subject's data
a = what('data'); %list of files in data directory
foundMaster = 0; %indicator of whether master datafile is in data directory
foundInd = 0; %indicator of whether individal datafile with this name is already in data directory
for i=1:length(a.mat) %loop through .mat files in data directory
    if isequal(a.mat{i},['dataMaster_',machineID,'.mat']) %compare filename to desired master file name
        foundMaster=1;
    end
    if isequal(a.mat{i},[indFile,'.mat']) %compare filename to individual file name
        foundInd=1;
    end
end
if foundInd %for some reason there's already data on this subject number
    suffix = num2str(rand); %random number, as text
    save(['data/',indFile,'-',suffix(3:6)],'data'); %saving individual data file for this subject, with 4 random digits as suffix
else save(['data/',indFile],'data'); %otherwise, save individual data file as normal
end
if foundMaster %append current data to master file
    load(['data/dataMaster_',machineID]); %mat file holds single structure array of all data collected on this machine; array name is same as filename
    eval(['dataMaster_',machineID,' = [dataMaster_',machineID,' data];']); %appending current data to array
else eval(['dataMaster_',machineID,' = data;']); %starting a master array to hold all subjects' data, beginning with current subject
end
save(['data/dataMaster_',machineID],['dataMaster_',machineID]); %first entry is filename; second is name of variable to be saved
disp('Data have been saved.');
disp(' ');

if data.aborted==0
    instruct(4,errors);  %states that experiment is complete
    getPasswd(300)  %must enter password, or wait 300 seconds, before screen clears
end
ShowCursor; %unhide mouse
Screen('CloseAll'); %close Psychtoolbox
home %clear command window (useful in case a subject is somehow still sitting there)
end

function abort
ShowCursor; %unhide mouse
Screen('CloseAll'); %relinquish screen control
disp('Experiment aborted by escape sequence') %present this text in command window
end