/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
 * Copyright by The HDF Group.                                               *
 * All rights reserved.                                                      *
 *                                                                           *
 * This file is part of HDF5.  The full HDF5 copyright notice, including     *
 * terms governing use, modification, and redistribution, is contained in    *
 * the LICENSE file, which can be found at the root of the source code       *
 * distribution tree, or in https://www.hdfgroup.org/licenses.               *
 * If you do not have access to either file, you may request a copy from     *
 * help@hdfgroup.org.                                                        *
 * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */

#include <string>

#include "H5Include.h"
#include "H5Exception.h"
#include "H5IdComponent.h"
#include "H5PropList.h"
#include "H5OcreatProp.h"
#include "H5DcreatProp.h"
#include "H5DxferProp.h"
#include "H5LcreatProp.h"
#include "H5LaccProp.h"
#include "H5DaccProp.h"
#include "H5Location.h"
#include "H5Object.h"
#include "H5Alltypes.h"
#include "H5AbstractDs.h"
#include "H5DataSpace.h"
#include "H5DataSet.h"

namespace H5 {

//--------------------------------------------------------------------------
// Function:    CompType default constructor
///\brief       Default constructor: Creates a stub compound datatype
//--------------------------------------------------------------------------
CompType::CompType() : DataType()
{
}

//--------------------------------------------------------------------------
// Function:    CompType copy constructor
///\brief       Copy constructor: same HDF5 object as \a original
///\param       original - IN: Original CompType instance
//--------------------------------------------------------------------------
CompType::CompType(const CompType &original) : DataType(original)
{
}

//--------------------------------------------------------------------------
// Function:    CompType overloaded constructor
///\brief       Creates a CompType object using the id of an existing datatype.
///\param       existing_id - IN: Id of an existing compound datatype
//--------------------------------------------------------------------------
CompType::CompType(const hid_t existing_id) : DataType(existing_id)
{
}

//--------------------------------------------------------------------------
// Function:    CompType overloaded constructor
///\brief       Creates an empty compound datatype given a size, in bytes.
///\param       size - IN: Number of bytes in the datatype to create
///\exception   H5::DataTypeIException
// Description
//              The DataType constructor calls the C API H5Tcreate to create
//              the compound datatype.
//--------------------------------------------------------------------------
CompType::CompType(size_t size) : DataType(H5T_COMPOUND, size)
{
}

//--------------------------------------------------------------------------
// Function:    CompType overloaded constructor
///\brief       Gets the compound datatype of the specified dataset.
///\param       dataset - IN: Dataset that this enum datatype associates with
///\exception   H5::DataTypeIException
//--------------------------------------------------------------------------
CompType::CompType(const DataSet &dataset) : DataType()
{
    // Calls C function H5Dget_type to get the id of the datatype
    id = H5Dget_type(dataset.getId());

    // If the datatype id is invalid, throw exception
    if (id < 0) {
        throw DataSetIException("CompType constructor", "H5Dget_type failed");
    }
}

//--------------------------------------------------------------------------
// Function:    CompType overloaded constructor
///\brief       Creates an CompType instance by opening an HDF5 compound
///             given its name, provided as a C character string.
///\param       loc        - IN: Location of the type
///\param       dtype_name - IN: Compound type name
///\exception   H5::DataTypeIException
// Description
//              In 1.10.1, this constructor was introduced and may replace the
//              existing function CommonFG::openCompType(const char*) to
//              improve usability.
//              -BMR, Dec 2016
//--------------------------------------------------------------------------
CompType::CompType(const H5Location &loc, const char *dtype_name) : DataType()
{
    id = p_opentype(loc, dtype_name);
}

//--------------------------------------------------------------------------
// Function:    CompType overloaded constructor
///\brief       Creates an CompType instance by opening an HDF5 compound
///             datatype given its name, provided as an \c H5std_string.
///\param       loc        - IN: Location of the type
///\param       dtype_name - IN: Compound type name
///\exception   H5::DataTypeIException
// Description
//              In 1.10.1, this constructor was introduced and may replace the
//              existing function CommonFG::openCompType(const H5Location&)
//              to improve usability.
//              -BMR, Dec 2016
//--------------------------------------------------------------------------
CompType::CompType(const H5Location &loc, const H5std_string &dtype_name) : DataType()
{
    id = p_opentype(loc, dtype_name.c_str());
}

//--------------------------------------------------------------------------
// Function:    CompType::decode
///\brief       Returns a CompType object via DataType* by decoding the
///             binary object description of this datatype.
///
///\exception   H5::DataTypeIException
//--------------------------------------------------------------------------
DataType *
CompType::decode() const
{
    hid_t encoded_cmptype_id = H5I_INVALID_HID;
    try {
        encoded_cmptype_id = p_decode();
    }
    catch (DataTypeIException &err) {
        throw;
    }
    CompType *encoded_cmptype = new CompType;
    encoded_cmptype->p_setId(encoded_cmptype_id);
    return (encoded_cmptype);
}

//--------------------------------------------------------------------------
// Function:    CompType::getNmembers
///\brief       Returns the number of members in this compound datatype.
///\return      Number of members
///\exception   H5::DataTypeIException
//--------------------------------------------------------------------------
int
CompType::getNmembers() const
{
    int num_members = H5Tget_nmembers(id);
    if (num_members < 0) {
        throw DataTypeIException("CompType::getNmembers",
                                 "H5Tget_nmembers returns negative number of members");
    }
    return (num_members);
}

//--------------------------------------------------------------------------
// Function:    CompType::getMemberName
///\brief       Returns the name of a member in this compound datatype.
///\param       member_num - IN: Zero-based index of the member
///\return      Name of member
///\exception   H5::DataTypeIException
//--------------------------------------------------------------------------
H5std_string
CompType::getMemberName(unsigned member_num) const
{
    char *member_name_C = H5Tget_member_name(id, member_num);
    if (member_name_C == NULL) // NULL means failure
    {
        throw DataTypeIException("CompType::getMemberName",
                                 "H5Tget_member_name returns NULL for member name");
    }
    H5std_string member_name = H5std_string(member_name_C); // convert C string to string
    H5free_memory(member_name_C);                           // free the C string
    return (member_name);                                   // return the member name string
}

//--------------------------------------------------------------------------
// Function:    CompType::getMemberIndex
///\brief       Returns the index of a member in this compound datatype.
///\param       name - IN: Name of the member
///\return      Index of member
///\exception   H5::DataTypeIException
///\par Description
///             Members are stored in no particular order with numbers 0
///             through N-1, where N is the value returned by the member
///             function \c CompType::getNmembers.
//--------------------------------------------------------------------------
int
CompType::getMemberIndex(const char *name) const
{
    int member_index = H5Tget_member_index(id, name);
    if (member_index < 0) {
        throw DataTypeIException("CompType::getMemberIndex", "H5Tget_member_index returns negative value");
    }
    return (member_index);
}
int
CompType::getMemberIndex(const H5std_string &name) const
{
    return (getMemberIndex(name.c_str()));
}

//--------------------------------------------------------------------------
// Function:    CompType::getMemberOffset
///\brief       Returns the byte offset of the beginning of a member with
///             respect to the beginning of the compound data type datum.
///\param       member_num - IN: Zero-based index of the member
///\return      Byte offset
// Description
///             Members are stored in no particular order with numbers 0
///             through N-1, where N is the value returned by the member
///             function \c CompType::getNmembers.
//
//              Note that byte offset being returned as 0 doesn't indicate
//              a failure. (According to Quincey)
//--------------------------------------------------------------------------
size_t
CompType::getMemberOffset(unsigned member_num) const
{
    size_t offset = H5Tget_member_offset(id, member_num);
    return (offset);
}

//--------------------------------------------------------------------------
// Function:    CompType::getMemberClass
///\brief       Gets the type class of the specified member.
///\param       member_num - IN: Zero-based index of the member
///\return      Type class of the member
///\exception   H5::DataTypeIException
// Modification
//              Modified to use H5Tget_member_class instead. - Jul, 2005
//--------------------------------------------------------------------------
H5T_class_t
CompType::getMemberClass(unsigned member_num) const
{
    H5T_class_t member_class = H5Tget_member_class(id, member_num);
    if (member_class == H5T_NO_CLASS) {
        throw DataTypeIException("CompType::getMemberClass", "H5Tget_member_class returns H5T_NO_CLASS");
    }
    return (member_class);
}

// This private member function calls the C API to get the identifier
// of the specified member.  It provides the id to construct appropriate
// sub-types in the functions getMemberXxxType below, where Xxx indicates
// the sub-types.
hid_t
CompType::p_get_member_type(unsigned member_num) const
{
    // get the id of the specified member first
    hid_t member_type_id = H5Tget_member_type(id, member_num);
    if (member_type_id > 0)
        return (member_type_id);
    else {
        // p_get_member_type is private, caller will catch this exception
        // then throw another with appropriate API name
        throw DataTypeIException("", "H5Tget_member_type failed");
    }
}

//--------------------------------------------------------------------------
// Function:    CompType::getMemberDataType
///\brief       Returns the generic datatype of the specified member in this
///             compound datatype.
///\param       member_num - IN: Zero-based index of the member
///\return      DataType instance
///\exception   H5::DataTypeIException
//--------------------------------------------------------------------------
DataType
CompType::getMemberDataType(unsigned member_num) const
{
    try {
        DataType datatype;
        f_DataType_setId(&datatype, p_get_member_type(member_num));
        return (datatype);
    }
    catch (DataTypeIException &E) {
        throw DataTypeIException("CompType::getMemberDataType", E.getDetailMsg());
    }
}

//--------------------------------------------------------------------------
// Function:    CompType::getMemberArrayType
///\brief       Returns the array datatype of the specified member in this
///             compound datatype.
///\param       member_num - IN: Zero-based index of the member
///\return      ArrayType instance
///\exception   H5::DataTypeIException
//--------------------------------------------------------------------------
ArrayType
CompType::getMemberArrayType(unsigned member_num) const
{
    try {
        ArrayType arraytype;
        f_DataType_setId(&arraytype, p_get_member_type(member_num));
        return (arraytype);
    }
    catch (DataTypeIException &E) {
        throw DataTypeIException("CompType::getMemberArrayType", E.getDetailMsg());
    }
}

//--------------------------------------------------------------------------
// Function:    CompType::getMemberCompType
///\brief       Returns the compound datatype of the specified member in this
///             compound datatype.
///\param       member_num - IN: Zero-based index of the member
///\return      CompType instance
///\exception   H5::DataTypeIException
//--------------------------------------------------------------------------
CompType
CompType::getMemberCompType(unsigned member_num) const
{
    try {
        CompType comptype;
        f_DataType_setId(&comptype, p_get_member_type(member_num));
        return (comptype);
    }
    catch (DataTypeIException &E) {
        throw DataTypeIException("CompType::getMemberCompType", E.getDetailMsg());
    }
}

//--------------------------------------------------------------------------
// Function:    CompType::getMemberEnumType
///\brief       Returns the enumeration datatype of the specified member in
///             this compound datatype.
///\param       member_num - IN: Zero-based index of the member
///\return      EnumType instance
///\exception   H5::DataTypeIException
//--------------------------------------------------------------------------
EnumType
CompType::getMemberEnumType(unsigned member_num) const
{
    try {
        EnumType enumtype;
        f_DataType_setId(&enumtype, p_get_member_type(member_num));
        return (enumtype);
    }
    catch (DataTypeIException &E) {
        throw DataTypeIException("CompType::getMemberEnumType", E.getDetailMsg());
    }
}

//--------------------------------------------------------------------------
// Function:    CompType::getMemberIntType
///\brief       Returns the integer datatype of the specified member in this
///             compound datatype.
///\param       member_num - IN: Zero-based index of the member
///\return      IntType instance
///\exception   H5::DataTypeIException
//--------------------------------------------------------------------------
IntType
CompType::getMemberIntType(unsigned member_num) const
{
    try {
        IntType inttype;
        f_DataType_setId(&inttype, p_get_member_type(member_num));
        return (inttype);
    }
    catch (DataTypeIException &E) {
        throw DataTypeIException("CompType::getMemberIntType", E.getDetailMsg());
    }
}

//--------------------------------------------------------------------------
// Function:    CompType::getMemberFloatType
///\brief       Returns the floating-point datatype of the specified member
///             in this compound datatype.
///\param       member_num - IN: Zero-based index of the member
///\return      FloatType instance
///\exception   H5::DataTypeIException
//--------------------------------------------------------------------------
FloatType
CompType::getMemberFloatType(unsigned member_num) const
{
    try {
        FloatType floatype;
        f_DataType_setId(&floatype, p_get_member_type(member_num));
        return (floatype);
    }
    catch (DataTypeIException &E) {
        throw DataTypeIException("CompType::getMemberFloatType", E.getDetailMsg());
    }
}

//--------------------------------------------------------------------------
// Function:    CompType::getMemberStrType
///\brief       Returns the string datatype of the specified member in this
///             compound datatype.
///\param       member_num - IN: Zero-based index of the member
///\return      StrType instance
///\exception   H5::DataTypeIException
//--------------------------------------------------------------------------
StrType
CompType::getMemberStrType(unsigned member_num) const
{
    try {
        StrType strtype;
        f_DataType_setId(&strtype, p_get_member_type(member_num));
        return (strtype);
    }
    catch (DataTypeIException &E) {
        throw DataTypeIException("CompType::getMemberStrType", E.getDetailMsg());
    }
}

//--------------------------------------------------------------------------
// Function:    CompType::getMemberVarLenType
///\brief       Returns the variable length datatype of the specified member
///             in this compound datatype.
///\param       member_num - IN: Zero-based index of the member
///\return      VarLenType instance
///\exception   H5::DataTypeIException
//--------------------------------------------------------------------------
VarLenType
CompType::getMemberVarLenType(unsigned member_num) const
{
    try {
        VarLenType varlentype;
        f_DataType_setId(&varlentype, p_get_member_type(member_num));
        return (varlentype);
    }
    catch (DataTypeIException &E) {
        throw DataTypeIException("CompType::getMemberVarLenType", E.getDetailMsg());
    }
}

/* old style of getMemberType - using overloads; new style above
   returns the appropriate datatypes but has different named functions.
   In the old style, a datatype must be passed into the function.
   May, 2004: These should be reconsidered to provide more convenience.
// Returns the datatype of the specified member in this compound datatype.
// Several overloading of getMemberType are for different datatypes
void CompType::getMemberType(unsigned member_num, EnumType& enumtype) const
{
   p_get_member_type(member_num, enumtype);
}

void CompType::getMemberType(unsigned member_num, CompType& comptype) const
{
   p_get_member_type(member_num, comptype);
}

void CompType::getMemberType(unsigned member_num, IntType& inttype) const
{
   p_get_member_type(member_num, inttype);
}

void CompType::getMemberType(unsigned member_num, FloatType& floatype) const
{
   p_get_member_type(member_num, floatype);
}

void CompType::getMemberType(unsigned member_num, StrType& strtype) const
{
   p_get_member_type(member_num, strtype);
}
// end of overloading of getMemberType
*/

//--------------------------------------------------------------------------
// Function:    CompType::insertMember
///\brief       Inserts a new member to this compound datatype.
///\param       name - IN: Name of the new member
///\param       offset - IN: Offset in memory structure of the field to insert
///\param       new_member - IN: New member to be inserted
///\exception   H5::DataTypeIException
//--------------------------------------------------------------------------
void
CompType::insertMember(const H5std_string &name, size_t offset, const DataType &new_member) const
{
    // Convert string to C-string
    const char *name_C;
    name_C = name.c_str(); // name_C refers to the contents of name as a C-str

    hid_t new_member_id = new_member.getId(); // get new_member id for C API

    // Call C routine H5Tinsert to add the new member
    herr_t ret_value = H5Tinsert(id, name_C, offset, new_member_id);
    if (ret_value < 0) {
        throw DataTypeIException("CompType::insertMember", "H5Tinsert failed");
    }
}

//--------------------------------------------------------------------------
// Function:    CompType::pack
///\brief       Recursively removes padding from within a compound datatype.
///
///\exception   H5::DataTypeIException
//--------------------------------------------------------------------------
void
CompType::pack() const
{
    // Calls C routine H5Tpack to remove padding
    herr_t ret_value = H5Tpack(id);
    if (ret_value < 0) {
        throw DataTypeIException("CompType::pack", "H5Tpack failed");
    }
}

//--------------------------------------------------------------------------
// Function:    CompType::setSize
///\brief       Sets the total size for this compound datatype.
///\param       size - IN: Size to set
///\exception   H5::DataTypeIException
// Note
//      H5Tset_size works on atom datatypes and compound datatypes only
//--------------------------------------------------------------------------
void
CompType::setSize(size_t size) const
{
    // Call C routine H5Tset_size to set the total size
    herr_t ret_value = H5Tset_size(id, size);
    if (ret_value < 0) {
        throw DataTypeIException("CompType::setSize", "H5Tset_size failed");
    }
}

} // namespace H5
