function UHD2_imro

% general info about UHD2 programming
% electrodes are switched on in groups 2 columns x 8 rows = 16 sites
% a groupRow in this code is a set of 4 groups spanning the shank horizontally

% colMode option is set for whole shank
% colMode options:
% -----check that these are the correct values
% 0 = INNER, active columns = {1,3,4,6}
% 1 = OUTER, active columns = {0,2,5,7}
% 2 = ALL, all columns active

% if colMode = 2, only need one call to set connection
% if colMode = 0 or 1, need to call twice, one for a bank selection
% in a standard bank, once in a 'column crossed bank'.

% standard banks: 0-3, 8-11
% column crossed banks: 4-7, 12-15
% the channels in each group are:
% for even group indices:  (n/2)*32 + [0,2,4...30]
% for odd group indices: ((n-1)/2)*32 + [1,3,5,...31]

% for SpikeGLX. requiring reference, gains, and filter selection switch to be constant for all sites.

% imro header (type, colMode, reference index, AP gain, LF gain, AP filter on/off)
% one imro table entry per group (group, bank 0, bank 1)

% with colMode = 2, 'ALL', the two banks must be equal

% ~imroTbl=(1110,2,0,500,250,1)(0 0 0)(2 0 0) ...

% with colMode = 0 or 1, each group is connected to a 'standard' bank
% and a 'column crossed' bank:

% ~imroTbl=(1110,0,0,500,250,1)(0 0 0)(2 0 0) ...

% patternType = 0 for 24 groups of 16 channels (all 384), a 'bottom group row'
%                 from 0 (bottom 24 groups, 6 rows) to 90 (top 6 rows)
% patternType = 1 all sites in a bank
% patternType = 2 center vertical stripe of 384 channels
% patternType = 3 center vertical stripe, two groups wide, bottom bank = bankChoice
%

patternType = 1;
botRow = 0;      % bottom row of groups for patternType 0
bankChoice = 0;  % bank for patternType 1, bottom bank for patternType 3
colMode = 1;     % this setting only used for patternType 2, must be 0 (inner) or 1 (outer)

% for these probes, these parameters are set for the entire shank
refElec = 0;  % 0 for external reference, 1 for tip ref
apGain = 500;
lfGain = 250;
apFilter = 1;

gb0 = zeros(1,24);
gb1 = zeros(1,24);
group = zeros(1,24);

bMapOK = 1;

switch patternType
    case 0
        % 24 groups starting from a specified row, 0-90;
        nameStr = sprintf('NPUHD2_botRow%d_ref%d', botRow, refElec);
        colMode = 2;        % ALL
        for cI = 0:5
            cL = cI*4+1;
            currRow = botRow + cI;
            rowInBank = mod(currRow,6);
            currBank = floor(currRow/6);
            % for each row, include all groups so group crossing doesn't matter
            gb0(cL:cL+3) = currBank;
            gb1(cL:cL+3) = currBank;  % ALL pattern, gb1 = gb0
            group(cL:cL+3) = rowInBank*4 + (0:3);
            % fprintf('%d,%d,%d,%d\n', currRow, currBank, rowInBank, group(cL));
        end

    case 1
        % all groups in a bank.
        nameStr = sprintf('NPUHD2_bank%d_ref%d', bankChoice, refElec);
        colMode = 2;        % ALL
        botRow = bankChoice*6;
        for cI = 0:5
            cL = cI*4+1;
            currRow = botRow + cI;
            rowInBank = mod(currRow,6);
            currBank = floor(currRow/6);
            % for each row, include all groups so group crossing doesn't matter
            gb0(cL:cL+3) = currBank;
            gb1(cL:cL+3) = currBank;  % ALL pattern, gb1 = gb0
            group(cL:cL+3) = rowInBank*4 + (0:3);
            % fprintf('%d,%d,%d,%d\n', currRow, currBank, rowInBank, group(cL));
        end

    case 2
        % single stripe survey pattern
        if ~colMode
            nameStr = sprintf('NPUHD2_inner_vstripe_ref%d', refElec);
        else
            nameStr = sprintf('NPUHD2_outer_vstripe_ref%d', refElec);
        end
        group(1:24) = 0:23;
        % manually setting banks
        gb0( [2:4:22] + 1 ) = 0; % direct column
        gb1( [2:4:22] + 1 ) = 4; % column crossed
        gb0( [0:4:20] + 1 ) = 1;
        gb1( [0:4:20] + 1 ) = 5;
        gb0( [1:4:21] + 1 ) = 3;
        gb1( [1:4:21] + 1 ) = 7;
        gb0( [3:4:23] + 1 ) = 2;
        gb1( [3:4:23] + 1 ) = 6;

   case 3
        % center coverage of two banks starting from bankChoice
        nameStr = sprintf('NPUHD2_cent_2gp_bank%d_ref%d', bankChoice, refElec);
        colMode = 2;        % ALL, all channels in each bank
        group(1:24) = 0:23;
        if mod(bankChoice,2) == 0
            % bottom bank is direct
            gb0( [2:4:22] + 1) = bankChoice;
            gb0( [1:4:21] + 1) = bankChoice + 1;
            gb0( [0:4:20] + 1) = bankChoice + 1;
            gb0( [3:4:23] + 1) = bankChoice;
        else
            % bottom bank is group crossed
            gb0( [2:4:22] + 1) = bankChoice + 1;
            gb0( [1:4:21] + 1) = bankChoice;
            gb0( [0:4:20] + 1) = bankChoice;
            gb0( [3:4:23] + 1) = bankChoice + 1;
        end
        gb1 = gb0;

