/** \file
 *
 *  Contains the HDF5 class declaration.
 *
 *  Copyright (c) 2007,2008,2009 MBARI
 *  MBARI Proprietary Information.  All Rights Reserved
 */

#ifndef HDF5_H_
#define HDF5_H_

#include "data/BlobValue.h"
#include "data/ElementURI.h"
#include "utils/FastMap.h"
#include "utils/FlexArray.h"
#include "units/Units.h"
#include "utils/Str.h"

class DataValue;
class Unit;

class HDF5Group;
class HDF5Dataset;

class HDF5Item
{

public:
    virtual ~HDF5Item() {}

    int getId()
    {
        return id_;
    }

    virtual bool writeHeader() = 0;

    virtual bool finalize();

protected:

    HDF5Item()
        : id_( -1 ),
          typeId_( -1 ),
          spaceId_( -1 )
    {}

    int id_;
    int typeId_;
    int spaceId_;

private:
    // Note that the copy constructor below is private and not given a body.
    // Any attempt to call it will return a compiler error.
    HDF5Item( const HDF5Item& old ); // disallow copy constructor

};


class HDF5Attribute: public HDF5Item
{
public:
    HDF5Attribute( HDF5Item* parent, const char* name, const char* value );

    HDF5Attribute( HDF5Item* parent, const char* name, const int value );

    HDF5Attribute( HDF5Item* parent, const char* name, FlexArray<Str*>& fields );

    virtual ~HDF5Attribute();

    virtual bool writeHeader();

    virtual bool finalize();

protected:

    void* value_;
    bool vlArrayDelete_;

private:
    // Note that the copy constructor below is private and not given a body.
    // Any attempt to call it will return a compiler error.
    HDF5Attribute( const HDF5Attribute& old ); // disallow copy constructor

};

class HDF5HasAttributes: public HDF5Item
{

public:
    HDF5HasAttributes( HDF5Group* parent )
        : HDF5Item(),
          parent_( parent ),
          attributes_( true )
    {}

    virtual ~HDF5HasAttributes() {}

    int getId()
    {
        return id_;
    }

    virtual HDF5Group* getParent()
    {
        return parent_;
    }

    virtual bool isMatlab()
    {
        return NULL == parent_ ? false : ( ( HDF5HasAttributes* )parent_ )->isMatlab();
    }

    virtual HDF5Group* getRoot()
    {
        return NULL == parent_ ? NULL : ( ( HDF5HasAttributes* )parent_ )->getRoot();
    }

    void addAttribute( const char* name, const char* value );

    void addAttribute( const char* name, const int value );

    virtual bool writeHeader();

    virtual bool finalize();

protected:

    HDF5Group* parent_;
    FlexArray<HDF5Attribute*> attributes_;

private:
    // Note that the copy constructor below is private and not given a body.
    // Any attempt to call it will return a compiler error.
    HDF5HasAttributes( const HDF5HasAttributes& old ); // disallow copy constructor

};

class HDF5Group: public HDF5HasAttributes
{
public:
    HDF5Group( HDF5Group* parent, const char* name, bool useMetadata );

    virtual ~HDF5Group();

    HDF5Group* findGroup( const char* name );

    HDF5Group* addGroup( const char* name, bool useMetadata = true );

    HDF5Dataset* findDataset( const char* name );

    HDF5Dataset* addScalarDataset( const char* name, const char* value, size_t length, const Unit& unit );

    HDF5Dataset* addDataset( const char* name, BinaryDataType binaryType,
                             const Unit& unit, bool useChunks,
                             BlobType blobType = NOT_BLOB,
                             int rank = 1, unsigned short m = 0, unsigned short n = 0, unsigned short o = 0 );

    virtual bool writeHeader();

    virtual bool finalize();

    unsigned int getDatasetCount()
    {
        return datasetMap_.size();
    }

protected:

    FastMap<const Str, HDF5Group*> groupMap_;
    FastMap<const Str, HDF5Dataset*> datasetMap_;
    FlexArray<Str*> fields_;
    bool useMetadata_;

private:
    // Note that the copy constructor below is private and not given a body.
    // Any attempt to call it will return a compiler error.
    HDF5Group( const HDF5Group& old ); // disallow copy constructor

};

class HDF5File: public HDF5Group
{
public:
    HDF5File( const char* fileName, bool matlab );
    virtual ~HDF5File() {}

    virtual bool isMatlab()
    {
        return matlab_;
    }

    virtual HDF5Group* getRoot()
    {
        return this;
    }

    virtual bool finalize();

protected:
    bool matlab_;
    Str filename_;
    Str tempFilename_;

private:
    // Note that the copy constructor below is private and not given a body.
    // Any attempt to call it will return a compiler error.
    HDF5File( const HDF5File& old ); // disallow copy constructor

};

class HDF5Dataset: public HDF5HasAttributes
{
public:
    HDF5Dataset( HDF5Group* parent, const char* name, BinaryDataType binaryType,
                 const Unit& unit, bool useChunks,
                 BlobType blobType = NOT_BLOB,
                 int rank = 1, unsigned short m = 0, unsigned short n = 0, unsigned short o = 0 );

    HDF5Dataset( HDF5Group* parent, const char* name, const char* value, size_t length, const Unit& unit );

    virtual ~HDF5Dataset();

    DataValue* getDataValue();

    bool writeDataValue();

    bool finalize();

    static int TypeIdFromBlobType( BlobType blobType );

    static const char* MatlabClassFromBlobType( BlobType blobType );

protected:
    DataValue* dataValue_;
    BlobType blobType_;
    int rank_;
    unsigned short m_, n_, o_;
    int propsId_;
    size_t numRecs_;

    static const int MAX_RANK = 4;

private:
    // Note that the copy constructor below is private and not given a body.
    // Any attempt to call it will return a compiler error.
    HDF5Dataset( const HDF5Dataset& old ); // disallow copy constructor

};

#endif /*HDF5_H_*/
