% Visual stimuli to peripheral visual field
%
% See also:
%   ../online_BCI.m
%   system/sys_genStimDesign.m
%   system/sys_genFlickCode.m
%   system/sys_preloadStimuli.m
%   system/sys_visualFeedback.m
%   system/sys_trigTranslate.m
%   signal_processing/online_signalProcess.m
%   signal_processing/online_cca.m
%   signal_processing/online_fbcca.m
%   signal_processing/online_cca_itd.m
%
% Masaki Nakanishi, Swartz Center for Computational Neuroscience, Institute
% for Neural Computation, University of California, San Dieg
% Date: Feb-11-2015, Update: Feb-12-2015.

fprintf('PERIPHERAL-STIMULATION:\n');

%% Parameter configuration (User parameters)

% Application setting
STIM_DUR            = 4;                    % Stimulation duration [s]
WAIT_DUR            = 1;                    % Gaze shift duration [s]
NUM_TRAINS          = 1;                    % Number of training session

% Hardware setting
IO_TYPE             = 'parallel';           % I/O Type
IO_ADDRESS          = 'D010';               % I/O address

%% Define constant values

% Font and color parameters
WHITE               = [255, 255, 255];
BLACK               = [  0,   0,   0];
RED                 = [255,   0, 102];
BG_COLOR            = BLACK;
TEXT_FONT           = 'Helvetica';
FONT_SIZE           = 24;

% Variables for the script
cntInput            = 1;
curIndex            = 0;
fb_seq              = [];
fb_index            = [];
target              = 1;
numFrameLoss        = [];
actualStimDur       = [];
cntFlip             = [];