end

[~,sortOrder] = sort(group);
gb0_sorted = gb0(sortOrder);
gb1_sorted = gb1(sortOrder);

% calculate positions on shank and channels
[elecInd, chan] = imroToShankMap( colMode, gb0_sorted, gb1_sorted );

% check for duplicate channels / illegal map
for i = 1:384
    if sum( chan(1:i-1)==chan(i) ) > 0
        fprintf( "duplicate channels => impossible map\n" );
        bMapOK = 0;
    end
    % fprintf("%d,%d,%d,%d\n",elec_group,bank(i),chan(i),elecInd(i));
end

if bMapOK
    % write imro table
    % open a new file wherever we are

    fileName = [nameStr,'.imro'];
    nmID = fopen(fileName,'w');

    % imro table
    % print first entry, specifying probe type and number of channels
    fprintf(nmID,'(%d,%d,%d,%d,%d,%d)', 1110, colMode, refElec, apGain, lfGain, apFilter);
    for i = 1:numel(gb0)
        fprintf(nmID,'(%d %d %d)', i-1, gb0_sorted(i), gb1_sorted(i));
    end
    fprintf(nmID, '\n');

    fclose(nmID);
    % plot
    PlotElecUHD2(elecInd);

else
    fprintf('conflicts in channel map\n');
end

end

function PlotElecUHD2( elecInd )

    % NP UHD2 electrode positions
    nElec = 6144;
    nCol = 8;
    nRow = nElec/nCol;
    vSep = 6;   % in um
    hSep = 6;

    elecPos = zeros(nElec, 2);

    % positions for all electrodes
    for row = 1:nRow
        st = (row-1)*8 + 1;
        elecPos(st:st+7,1) = hSep*(0:7);
        elecPos(st:st+7,2) = vSep*(row-1);
    end

    selPos = elecPos(elecInd+1,:);


    % make a plot of all the electrode positions
    figure(1)

    scatter( elecPos(:,1), elecPos(:,2), 30, 'k', 'square' ); hold on;
    scatter( selPos(:,1), selPos(:,2), 20, 'g', 'square', 'filled' ); hold on;

    xlim([-16,64]);
    ylim([-8,4620]);
    title('NP UHD2 view');
    hold off;

end

function [sortedElecInd, sortedChan] =  imroToShankMap( colMode, gb0, gb1 )

% for a value of colMode and a set of bank selections gb0, gb1
% for all 24 groups, calculate the chan, row, and column arrays

% even numbered banks are 'direct'
% odd numbered banks are row crossed
% standard banks: 0-3, 8-11
% column crossed banks: 4-7, 12-15
ccbanks = [4:7,12:15];

