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

#ifndef ELEMENTURI_H_
#define ELEMENTURI_H_

#include "data/BinaryDataType.h"
#include "data/BlobType.h"
#include "io/InStream.h"
#include "io/OutStream.h"
#include "units/Units.h"
#include "utils/Str.h"

/**
 *  Code unit that represents a unique name for a DataElement.
 *  Typically combines the name of the element owner with
 *  the name of the "quantity" represented by the DataElement.
 *
 * \ingroup data
 */
class ElementURI : public Str
{
public:

    enum ElementType
    {
        BASE_ELEMENT = 0,
        CONFIG_ELEMENT = 1,
        DATA_ELEMENT = 2,
        SETTING_ELEMENT = 3,
        UNIVERSAL_ELEMENT = 4,
        BLOB_VALUE_FLAG = 0x10
    };

    static const unsigned short NUM_CODES;
    static const unsigned short NO_CODE;
    static const unsigned short SELF_CODE;
    static const Unit UNSPECIFIED_UNIT;

    ElementURI( const char* component, const char* elementName, ElementType = BASE_ELEMENT, const Unit& unit = UNSPECIFIED_UNIT, BinaryDataType binaryType = NO_TYPE,
                BlobType blobType = NOT_BLOB, unsigned short dimension1 = 0,
                unsigned short dimension2 = 0, unsigned short dimension3 = 0 );

    ElementURI( const Str& component, const char* elementName, ElementType = BASE_ELEMENT, const Unit& unit = UNSPECIFIED_UNIT, BinaryDataType binaryType = NO_TYPE,
                BlobType blobType = NOT_BLOB, unsigned short dimension1 = 0,
                unsigned short dimension2 = 0, unsigned short dimension3 = 0 );

    ElementURI( const Str& component, const char* elementName, const char* description, ElementType = BASE_ELEMENT, const Unit& unit = UNSPECIFIED_UNIT, BinaryDataType binaryType = NO_TYPE,
                BlobType blobType = NOT_BLOB, unsigned short dimension1 = 0,
                unsigned short dimension2 = 0, unsigned short dimension3 = 0 );

    ElementURI( const Str &component, const Str &elementName, ElementType = BASE_ELEMENT, const Unit& unit = UNSPECIFIED_UNIT, BinaryDataType binaryType = NO_TYPE,
                BlobType blobType = NOT_BLOB, unsigned short dimension1 = 0,
                unsigned short dimension2 = 0, unsigned short dimension3 = 0 );

    ElementURI( const ElementURI& src );

    ElementURI& operator =( const ElementURI& src );

    /// Destructor
    virtual ~ElementURI()
    {}

    // Cast
    operator const Str& ( void ) const
    {
        return *this;
    }

    unsigned short getCode() const
    {
        return code_;
    }

    void setCode( unsigned short code )
    {
        code_ = code;
    }

    virtual bool isUniversal() const
    {
        return false;
    }

    const Unit& getUnit() const
    {
        return *unit_;
    }

    BinaryDataType getBinaryType() const
    {
        return binaryType_ == NO_TYPE ? unit_->getDefaultDataType() : binaryType_;
    }

    ElementType getElementType() const
    {
        return elementType_;
    }

    static ElementURI* FindURI( const Str& str );

    static ElementURI* FindURI( const char* str );

    unsigned int write( OutStream& outStream );

    static ElementURI* Read( InStream& inStream );

    static ElementURI Create( const Str &component, const Str &elementName );

    const Str buildURI( const char* component, const char* elementName );

    const Str buildURI( const Str &component, const char* elementName );

    const Str buildURI( const Str &component, const Str &elementName );

    BlobType getBlobType() const
    {
        return blobType_;
    }

    int getDimensions() const
    {
        return dimension3_ > 0 ? 3 : ( dimension2_ > 0 ? 2 : ( dimension1_ > 0 ? 1 :
                                       ( blobType_ == NOT_BLOB ? 0 : 1 ) ) );
    }

    const unsigned short getDimension1() const
    {
        return dimension1_;
    }

    const unsigned short getDimension2() const
    {
        return dimension2_;
    }

    const unsigned short getDimension3() const
    {
        return dimension3_;
    }

protected:

    friend class Slate;

    ElementType elementType_;
    const Unit* unit_;
    BinaryDataType binaryType_;
    unsigned short code_;
    BlobType blobType_;
    unsigned short dimension1_;
    unsigned short dimension2_;
    unsigned short dimension3_;

    ElementURI( unsigned short code, const char* component, const char* elementName, ElementType = BASE_ELEMENT,
                const Unit& unit = UNSPECIFIED_UNIT, BinaryDataType binaryType = NO_TYPE,
                BlobType blobType = NOT_BLOB, unsigned short dimension1 = 0,
                unsigned short dimension2 = 0, unsigned short dimension3 = 0 );

};

