function datasetId = h5datacreate(h5file,varname,varargin)
%H5datacreate  Create HDF5 dataset.
%
%   H5DATACREATE(HFILE,VARNAME,parameter,value,...) creates a dataset
%   named VARNAME.  HFILE may be either a path to the HDF5 file or a file 
%   ID to an already opened file.  
%
%   If VARNAME is a full pathname, all 
%   intermediate groups are created if they don't already exist.
%
%   parameter
%     'size'        - size of the dataset
%     'chunk_size'  - size of the chunks of the dataset
%     'compression' - 0-9  compression level
%     'type'        - type of the dataset to create.  This can be a string
%                     such as 'double', in which case the datatype maps to
%                     'H5T_NATIVE_DOUBLE', or it can be a derived HDF5 
%                     datatype.
%     'max_size'    - the maximum size of the dataset to create
%     'fill'        - the fill value
%
%
%   Example:  create a 10x20 single precision dataset named 'DS1'.
%       h5filecreate('myfile.h5');
%       h5datacreate('myfile.h5','DS1','type','single','size',[10 20]);
%
%   Example:  create a 4x1 string dataset where each string has length
%   8 and is null-terminated.
%       mytype = H5T.copy('H5T_C_S1');
%       H5T.set_size(mytype,8);
%       H5T.set_strpad(mytype,'H5T_STR_NULLTERM');
%       h5datacreate('myfile.h5','/path/to/DS2','type',mytype,'size',4);
%
%   Credit where credit is due:  Philip Top at LLNL
%
%   See also h5filecreate.

%   Copyright 2010 The MathWorks, Inc.

p = inputParser;
p.addParamValue('size',0,@isnumeric);
p.addParamValue('chunk_size',0,@isnumeric);
p.addParamValue('compression',0,@isnumeric);
p.addParamValue('type',H5T.copy('H5T_NATIVE_DOUBLE'),@(x)ischar(x) || isa(x,'H5ML.id'));
p.addParamValue('max_size',0,@isnumeric);
p.addParamValue('fill',0,@isnumeric);
p.parse(varargin{:});
params = p.Results;
params = validate_params(h5file,varname,params);


flags = 'H5F_ACC_RDWR';
fapl  = 'H5P_DEFAULT'; 

if ischar(h5file)
    file_id = H5F.open(h5file,flags,fapl);
else
    file_id=h5file;
end



params = set_dataspace_id (params);
params = set_datatype_id (params);
params = set_dcpl(params);

datasetId = set_data_id(file_id,varname,params);

H5S.close(params.dataspaceId);
H5T.close(params.datatypeId);
H5P.close(params.dcpl);


if ischar(h5file)
    H5F.close(file_id);
end





%==========================================================================
function params = set_dcpl(params)
% Setup the dataset creation property list.  

params.dcpl = H5P.create('H5P_DATASET_CREATE'); % create property list
if (~isequal(params.chunk_size,0))
    % set chunk size
    H5P.set_chunk(params.dcpl,fliplr(params.chunk_size)); 
end
if (params.compression~=0)
    % set gzip compression level
    H5P.set_deflate(params.dcpl,params.compression);
end
if (params.fill~=0)
    % set fill value
    H5P.set_fill_value(params.dcpl, params.datatypeId, params.fill.*ones(params.type));
end
return







 
%==========================================================================
% SET_DATASET_ID
%
% Setup the dataet ID.  We need to check as to whether or not the dataset
% already exists.
function datasetID = set_data_id(fid,dataname,params)

v = version('-release');
switch(v)
	case { '2007b', '2008a', '2008b' }
		% Give it our best shot.
		datasetID = H5D.create(fid,dataname,...
                       params.datatypeId, params.dataspaceId, ...
                       params.dcpl);

	otherwise
	
		% create any intermediate groups along the way.
		params.lcpl = H5P.create('H5P_LINK_CREATE');
		H5P.set_create_intermediate_group(params.lcpl,1);
		dapl = 'H5P_DEFAULT';


		% need to use the 1.8.x version of H5D.create for this.
		datasetID = H5D.create(fid,dataname,...
                       params.datatypeId, params.dataspaceId, ...
                       params.lcpl,params.dcpl,dapl);