% group to column position mapping
% NEW LAYOUT -- changes due to new positions of odd group indices
groupCol_direct = [0,3,1,2];
groupCol_crossed = [1,2,0,3];

% lowest channel for each group in a set of 4, in order of group index
stChan = [0,1,32,33];

% which column of electrodes within each group are active for
% INNER and OUTER settings of colMode
% order = group position on shank, rather than group index
% NEW LAYOUT -- changes for odd groups,
if colMode == 0 % INNER
    activeCol = [1,1,0,0];  % inner column = rh for group pos 0,1, lh for groups 2,3
elseif colMode == 1 % OUTER
    activeCol = [0,0,1,1];  % inner column = lh for group pos 0,1, rh for groups 2,3
end

elecInd = zeros(1,384);     % which electrodes on the shank are selected
chan = zeros(1,384);        % which channels those map to

eI = 1;  %starting index of current elec and chan index

for group = 0:23
    bankList = [gb0(group+1), gb1(group+1)];
    for bI = 1:2
        bank = bankList(bI);
        groupRow = floor(group/4);     % groups numbered from 0 to 23, groupRow = 0 to 5, the 6 rows in a bank
        groupWithinRow = mod(group,4); % 0-3, for lowest to highest group index

        % location of this group on the probe in the groupRow = groupCol, 0-3, left to right
        if mod(bank,2) == 0   % even bank index, non group crossed
            groupCol = groupCol_direct(groupWithinRow+1);
        else
            groupCol = groupCol_crossed(groupWithinRow+1);
        end
        % fprintf('bank, group, groupCol: %d,%d,%d\n', bank, group, groupCol);
        for rowWithinGroup = 0:7

            % which physical column (lh or rh) in the group is active?
            if colMode == 2
                % ALL -- set column to 0,1 for even,odd values of eI
                colWithinGroup = bI - 1;
            else
                % INNER or OUTER -- set column according to activeCol
                colWithinGroup = activeCol(groupCol+1);
            end
            elecInd(eI) = 384*bank + groupRow*64 + rowWithinGroup*8 + groupCol*2 + colWithinGroup;

            if mod(group,2) == 0
                % even group and channel; lh = lower index for std, higher index for col crossed
                if ~ismember(bank, ccbanks)
                    % not column crossed, lower numbered channels in column 0 of the group
                    chan(eI) = groupRow*64 + stChan(groupWithinRow+1) + rowWithinGroup*4 + colWithinGroup*2;
                else
                    % if column crossed, lower numbered channels column 1 of the group
                    crCol = ~colWithinGroup;
                    chan(eI) = groupRow*64 + stChan(groupWithinRow+1) + rowWithinGroup*4 + crCol*2;
                end

            else
                % odd group and channel; lh = higher index for std, lower index for col crossed

                % highest channel index at low rowWithingroup
                if ~ismember(bank, ccbanks)
                    % not column crossed, lower numbered channels in column 1 of the group
                    chan(eI) = groupRow*64 + stChan(groupWithinRow+1) + (7-rowWithinGroup)*4 + (~colWithinGroup)*2;
                else
                    % if column crossed, lower numbered channels column 0 of the group
                    crCol = ~colWithinGroup;
                    chan(eI) = groupRow*64 + stChan(groupWithinRow+1) + (7-rowWithinGroup)*4 + (~crCol)*2;
                end

            end

            eI = eI + 1;
        end
    end
end

[sortedElecInd, sortOrder] = sort(elecInd);
sortedChan = chan(sortOrder);

fprintf('Electrode Index, row, col, chan, calculated elecInd\n');
f_to = fopen('test_out.txt','w');
fprintf(f_to,'chan\telectrode\n');
for i = 1:384
    curr = sortedElecInd(i);
    row = floor(curr/8);
    col = mod(curr,8);
    % just for checking the chanToElec function
    calc_elecInd = chanToElec(sortedChan(i), colMode, gb0, gb1);
    fprintf('%d,%d,%d,%d,%d\n', curr,row,col,sortedChan(i), calc_elecInd);
    calc_elecInd = chanToElec(i-1, colMode, gb0, gb1);
    fprintf(f_to,'%d\t%d\n', i-1, calc_elecInd);