class ConfigURI : public ElementURI
{
protected:
    bool requiresRestart_;

public:

    ConfigURI( const Str& component, const char* elementName, const Unit& unit, BinaryDataType binaryType = NO_TYPE )
        : ElementURI( component, elementName, CONFIG_ELEMENT, unit, binaryType ),
          requiresRestart_( false )
    {}

    ConfigURI( const Str& component, const char* elementName, const char* description, const Unit& unit, BinaryDataType binaryType = NO_TYPE )
        : ElementURI( component, elementName, description, CONFIG_ELEMENT, unit, binaryType ),
          requiresRestart_( false )
    {}

    bool requiresRestart() const
    {
        return requiresRestart_;
    }

    static const ConfigURI NO_CONFIG_URI;

};

class RestartConfigURI : public ConfigURI
{

public:

    RestartConfigURI( const Str& component, const char* elementName, const Unit& unit, BinaryDataType binaryType = NO_TYPE )
        : ConfigURI( component, elementName, unit, binaryType )
    {
        requiresRestart_ = true;
    }

    RestartConfigURI( const Str& component, const char* elementName, const char* description, const Unit& unit, BinaryDataType binaryType = NO_TYPE )
        : ConfigURI( component, elementName, description, unit, binaryType )
    {
        requiresRestart_ = true;
    }

};

class BlobURI : public ElementURI
{
public:

    BlobURI( const Str& component, const char* elementName, const Unit& unit,
             BlobType blobType, unsigned short dimension1, unsigned short dimension2 = 0,
             unsigned short dimension3 = 0 )
        : ElementURI( component, elementName, DATA_ELEMENT, unit, MULTIVALUE, blobType, dimension1, dimension2, dimension3 )
    {}

    BlobURI( const Str& component, const char* elementName, const char* description, const Unit& unit,
             BlobType blobType, unsigned short dimension1, unsigned short dimension2 = 0,
             unsigned short dimension3 = 0 )
        : ElementURI( component, elementName, description, DATA_ELEMENT, unit, MULTIVALUE, blobType, dimension1, dimension2, dimension3 )
    {}

};

class DataURI : public ElementURI
{
public:

    DataURI( const Str& component, const char* elementName, const Unit& unit, BinaryDataType binaryType = NO_TYPE )
        : ElementURI( component, elementName, DATA_ELEMENT, unit, binaryType )
    {}

    DataURI( const Str& component, const char* elementName, const char* description, const Unit& unit, BinaryDataType binaryType = NO_TYPE )
        : ElementURI( component, elementName, description, DATA_ELEMENT, unit, binaryType )
    {}
};

class ScratchpadURI : public DataURI
{
public:

    ScratchpadURI( const char* elementName, const Unit& unit )
        : DataURI( "_", elementName, unit, NO_TYPE )
    {}
};

class NameURI : public Str
{
public:

    NameURI( const char* elementName )
        : Str( elementName )
    {}

    NameURI( const char* elementName, const char* description )
        : Str( elementName )
    {}
};

class OutputURI : public ElementURI
{
public:

    OutputURI( const char* elementName, const Unit& unit, BinaryDataType binaryType = NO_TYPE )
        : ElementURI( Str::EMPTY_STR, elementName, SETTING_ELEMENT, unit, binaryType )
    {}

    OutputURI( const char* elementName, const char* description, const Unit& unit, BinaryDataType binaryType = NO_TYPE )
        : ElementURI( Str::EMPTY_STR, elementName, description, SETTING_ELEMENT, unit, binaryType )
    {}
};

class SettingURI : public ElementURI
{
public:

    SettingURI( const char* elementName, const Unit& unit, BinaryDataType binaryType = NO_TYPE )
        : ElementURI( Str::EMPTY_STR, elementName, SETTING_ELEMENT, unit, binaryType )
    {}

    SettingURI( const char* elementName, const char* description, const Unit& unit, BinaryDataType binaryType = NO_TYPE )
        : ElementURI( Str::EMPTY_STR, elementName, description, SETTING_ELEMENT, unit, binaryType )
    {}

    SettingURI( const SettingURI& settingURI )
        : ElementURI( settingURI )
    {}
};

class StringSettingURI : public SettingURI
{
public:

    StringSettingURI( const char* elementName )
        : SettingURI( elementName, Units::NONE, MULTIVALUE )
    {}

    StringSettingURI( const char* elementName, const char* description )
        : SettingURI( elementName, description, Units::NONE, MULTIVALUE )
    {}
};


#endif /*ELEMENTURI_H_*/
