function test_suite = test_surficial_io_gifti
% tests for GIFTI input/output
%
% #   For CoSMoMVPA's copyright information and license terms,   #
% #   see the COPYING file distributed with CoSMoMVPA.           #
    try % assignment of 'localfunctions' is necessary in Matlab >= 2016
        test_functions=localfunctions();
    catch % no problem; early Matlab versions can use initTestSuite fine
    end
    initTestSuite;


function test_gifti_dataset_io()
    if cosmo_skip_test_if_no_external('gifti')
        return;
    end

    ds=cosmo_synthetic_dataset('ntargets',2,'nchunks',1,...
                        'type','surface');
    ds.a.fdim.values{1}=1+[1 4 7 8 5 3];

    encodings=get_gifti_encodings();
    orders=get_indexing_orders();

    fn=cosmo_make_temp_filename('_tmp','.gii');
    cleaner=onCleanup(@()delete(fn));

    % only test subset
    ntest=2;

    n_encodings=numel(encodings);
    n_orders=numel(orders);

    rp=randperm(n_encodings*n_orders);
    test_idxs=rp(1:ntest);

    counter=0;
    for k=1:numel(encodings)
        encoding=encodings{k};
        for j=1:numel(orders)
            counter=counter+1;
            if ~any(test_idxs==counter)
                continue;
            end

            order=orders{j};

            cosmo_map2surface(ds,fn,'encoding',encoding);
            ds2=cosmo_surface_dataset(fn);
            assert_dataset_equal(ds,ds2);

            g_str=get_gifti_string(encoding, order);
            fid=fopen(fn,'w');
            fprintf(fid,'%s',g_str);
            fclose(fid);

            ds3=cosmo_surface_dataset(fn);
            assert_dataset_equal(ds,ds3);

            g=gifti(fn);
            assertElementsAlmostEqual(double(g.cdata)',ds.samples,...
                                            'absolute',1e-5);
            node_indices=ds.a.fdim.values{1}(ds.fa.node_indices);
            assertEqual(double(g.indices)',node_indices);

            g2=cosmo_map2surface(ds,'-gii');
            assertElementsAlmostEqual(g.cdata,g2.cdata,...
                                            'absolute',1e-5);
            assertEqual(g.indices,g2.indices);
        end
    end

function assert_dataset_equal(x,y)
    assertElementsAlmostEqual(x.samples,y.samples,'absolute',1e-5);
    assertEqual(sort(fieldnames(x)),sort(fieldnames(y)));
    assertEqual(x.fa,y.fa);
    assertEqual(x.a,y.a);



function string=get_gifti_string(encoding, indexing_order)
    header=get_header();
    body=get_data_arrays(encoding, indexing_order);
    footer=get_footer();

    string=[header body footer];


function string=get_header()
    string=sprintf(['<?xml version="1.0" encoding="UTF-8"?>\n'...
                '<!DOCTYPE GIFTI SYSTEM "http://gifti.projects.nitr'...
                        'c.org/gifti.dtd">\n'...
                '<GIFTI Version="1.0"  NumberOfDataArrays="3">\n'...
                '   <MetaData>\n'...
                '      <MD>\n'...
                '         <Name><![CDATA[UniqueID]]></Name>\n'...
                '         <Value><![CDATA[XYZ_Uj6iYZUAV3HKbGirxcn-c'...
                        'A]]></Value>\n'...
                '      </MD>\n'...
                '   </MetaData>\n'...
                '   <LabelTable/>\n'...
                ]);

function string=get_footer()
    string='</GIFTI>';

function encodings=get_gifti_encodings()
    encodings={'ASCII','Base64Binary','GZipBase64Binary'};

function orders=get_indexing_orders()
    orders={'ColumnMajorOrder','RowMajorOrder'};


function string=get_data_array_helper(data_str, encoding, data_type, ...
                                               indexing_order, intent)
    check_one('encoding',encoding,...
                    get_gifti_encodings());
    check_one('data_type',data_type,...
                    {'NIFTI_TYPE_INT32','NIFTI_TYPE_FLOAT32'});
    check_one('indexing_order',indexing_order,...
                    get_indexing_orders());



    string=sprintf([...
                '   <DataArray Intent="%s"\n'...
                '              DataType="%s"\n'...
                '              ArrayIndexingOrder="%s"\n'...
                '              Dimensionality="1"\n'...
                '              Dim0="6"\n'...
                '              Encoding="%s"\n'...
                '              Endian="LittleEndian"\n'...
                '              ExternalFileName=""\n'...
                '              ExternalFileOffset="">\n'...
                '      <MetaData>\n'...
                '         <MD>\n'...
                '            <Name><![CDATA[Name]]></Name>\n'...
                '            <Value><![CDATA[numeric]]></Value>\n'...
                '         </MD>\n'...
                '      </MetaData>\n'...
                '      <Data>\n%s'...
                '      </Data>\n'...
                '   </DataArray>\n'...
                ], intent, data_type, indexing_order, encoding, data_str);

function string=get_data_arrays(encoding, indexing_order)
    switch encoding
        case 'ASCII'
            data={sprintf([ '        2.031686\n'...
                            '        -3.684948\n'...
                            '        -1.050449\n'...
                            '        1.349442\n'...
                            '        -0.261723\n'...
                            '        -0.203955\n'...
                  ]),...
                  sprintf([ '        0.583806\n'...
                            '        1.723501\n'...
                            '        -1.326491\n'...
                            '        -0.397334\n'...
                            '        2.338657\n'...
                            '        0.482345\n'...
                  ]),...
                  sprintf([ '         1 \n'...
                            '         4  \n'...
                            '         7 \n'...
                            '         8 \n'...
                            '         5 \n'...
                            '         3 \n'...
                  ])};
        case 'Base64Binary'
            data={'JQcCQDDWa8AddYa/hLqsP48Ahr6U2VC+',...
                  'T3QVP66b3D91yqm/XW/Lvo6sFUDt9fY+',...
                  'AQAAAAQAAAAHAAAACAAAAAUAAAADAAAA',...
                  };
        case 'GZipBase64Binary'
            data={  'eAEBGADn/yUHAkAw1mvAHXWGv4S6rD+PAIa+lNlQvnPfCu4=',...
                    'eAEBGADn/090FT+um9w/dcqpv11vy76OrBVA7fX2PpcYDR0=',...
                    'eAEBGADn/wEAAAAEAAAABwAAAAgAAAAFAAAAAwAAAAGEAB0='...
                    };
    end

    narrays=numel(data);
    arrays_cell=cell(1,narrays);

    data_types={'NIFTI_TYPE_FLOAT32',...
                'NIFTI_TYPE_FLOAT32',...
                'NIFTI_TYPE_INT32',...
                };
    intents={'NIFTI_INTENT_NONE',...
                'NIFTI_INTENT_NONE',...
                'NIFTI_INTENT_NODE_INDEX'};

    assert(numel(data_types)==narrays);
    assert(numel(intents)==narrays);


    for k=1:narrays
        arrays_cell{k}=get_data_array_helper(data{k}, encoding, ...
                                            data_types{k}, ...
                                            indexing_order, intents{k});
    end

    string=cat(2,arrays_cell{:});

function check_one(label, value, allowed)
    if isempty(strmatch(value, allowed))
        error('illegal value for %s: %s', label, value);
    end
