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

#include "ElementURI.h"
#include "UniversalURI.h"
#include "data/Slate.h"

const unsigned short ElementURI::NUM_CODES = 0x3FFF;
const unsigned short ElementURI::NO_CODE = 0x3FFF;
const unsigned short ElementURI::SELF_CODE = 0xFFFF;
const Unit ElementURI::UNSPECIFIED_UNIT( "unspecified", "_", NO_TYPE );
const ConfigURI ConfigURI::NO_CONFIG_URI( Str::EMPTY_STR, "", Units::NONE );


ElementURI::ElementURI( unsigned short code, const char* component, const char* elementName, ElementType elementType, const Unit& unit, BinaryDataType binaryType,
                        BlobType blobType, unsigned short dimension1,
                        unsigned short dimension2, unsigned short dimension3 )
    : Str( ElementURI::buildURI( component, elementName ) ),
      elementType_( elementType ),
      unit_( &unit ),
      binaryType_( binaryType ),
      code_( code ),
      blobType_( blobType ),
      dimension1_( dimension1 ),
      dimension2_( dimension2 ),
      dimension3_( dimension3 )
{}

ElementURI::ElementURI( const char* component, const char* elementName, ElementType elementType, const Unit& unit, BinaryDataType binaryType,
                        BlobType blobType, unsigned short dimension1,
                        unsigned short dimension2, unsigned short dimension3 )
    : Str( ElementURI::buildURI( component, elementName ) ),
      elementType_( elementType ),
      unit_( &unit ),
      binaryType_( binaryType ),
      code_( NO_CODE ),
      blobType_( blobType ),
      dimension1_( dimension1 ),
      dimension2_( dimension2 ),
      dimension3_( dimension3 )
{}

ElementURI::ElementURI( const Str& component, const char* elementName, ElementType elementType, const Unit& unit, BinaryDataType binaryType,
                        BlobType blobType, unsigned short dimension1,
                        unsigned short dimension2, unsigned short dimension3 )
    : Str( ElementURI::buildURI( component, elementName ) ),
      elementType_( elementType ),
      unit_( &unit ),
      binaryType_( binaryType ),
      code_( NO_CODE ),
      blobType_( blobType ),
      dimension1_( dimension1 ),
      dimension2_( dimension2 ),
      dimension3_( dimension3 )
{}

ElementURI::ElementURI( const Str& component, const char* elementName, const char* description, ElementType elementType, const Unit& unit, BinaryDataType binaryType,
                        BlobType blobType, unsigned short dimension1,
                        unsigned short dimension2, unsigned short dimension3 )
    : Str( ElementURI::buildURI( component, elementName ) ),
      elementType_( elementType ),
      unit_( &unit ),
      binaryType_( binaryType ),
      code_( NO_CODE ),
      blobType_( blobType ),
      dimension1_( dimension1 ),
      dimension2_( dimension2 ),
      dimension3_( dimension3 )
{}

ElementURI::ElementURI( const Str &component, const Str &elementName, ElementType elementType, const Unit& unit, BinaryDataType binaryType,
                        BlobType blobType, unsigned short dimension1,
                        unsigned short dimension2, unsigned short dimension3 )
    : Str( ElementURI::buildURI( component, elementName ) ),
      elementType_( elementType ),
      unit_( &unit ),
      binaryType_( binaryType ),
      code_( NO_CODE ),
      blobType_( blobType ),
      dimension1_( dimension1 ),
      dimension2_( dimension2 ),
      dimension3_( dimension3 )
{}

ElementURI::ElementURI( const ElementURI& src )
    : Str( src ),
      elementType_( src.elementType_ ),
      unit_( src.unit_ ),
      binaryType_( src.binaryType_ ),
      code_( src.code_ ),
      blobType_( src.blobType_ ),
      dimension1_( src.dimension1_ ),
      dimension2_( src.dimension2_ ),
      dimension3_( src.dimension3_ )
{}

ElementURI& ElementURI::operator =( const ElementURI& src )
{
    Str::operator = ( src );
    elementType_ = src.elementType_;
    unit_ = src.unit_;
    binaryType_ = src.binaryType_;
    code_  = src.code_;
    blobType_ = src.blobType_;
    dimension1_ = src.dimension1_;
    dimension2_ = src.dimension2_;
    dimension3_ = src.dimension3_;
    return *this;
}

ElementURI ElementURI::Create( const Str &component, const Str &elementName )
{
    return ElementURI( component, elementName );
}

ElementURI* ElementURI::FindURI( const Str& str )
{
    return Slate::FindElementURI( str );
}

ElementURI* ElementURI::FindURI( const char* str )
{
    return Slate::FindElementURI( Str( str ) );
}

unsigned int ElementURI::write( OutStream& outStream )
{
    unsigned int bytesWritten( 0 );

    bytesWritten += outStream.write( " code=" ).bytesWritten();
    bytesWritten += outStream.writeShortHex( code_ ).bytesWritten();

    bytesWritten += outStream.write( " elementURI=\"" ).bytesWritten();
    bytesWritten += outStream.write( cStr() ).bytesWritten();
    bytesWritten += outStream.writeChar( '\"' ).bytesWritten();

    int type = elementType_;
    if( blobType_ != NOT_BLOB )
    {
        type |= BLOB_VALUE_FLAG;

    }
    bytesWritten += outStream.write( " type=" ).bytesWritten();
    bytesWritten += outStream.writeCharHex( type ).bytesWritten();

    if( blobType_ != NOT_BLOB )
    {
        bytesWritten += outStream.write( " blobType=" ).bytesWritten();
        bytesWritten += outStream.writeUCharHex( ( unsigned char )blobType_ ).bytesWritten();
        bytesWritten += outStream.write( " fixedSize=" ).bytesWritten();
        bytesWritten += outStream.writeUShortHex( dimension1_ ).bytesWritten();
        if( dimension2_ > 0 )
        {
            bytesWritten += outStream.write( "," ).bytesWritten();
            bytesWritten += outStream.writeUShortHex( dimension2_ ).bytesWritten();
        }
        if( dimension3_ > 0 )
        {
            bytesWritten += outStream.write( "," ).bytesWritten();
            bytesWritten += outStream.writeUShortHex( dimension3_ ).bytesWritten();
        }
    }

    bytesWritten += outStream.write( "\n" ).bytesWritten();

    return bytesWritten;

}