end

fclose(f_to);

end

function elecInd =  chanToElec( chan, colMode, gb0, gb1 )

% for a value of chan and {colMode, {gb0}, {gb1}}
% {gb0} and {gb1} are arrays of the bank indices for all 24 groups

% Calculate group from channel
% The channels in each group are:
% for even group/channel indices:  (n/2)*32 + [0,2,4...30]
% for odd group/channel indices:   ((n-1)/2)*32 + [1,3,5,...31]
groupEven = ~mod(chan,2);
if groupEven
    group = 2*floor(chan/32);
    colLowChan = ~mod(chan/2,2);  % is this channel in the low index half of the group
else
    group = 2*floor(chan/32) + 1;
    colLowChan = ~mod((chan-1)/2,2);
end

% even numbered banks are 'direct'
% odd numbered banks are row crossed
% standard banks: 0-3, 8-11
% column crossed banks: 4-7, 12-15
ccbanks = [4:7,12:15];

% is gb0(group) or gb1(group) the bank for this channel?
% NEW LAYOUT -- changes which columns are active for odd groups
% for colMode = 0, INNER
%   (groupEven, colLowChan) = (1,1) => bank = cc
%                             (0,1) => bank = cc
%                             (1,0) => bank = std
%                             (0,0) => bank = std
% For colMode = 1, OUTER
%   (groupEven, colLowChan) = (1,1) => bank = std
%                             (0,1) => bank = std
%                             (1,0) => bank = cc
%                             (0,0) => bank = cc

% if colMode = 2, bank = gb0(group)

if colMode < 2
    % which bank is direct and which is cc
    bankList = [gb0(group+1), gb1(group+1)];
    cc_curr = [ismember(bankList(1),ccbanks), ismember(bankList(2),ccbanks)];
    if sum(cc_curr) == 1
        % correct assignment with one each standard and column-crossed bank
    else
        % incorrect assignment,
        fprintf( 'columnMode %d requires a standard and column crossed bank\n', colMode );
        chan = -1;
        return;
    end

    colSwitch = sprintf('%d%d',groupEven,colLowChan);
    % NEW LAYOUT -- changes which columns are active for odd groups
    switch colSwitch
        case '11'
            ccb = 1;  % lower index chans are inner on ccb for odd and even w/ new layout
        case '01'
            ccb = 1;
        case '10'
            ccb = 0;  % higer index chans are inner on std banks for odd and even w/ new layout
        case '00'
            ccb = 0;
    end

    % if column selection is 1, inner, need opposite choice for column crossed or not
    if colMode == 1
        ccb = ~ccb;
    end

    bank = bankList(cc_curr == ccb);

else
    % fprintf('group: %d\n',group)
    bank = gb0(group+1);
    ccb = ismember(bank,ccbanks);
end



% group to column position mapping
% NEW LAYOUT -- changes position of odd groups
groupCol_direct = [0,3,1,2];
groupCol_crossed = [1,2,0,3];

groupRow = floor(group/4);     % groups numbered from 0 to 23, groupRow = 0 to 5, the 6 rows in a bank
groupWithinRow = mod(group,4); % 0-3, for lowest to highest group index

% location of this group on the probe in the groupRow = groupCol, 0-3, left to right
if mod(bank,2) == 0   % even bank index, non group crossed
    groupCol = groupCol_direct(groupWithinRow+1);
else
    groupCol = groupCol_crossed(groupWithinRow+1);
end


chanInGroupRow = chan - groupRow*64;

if mod(group,2) == 0
    rowWithinGroup = floor(mod(chanInGroupRow,32)/4);
    colWithinGroup = ~colLowChan;
else
    % channels within group rotated for odd groups
    rowWithinGroup = 7-floor(mod(chanInGroupRow,32)/4);
    colWithinGroup = colLowChan;
end

% both even and odd groups swap columns for column crossed banks
if ccb
    colWithinGroup = ~colWithinGroup;
end

elecInd = 384*bank + groupRow*64 + rowWithinGroup*8 + groupCol*2 + colWithinGroup;

end