end

return

%===============================================================================
% SET_DATASPACE_ID
%
% Setup the dataspace ID.

function params = set_dataspace_id (params)

params.dataspaceId=H5S.create('H5S_SIMPLE');
H5S.set_extent_simple(params.dataspaceId,length(params.size),...
    fliplr(params.size),fliplr(params.max_size));
return



%===============================================================================
% SET_DATATYPE_ID
%
% We need to choose an appropriate HDF5 datatype
function params = set_datatype_id (params)
if ischar(params.type)
    switch params.type
        case 'double'
            params.datatypeId = H5T.copy('H5T_NATIVE_DOUBLE');
        case {'single','float'}
            params.datatypeId = H5T.copy('H5T_NATIVE_FLOAT');
        case 'int64'
            params.datatypeId = H5T.copy('H5T_NATIVE_LLONG');
        case 'uint64'
            params.datatypeId = H5T.copy('H5T_NATIVE_ULLONG');
        case {'int32','int'}
            params.datatypeId = H5T.copy('H5T_NATIVE_INT');
        case {'uint32','uint'}
            params.datatypeId = H5T.copy('H5T_NATIVE_UINT');
        case {'int16','short'}
            params.datatypeId = H5T.copy('H5T_NATIVE_SHORT');
        case 'uint16'
            params.datatypeId = H5T.copy('H5T_NATIVE_USHORT');
        case 'int8'
            params.datatypeId = H5T.copy('H5T_NATIVE_SCHAR');
        case 'uint8'
            params.datatypeId = H5T.copy('H5T_NATIVE_UCHAR');
        case 'complex'
            % special case for complex data.  We'll actually create 3 
            % datasets.  One to hold the real data, one to hold the 
            % complex data, and one to hold references to both.
            params.datatypeId = H5T.copy('H5T_STD_REF_OBJ');
        otherwise
            error('hdf5tools:H5DATACREATE:unsupportedDatatype', ...
                '''%s'' is not a supported H5DATACREATE datatype.\n', params.type );
    end
    return
end

if isa(params.type,'H5ML.id')
    params.datatypeId = H5T.copy(params.type);
    return
end



%--------------------------------------------------------------------------
function params = validate_params(hfile,varname,params)

if (params.size == 0 )
    error('hdf5tools:h5datacreate:zeroSize', ...
        'Size must be specified and be greater than zero.');
end


if ~ischar(hfile)
    if ~isequal(class(hfile),'H5ML.id')
        error('hdf5tools:H%DATACREATE:badDatatype', ...
            'Filename input argument must have datatype char.' );
        
    end
end

if ~ischar(varname)
    error('hdf5tools:H5DATACREATE:badDatatype', ...
        'VARNAME input argument must have datatype char.' );
end



if (~isequal(params.chunk_size,0))
    if (numel(params.chunk_size)~=numel(params.size))
        error('dataset size and chunk size have different dimensions');
    end
end

if (~isequal(params.max_size,0))
    if (numel(params.max_size)~=numel(params.size))
        error('dataset max size and initial size have different dimensions');
    elseif (any(isinf(params.max_size)))
        mxs=cell(size(params.max_size));
        for kk=1:length(params.max_size)
            if (isinf(params.max_size(kk)))
                mxs{kk}='H5S_UNLIMITED';
            else
                mxs{kk}=params.max_size(kk);
            end
        end
        params.max_size = mxs;
    end
        
else
    params.max_size=params.size;
end

if ((params.compression<0)||(params.compression>9))
    error('invalid compression level');
    
else
    if (params.compression>0)
        if (isequal(params.chunk_size,0))
            params.chunk_size=params.size;
        end
    end
end
return







