function edf_header = edf_info(fname)
% EDF_INFO.M
% provides a structure containing all elements found in the header of the
% specified .edf file.
% Usage:
% edfheader = edf_info(filename)
%   where filename is the name of the EDF file
%
% GJ May 2006
% modified July 2006 to force all parameters to lower case (PyHST generates
% a number of different variations!
%
% Modified by Nicola Vigano, 07/12/2011, nicola.vigano@esrf.fr

    %%% Fields pattern
    pattern = '(?<parameter>.*?)=(?<value>.*);';

    edf_check(fname)

    %%% Read the header
    fid = fopen(fname, 'rt');

    if (fid == -1)
        error('EDF:file_not_found', ['Could not open ' fname]);
    end

    edf_header = [];
    end_of_header = [];
    edf_header.('headerlength') = 0;
    while (isempty(end_of_header))
        % Split lines
        htxt = fgetl(fid);

        % Check for inconsistencies in lines
        if (isnumeric(htxt))
            % Possible empty file! or at least malformed ;-)
            % Close Resource
            close(fid)

            error('EDF:empty_line_in_header', 'Found an empty line in header.');
        end

        tmp = regexp(htxt, pattern, 'names');
        if (~isempty(tmp))
            field = sfCleanField(lower(strtrim(tmp.parameter)));

            try
                edf_header.(field) = strtrim(tmp.value);
            catch mexc
                gtPrintException(mexc)
                warning('EDF:malformed_field', 'Didn''t understand this header');
                disp(field);
            end
        end

        % Look for end of header reached
        end_of_header = find( htxt == '}' );
        % Plus 1 is for the newline
        edf_header.headerlength = edf_header.headerlength + length(htxt) +1;
    end

    % Count the bytes of the header, and check if it is a standard header
    if (mod(edf_header.headerlength, 512) ~= 0)
        warning('EDF:wrong_header_size', ...
            'Header is not multiple of 512 bytes in %s (%d instead)!\n', ...
            fname, edf_header.headerlength);
%         disp('Setting correctly!')
%         % XXX - may be cause of errors in edf_read. Check it!
%         filePosition = round(filePosition/512) * 512;
    end

    % Release Resource
    fclose(fid);

    edf_header = sfCleanupHeaderInfo(edf_header);
end

function field_str = sfCleanField(field_str)
    % if header field has weird characters (spaces, parentheses) - this removes
    % them
    field_str( find(field_str == ' ') ) = '_';
    field_str( find(field_str == '-') ) = '_';
    field_str( find(field_str == ',') ) = '_';
    field_str( find(field_str == '(') ) = [];
    field_str( find(field_str == ')') ) = [];
end

function edf_header = sfCleanupHeaderInfo(edf_header)
    % convert to numbers any fields that I know should not be strings
    numeric_fields = { ...
        'dim_1', 'dim_2', 'dim_3', 'size', 'count_time', 'timestamp_ms', ...
        'col_beg', 'col_end', 'row_beg', 'row_end', 'col_bin', 'row_bin', ...
        'energy', 'optic_used', 'acq_frame_nb', 'point_no', 'scan_no', ...
        'run', 'image' ...
        };

    for ii = 1:length(numeric_fields)
        if isfield(edf_header, numeric_fields{ii})
            value = edf_header.(numeric_fields{ii});
            value = str2double(value);
            edf_header.(numeric_fields{ii}) = value;
        end
    end

    % convert all the motors and counters to individual fields
    coupled_fields = { 'motor', 'counter' };

    for ii = 1:length(coupled_fields)
        field_pos = [coupled_fields{ii} '_pos'];
        field_mne = [coupled_fields{ii} '_mne'];

        if isfield(edf_header, field_pos)
            field_tmp = [];
            token_pos = textscan(edf_header.(field_pos), '%f');
            token_name = textscan(edf_header.(field_mne), '%s');
            for jj = 1:length(token_pos{1})
                field_tmp.(token_name{1}{jj}) = token_pos{1}(jj);
            end
            edf_header.(coupled_fields{ii}) = field_tmp;
            edf_header = rmfield(edf_header, {field_pos, field_mne} );
        end
    end

    % convert endianness to standard representation
    switch (edf_header.byteorder)
        case 'HighByteFirst'
            edf_header.byteorder = 'b';
        case 'LowByteFirst'
            edf_header.byteorder = 'l';
        otherwise
            disp('No endian-ness specified');
    end

    % convert datatype to matlab standard representation
    switch lower(edf_header.datatype)
        case {'doublevalue'}
            edf_header.datatype = 'float64';
        case {'float','floatvalue','real'}
            edf_header.datatype = 'float32';
        case {'unsigned64'}
            edf_header.datatype = 'uint64';
        case {'signed64'}
            edf_header.datatype = 'int64';
        case {'unsignedinteger','unsignedlong'}
            edf_header.datatype = 'uint32';
        case {'signedinteger','signedlong'}
            edf_header.datatype = 'int32';
        case {'unsignedshort'}
            edf_header.datatype = 'uint16';
        case {'signedshort'}
            edf_header.datatype = 'int16';
        case {'unsignedbyte'}
            edf_header.datatype = 'uint8';
        case {'signedbyte'}
            edf_header.datatype = 'int8';
        otherwise
            edf_header.datatype = [];
            warning('EDF:unknown_type', ...
                'Type "%s" not known', edf_header.datatype);
    end
end