ElementURI* ElementURI::Read( InStream& inStream )
{

    short code;
    char elementName[256];
    ElementType elementType = BASE_ELEMENT;
    BlobType blobType( NOT_BLOB );
    unsigned short dimension1 = 0, dimension2 = 0, dimension3 = 0;
    bool ok = true;
    char dummy[16];

    // read " code="
    ok = inStream.read( dummy, 6 ).bytesRead() == 6 && strncmp( dummy, " code=", 6 ) == 0;
    if( ok )
    {
        // read code
        ok = inStream.readShortHex( code ).bytesRead() == 4;
    }

    if( ok )
    {
        // read " elementURI=\""
        ok = inStream.read( dummy, 13 ).bytesRead() == 13 && strncmp( dummy, " elementURI=\"", 13 ) == 0;
    }
    if( ok )
    {
        // read elementName
        ok = inStream.readUntil( elementName, 255, '\"' ).bytesRead() > 0;
        ok &= inStream.bytesRead() < 255;
        if( ok )  // remove the trailing quote
        {
            elementName[ inStream.bytesRead() - 1 ] = 0;
        }
    }

    if( ok )
    {
        // read "\n" (OK if '\n' missing)
        *dummy = 0;
        inStream.read( dummy, 1 );
    }

    bool skipLF = false;
    if( ok && *dummy == ' ' )
    {
        // read "type="
        bool isBlobType = false;
        ok = inStream.read( dummy, 5 ).bytesRead() == 5 && strncmp( dummy, "type=", 5 ) == 0;
        if( ok )
        {
            // read type
            signed char elementTypeChar = ( signed char )elementType;
            ok = inStream.readCharHex( elementTypeChar ).bytesRead() == 2;
            isBlobType = ( elementTypeChar & BLOB_VALUE_FLAG ) == BLOB_VALUE_FLAG;
            elementType = ( ElementType )( elementTypeChar & ! BLOB_VALUE_FLAG );
        }

        if( ok && isBlobType )
        {
            // read " blobType="
            ok = inStream.read( dummy, 10 ).bytesRead() == 10 && strncmp( dummy, " blobType=", 10 ) == 0;;
            unsigned char blobTypeCode = 0;
            if( ok )
            {
                // read blobTypeCode
                ok = inStream.readUCharHex( blobTypeCode ).bytesRead() == 2;
            }
            blobType = ( BlobType )blobTypeCode;

            // read " fixedSize="
            ok = inStream.read( dummy, 11 ).bytesRead() == 11 && strncmp( dummy, " fixedSize=", 11 ) == 0;;
            if( ok )
            {
                // read fixedSize(s)
                ok = inStream.readUShortHex( dimension1 ).bytesRead() == 4;
                if( ok )
                {
                    skipLF = true;
                    if( inStream.read( dummy, 1 ).bytesRead() == 1 && *dummy == ',' )
                    {
                        ok = inStream.readUShortHex( dimension2 ).bytesRead() == 4;
                        if( ok )
                        {
                            skipLF = true;
                            if( inStream.read( dummy, 1 ).bytesRead() == 1 && *dummy == ',' )
                            {
                                ok = inStream.readUShortHex( dimension3 ).bytesRead() == 4;
                                skipLF = false;
                            }
                        }
                    }
                }
            }
        }

        if( ok && !skipLF )
        {
            // read "\n" (OK if final '\n' missing)
            *dummy = 0;
            inStream.read( dummy, 1 );
        }

    }

    if( ok )
    {
        ElementURI* elementURI;
        if( strchr( elementName, '.' ) == NULL )
        {
            if( blobType == NOT_BLOB )
            {
                elementURI = new UniversalURI( code, elementName );
            }
            else
            {
                elementURI = new UniversalBlobURI( code, elementName,
                                                   UNSPECIFIED_UNIT, blobType, dimension1, dimension2, dimension3 );
            }
        }
        else
        {
            elementURI = new ElementURI( code, "", elementName, elementType,
                                         UNSPECIFIED_UNIT, NO_TYPE, blobType, dimension1, dimension2, dimension3 );
        }
        return elementURI;
    }

    return NULL;
}

const Str ElementURI::buildURI( const char* component, const char* elementName )
{
    if( NULL != component && strlen( component ) > 0 )
    {
        if( elementName[0] != '.' )
        {
            return Str( component ) + "." + elementName;
        }
        return Str( component ) + elementName;
    }
    return Str( elementName );
}

const Str ElementURI::buildURI( const Str &component, const char* elementName )
{
    if( component.length() > 0 )
    {
        if( elementName[0] != '.' )
        {
            return component + "." + elementName;
        }
        return component + elementName;
    }
    return elementName;
}

const Str ElementURI::buildURI( const Str &component, const Str &elementName )
{
    if( component.length() > 0 )
    {
        if( elementName[0] != '.' )
        {
            return component + "." + elementName;
        }
        return component + elementName;
    }
    return elementName;
}