try
    
    % Parallel port for sending an event trigger
    fprintf('PERIPHERAL-STIMULATION: Trying to make a parallel I/O connection.\n');
    parallelObj = io64;
    status = io64(parallelObj);
    IO_ADDRESS = hex2dec(IO_ADDRESS);
    io64(parallelObj, IO_ADDRESS, 0);
    fprintf('PERIPHERAL-STIMULATION: Parallel I/O port connection (LPT address: %x) was successfully done.\n', IO_ADDRESS);
    
    %% Prepare for opening stimulation screen
    
    fprintf('PERIPHERAL-STIMULATION: Creating windows.\n');
    
    HideCursor;
    KbName('UnifyKeyNames');
    escKey = KbName('ESCAPE');
    enterKey = KbName('Return');
    
    % If your PC's spec is not good enough, try the following.
    %Screen('Preference', 'SkipSyncTests', 1);
    oldDebugLevel = Screen('Preference', 'VisualDebuglevel', 3);
    
    % If there are multiple displays, one without menu bar is the best
    % choice. Display #0 has the menu bar.
    screens = Screen('Screens');
    screenNumber = max(screens);
    
    % Open a window.
    [window, windowRect] = Screen('OpenWindow', screenNumber, BG_COLOR, [], [], 2);
    [centX, centY] = RectCenter(windowRect);
    
    % Get a refresh rate of display.
    refreshRateHz = 1/Screen('GetFlipInterval', window);
    fprintf('PERIPHERAL-STIMULATION: Refresh rate of on-screen window = %d [Hz].\n',refreshRateHz);
    
    % Get a stimulus design
    design = sys_genStimDesign(refreshRateHz, [windowRect(3), windowRect(4)], STIM_DUR);
    design.CentWindow = [centX, centY];
    fprintf('PERIPHERAL-STIMULATION: Loading BCI parameter successed.\n');
    
    %% Create off-screen windows based on the stimulus design --------------
    
    % Create offscreen for start screen ---------------------------------------
    startScreen = Screen(window, 'OpenOffScreenWindow', BG_COLOR);
    Screen(startScreen, 'TextColor', WHITE);
    Screen(startScreen, 'TextFont', TEXT_FONT);
    Screen(startScreen, 'TextSize', FONT_SIZE);
    startMsg = 'Are you ready? Press the "ENTER" key to start spelling.';
    bounds = Screen(startScreen, 'TextBounds', startMsg);
    Screen('DrawText', startScreen, startMsg, centX-bounds(RectRight)/2, centY-bounds(RectBottom)/2, WHITE);
    
    % Create offscreen for stimulation ----------------------------------------
    for stim_i = 1:1:design.NumStim
        % Set coordinates of rectangle vertex
        % [left upper X, left upper Y, right lower X, right lower Y]
        design.StimLoc{stim_i} = [...
            design.CenterLoc{stim_i}(1) + centX - design.hLenSide{stim_i}/2,...
            design.CenterLoc{stim_i}(2) + centY - design.vLenSide{stim_i}/2,...
            design.CenterLoc{stim_i}(1) + centX + design.hLenSide{stim_i}/2,...
            design.CenterLoc{stim_i}(2) + centY + design.vLenSide{stim_i}/2];
    end % targ_i
    
    for targ_i = 1:1:design.NumTarg
        design.PointLoc{targ_i} = [...
            design.TargLoc{targ_i}(1) + centX - design.RadTarg,...
            design.TargLoc{targ_i}(2) + centY - design.RadTarg,...
            design.TargLoc{targ_i}(1) + centX + design.RadTarg,...
            design.TargLoc{targ_i}(2) + centY + design.RadTarg];
    end % targ_i
    
    % Set offscreen for stimulation
    fillColor = cell2mat(design.FlickCode');
    offScreen = zeros(1, design.LenCode);
    for win_i = 1:1:design.LenCode
        
        % Open off-screens
        stimParam = struct(...
            'FillColor',    fillColor(:, win_i),...
            'FrameColor',   fillColor(:, win_i),...
            'TextColor',    BLACK,...
            'TextFont',     TEXT_FONT,...
            'TextSize',     FONT_SIZE);        
        offScreen(win_i) = Screen(window, 'OpenOffScreenWindow', BG_COLOR);
        sys_preloadStimuli('stimuli', offScreen(win_i), design, stimParam);      
        
    end % win_i
    
    % Set offscreen for gaze shift
    stimParam = struct(...
        'FillColor',    BLACK,...
        'FrameColor',   WHITE,...
        'TextColor',    WHITE,...
        'TextFont',     TEXT_FONT,...
        'TextSize',     FONT_SIZE);    
    blankScreen = Screen(window, 'OpenOffScreenWindow', BG_COLOR);
    sys_preloadStimuli('blank', blankScreen, design, stimParam);
    
    fprintf('PERIPHERAL-STIMULATION: Finished preparing all on- and off-screens.\n');
    
    %% Main loop
    
    % Start screen ------------------------------------------------------------
    fprintf('PERIPHERAL-STIMULATION: Show preparation screen.\n');
    
    while true
        
        % If 'ESC' key is pressed, the iteration will be finished.
        [~, ~, keyCode] = KbCheck;
        if keyCode(enterKey), break; end
        
        for win_i = 1:1:design.LenCode           
            % If 'ESC' key is pressed, the iteration will be finished.
            [~, ~, keyCode] = KbCheck;
            if keyCode(enterKey), break; end            
            Screen('CopyWindow', offScreen(win_i), window);            
        end % win_i
        
        % Show start screen
        Screen('CopyWindow', startScreen, window);
        Screen('Flip', window);
        
    end
    
    % -------------------------------------------------------------------------
    % Stimulation only
    % -------------------------------------------------------------------------
    for train_i = 1:1:NUM_TRAINS
        
        % Pseudo-random integer sequence for randomize the order of stimulation
        randi_seq = randperm(design.NumTarg);
        
        for targ_i = 1:1:design.NumTarg
            
            % Preparing -----------------------------------------------
            tmpFrameLoss = [];
            tmpCntFlip = 0;
            
            % If 'ESC' key is pressed, the iteration will be finished.
            [~, ~, keyCode] = KbCheck;
            if keyCode(escKey), break; end
            
            % Timer for each trial
            trialStart = tic;
            
            % Set next target
            cue = randi_seq(targ_i);
            
            % Gaze shift ----------------------------------------------
            Screen('FillOval', blankScreen, RED, design.PointLoc{cue});
            Screen('CopyWindow', blankScreen, window);
            Screen('Flip', window);
            
            % Wait for WAIT_DUR (s)
            while toc(trialStart) <= WAIT_DUR
                
                % If 'ESC' key is pressed, the iteration will be finished.
                [~, ~, keyCode] = KbCheck;
                if keyCode(escKey), break; end
                
            end % win_i            
            Screen('FillOval', blankScreen, WHITE, design.PointLoc{cue});
            
            % Gazing (Stimulation) ------------------------------------
            stimTimer = tic;
            sys_sendEventTrig(IO_TYPE, parallelObj, cue, IO_ADDRESS);
            for win_i = 1:1:design.LenCode
                
                % If 'ESC' key is pressed, the iteration will be finished.
                [~, ~, keyCode] = KbCheck;
                if keyCode(escKey), break; end
                
                % Stimulation
                Screen('CopyWindow', offScreen(win_i), window);
                [~, ~, ~, tmp] = Screen('Flip', window);
                
                % For stimulus check
                tmpFrameLoss = [tmpFrameLoss, tmp];
                tmpCntFlip = tmpCntFlip + 1;
                
            end % win_i
            sys_sendEventTrig(IO_TYPE, parallelObj, 0, IO_ADDRESS);
            
            timeOfStimulation = toc(stimTimer);
            fprintf('PERIPHERAL-STIMULATION: It took %.3f [ms] for stimulation.\n', timeOfStimulation*1000);
            
            timeOfOneTrial = toc(trialStart);
            fprintf('PERIPHERAL-STIMULATION: It took %.3f [ms] for a trial.\n', timeOfOneTrial*1000);
            
            % For stimulus check
            numFrameLoss = [numFrameLoss, sum(tmpFrameLoss>0)];
            actualStimDur = [actualStimDur, timeOfStimulation];
            cntFlip = [cntFlip, tmpCntFlip];
            
        end % targ_i
    end % train_i
    
    
    fprintf('PERIPHERAL-STIMULATION: Online BCI has done.\n');
    
    ShowCursor;
    Screen('CloseAll');
    if strcmp(IO_TYPE, 'serial')
        fclose(serialObj);
    end
    Screen('Preference', 'VisualDebuglevel', oldDebugLevel);
    clear all
    
catch
    
    % This section executes in case of an error in the "try" section.
    fprintf('PERIPHERAL-STIMULATION: An error was ocurred in "try" section.\n');
    
    ShowCursor;
    Screen('CloseAll');
    if strcmp(IO_TYPE, 'serial')
        fclose(serialObj);
    end
    Screen('Preference', 'VisualDebuglevel', oldDebugLevel);
    clear all
    
end
