/*
 * Licensed to the Apache Software Foundation (ASF) under one or more
 * contributor license agreements.  See the NOTICE file distributed with
 * this work for additional information regarding copyright ownership.
 * The ASF licenses this file to You under the Apache License, Version 2.0
 * (the "License"); you may not use this file except in compliance with
 * the License.  You may obtain a copy of the License at
 * 
 *      http://www.apache.org/licenses/LICENSE-2.0
 * 
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

/*
 * $Id: XSerializeEngine.hpp 568078 2007-08-21 11:43:25Z amassari $
 */

#if !defined(XSERIALIZE_ENGINE_HPP)
#define XSERIALIZE_ENGINE_HPP

#include <xercesc/util/RefHashTableOf.hpp>
#include <xercesc/util/ValueVectorOf.hpp>
#include <xercesc/util/XMLExceptMsgs.hpp>

#include <xercesc/internal/XSerializationException.hpp>

XERCES_CPP_NAMESPACE_BEGIN

class XSerializable;
class XProtoType;
class MemoryManager;
class XSerializedObjectId;
class BinOutputStream;
class BinInputStream;
class XMLGrammarPool;
class XMLGrammarPoolImpl;
class XMLStringPool;

class XMLUTIL_EXPORT XSerializeEngine
{
public:

    enum { mode_Store
         , mode_Load 
    };
    

    static const bool toReadBufferLen;

    typedef unsigned int   XSerializedObjectId_t;

    /***
      *
      *  Destructor 
      *
      ***/
    ~XSerializeEngine();

    /***
      *
      *  Constructor for de-serialization(loading)
      *
      *  Application needs to make sure that the instance of
      *  BinInputStream, persists beyond the life of this
      *  SerializeEngine.
      *
      *  Param
      *     inStream         input stream
      *     gramPool         Grammar Pool
      *     bufSize          the size of the internal buffer
      *
      ***/
    XSerializeEngine(BinInputStream*         inStream
                   , XMLGrammarPool* const   gramPool
                   , unsigned long           bufSize = 8192 );


    /***
      *
      *  Constructor for serialization(storing)
      *
      *  Application needs to make sure that the instance of
      *  BinOutputStream, persists beyond the life of this
      *  SerializeEngine.
      *
      *  Param
      *     outStream        output stream
      *     gramPool         Grammar Pool
      *     bufSize          the size of the internal buffer
      *
      ***/
    XSerializeEngine(BinOutputStream*        outStream
                   , XMLGrammarPool* const   gramPool
                   , unsigned long           bufSize = 8192 );


    /***
      *
      *  Deprecated
      *
      *  Constructor for de-serialization(loading)
      *
      *  Application needs to make sure that the instance of
      *  BinInputStream, persists beyond the life of this
      *  SerializeEngine.
      *
      *  Param
      *     inStream         input stream
      *     manager          MemoryManager
      *     bufSize          the size of the internal buffer
      *
      ***/
    XSerializeEngine(BinInputStream*         inStream
                   , MemoryManager* const    manager = XMLPlatformUtils::fgMemoryManager
                   , unsigned long           bufSize = 8192 );

    
    /***
      *
      *  Deprecated
      *
      *  Constructor for serialization(storing)
      *
      *  Application needs to make sure that the instance of
      *  BinOutputStream, persists beyond the life of this
      *  SerializeEngine.
      *
      *  Param
      *     outStream        output stream
      *     manager          MemoryManager
      *     bufSize          the size of the internal buffer
      *
      ***/
    XSerializeEngine(BinOutputStream*        outStream
                   , MemoryManager* const    manager = XMLPlatformUtils::fgMemoryManager
                   , unsigned long           bufSize = 8192 );

    /***
      *
      *  When serialization, flush out the internal buffer
      *
      *  Return: 
      *
      ***/
    void flush();

    /***
      *
      *  Checking if the serialize engine is doing serialization(storing)
      *
      *  Return: true, if it is 
      *          false, otherwise
      *
      ***/
    inline bool isStoring() const;

    /***
      *
      *  Checking if the serialize engine is doing de-serialization(loading)
      *
      *  Return: true, if it is 
      *          false, otherwise
      *
      ***/
    inline bool isLoading() const;

    /***
      *
      *  Get the GrammarPool
      *
      *  Return: XMLGrammarPool
      *
      ***/
    XMLGrammarPool* getGrammarPool() const;

    /***
      *
      *  Get the StringPool
      *
      *  Return: XMLStringPool
      *
      ***/
    XMLStringPool* getStringPool() const;

    /***
      *
      *  Get the embeded Memory Manager
      *
      *  Return: MemoryManager
      *
      ***/
    MemoryManager* getMemoryManager() const;

    /***
      *
      *  Get the storer level (the level of the serialize engine
      *  which created the binary stream that this serialize engine
      *  is loading).
      *
      *  The level returned is meaningful only when
      *  the engine isLoading.
      *
      *  Return: level
      *
      ***/
    inline unsigned short getStorerLevel() const;

    /***
      *
      *  Write object to the internal buffer.
      *
      *  Param
      *     objectToWrite:    the object to be serialized
      *
      *  Return:
      *
      ***/
           void           write(XSerializable* const objectToWrite);

    /***
      *
      *  Write prototype info to the internal buffer.
      *
      *  Param
      *     protoType:    instance of prototype
      *
      *  Return:
      *
      ***/
           void           write(XProtoType* const protoType);

    /***
      *
      *  Write a stream of XMLByte to the internal buffer.
      *
      *  Param
      *     toWrite:   the stream of XMLByte to write
      *     writeLen:  the length of the stream
      *
      *  Return:
      *
      ***/
           void           write(const XMLByte* const toWrite
                               ,      int            writeLen);

    /***
      *
      *  Write a stream of XMLCh to the internal buffer.
      *
      *  Param
      *     toWrite:   the stream of XMLCh to write
      *     writeLen:  the length of the stream
      *
      *  Return:
      *
      ***/
           void           write(const XMLCh* const toWrite
                               ,      int          writeLen);

    /***
      *
      *  Write a stream of XMLCh to the internal buffer.
      *
      *  Write the bufferLen first if requested, then the length
      *  of the stream followed by the stream.
      *
      *  Param
      *     toWrite:        the stream of XMLCh to write
      *     bufferLen:      the maximum size of the buffer
      *     toWriteBufLen:  specify if the bufferLen need to be written or not
      *
      *  Return:
      *
      ***/
           void           writeString(const XMLCh* const toWrite
                                    , const int          bufferLen = 0
                                    , bool               toWriteBufLen = false);

    /***
      *
      *  Write a stream of XMLByte to the internal buffer.
      *
      *  Write the bufferLen first if requested, then the length
      *  of the stream followed by the stream.
      *
      *  Param
      *     toWrite:        the stream of XMLByte to write
      *     bufferLen:      the maximum size of the buffer
      *     toWriteBufLen:  specify if the bufferLen need to be written or not
      *
      *  Return:
      *
      ***/
           void           writeString(const XMLByte* const toWrite
                                    , const int            bufferLen = 0
                                    , bool                 toWriteBufLen = false);

    static const bool toWriteBufferLen;

    /***
      *
      *  Read/Create object from the internal buffer.
      *
      *  Param
      *     protoType:    an instance of prototype of the object anticipated
      *
      *  Return:          to object read/created
      *
      ***/
	       XSerializable* read(XProtoType* const protoType);

    /***
      *
      *  Read prototype object from the internal buffer.
      *  Verify if the same prototype object found in buffer.
      *
      *  Param
      *     protoType:    an instance of prototype of the object anticipated
      *     objTag:       the object Tag to an existing object
      *
      *  Return:          true  : if matching found
      *                   false : otherwise
      *
      ***/
           bool           read(XProtoType* const    protoType
		                     , XSerializedObjectId_t*       objTag);

    /***
      *
      *  Read XMLByte stream from the internal buffer.
      *
      *  Param
      *     toRead:   the buffer to hold the XMLByte stream
      *     readLen:  the length of the XMLByte to read in
      *
      *  Return:
      *
      ***/
           void           read(XMLByte* const toRead
                             , int            readLen);

    /***
      *
      *  Read XMLCh stream from the internal buffer.
      *
      *  Param
      *     toRead:   the buffer to hold the XMLCh stream
      *     readLen:  the length of the XMLCh to read in
      *
      *  Return:
      *
      ***/
           void           read(XMLCh* const toRead
                             , int          readLen);

    /***
      *
      *  Read a stream of XMLCh from the internal buffer.
      *
      *  Read the bufferLen first if requested, then the length
      *  of the stream followed by the stream.
      *
      *  Param
      *     toRead:       the pointer to the buffer to hold the XMLCh stream
      *     bufferLen:    the size of the buffer created
      *     dataLen:       the length of the stream
      *     toReadBufLen: specify if the bufferLen need to be read or not
      *
      *  Return:
      *
      ***/
           void           readString(XMLCh*&        toRead
                                   , int&           bufferLen
                                   , int&           dataLen
                                   , bool           toReadBufLen = false);

     /***
       *
       *  Read a stream of XMLCh from the internal buffer.
       *
       *  Read the bufferLen first if requested, then the length
       *  of the stream followed by the stream.
       *
       *  Param
       *     toRead:       the pointer to the buffer to hold the XMLCh stream
       *     bufferLen:    the size of the buffer created
       *
       *  Return:
       *
       ***/
            inline void     readString(XMLCh*&        toRead
                                    , int&            bufferLen);
 
     /***
       *
       *  Read a stream of XMLCh from the internal buffer.
       *
       *  Param
       *     toRead:       the pointer to the buffer to hold the XMLCh stream
       *
       *  Return:
       *
       ***/
            inline void      readString(XMLCh*&        toRead);

    /***
      *
      *  Read a stream of XMLByte from the internal buffer.
      *
      *  Read the bufferLen first if requested, then the length
      *  of the stream followed by the stream.
      *
      *  Param
      *     toRead:       the pointer to the buffer to hold the XMLByte stream
      *     bufferLen:    the size of the buffer created
      *     dataLen:       the length of the stream
      *     toReadBufLen: specify if the bufferLen need to be read or not
      *
      *  Return:
      *
      ***/
           void           readString(XMLByte*&      toRead
                                   , int&           bufferLen
                                   , int&           dataLen
                                   , bool           toReadBufLen = false);


     /***
       *
       *  Read a stream of XMLByte from the internal buffer.
       *
       *  Read the bufferLen first if requested, then the length
       *  of the stream followed by the stream.
       *
       *  Param
       *     toRead:       the pointer to the buffer to hold the XMLByte stream
       *     bufferLen:    the size of the buffer created
       *
       *  Return:
       *
       ***/
            inline void       readString(XMLByte*&      toRead
                                       , int&           bufferLen);
 
     /***
       *
       *  Read a stream of XMLByte from the internal buffer.
       *
       *  Read the bufferLen first if requested, then the length
       *  of the stream followed by the stream.
       *
       *  Param
       *     toRead:       the pointer to the buffer to hold the XMLByte stream
       *     bufferLen:    the size of the buffer created
       *     dataLen:       the length of the stream
       *     toReadBufLen: specify if the bufferLen need to be read or not
       *
       *  Return:
       *
       ***/
            inline void       readString(XMLByte*&      toRead);

    /***
      *
      *  Check if the template object has been stored or not
      *
      *  Param
      *    objectPtr:     the template object pointer
      *
      *  Return:          true  : the object has NOT been stored yet
      *                   false : otherwise
      *
      ***/
           bool           needToStoreObject(void* const templateObjectToWrite);

    /***
      *
      *  Check if the template object has been loaded or not
      *
      *  Param
      *    objectPtr:     the address of the template object pointer
      *
      *  Return:          true  : the object has NOT been loaded yet
      *                   false : otherwise
      *
      ***/
           bool           needToLoadObject(void**       templateObjectToRead);

    /***
      *
      *  In the case of needToLoadObject() return true, the client
      *  application needs to instantiate an expected template object, and
      *  register the address to the engine.
      *
      *  Param
      *    objectPtr:     the template object pointer newly instantiated
      *
      *  Return:  
      *
      ***/
           void           registerObject(void* const templateObjectToRegister);

    /***
      *
      *  Insertion operator for serializable classes
      *
      ***/

	friend XSerializeEngine& operator<<(XSerializeEngine&
                                      , XSerializable* const );

    /***
      *
      *  Insertion operators for 
      *     . basic Xerces data types
      *     . built-in types 
      *
      ***/
           XSerializeEngine& operator<<(XMLByte);
           XSerializeEngine& operator<<(XMLCh);

           XSerializeEngine& operator<<(char);
           XSerializeEngine& operator<<(short);
           XSerializeEngine& operator<<(int);
           XSerializeEngine& operator<<(unsigned int);
           XSerializeEngine& operator<<(long);
           XSerializeEngine& operator<<(unsigned long);
           XSerializeEngine& operator<<(float);
           XSerializeEngine& operator<<(double);
           XSerializeEngine& operator<<(bool);

    /***
      *
      *  Extraction operators for 
      *     . basic Xerces data types
      *     . built-in types 
      *
      ***/
           XSerializeEngine& operator>>(XMLByte&);
           XSerializeEngine& operator>>(XMLCh&);

           XSerializeEngine& operator>>(char&);
           XSerializeEngine& operator>>(short&);
           XSerializeEngine& operator>>(int&);
           XSerializeEngine& operator>>(unsigned int&);
           XSerializeEngine& operator>>(long&);
           XSerializeEngine& operator>>(unsigned long&);
           XSerializeEngine& operator>>(float&);
           XSerializeEngine& operator>>(double&);
           XSerializeEngine& operator>>(bool&);

    /***
      *
      *  Getters
      *
      ***/
    inline 
    XMLSize_t   getBufSize()    const;

    inline 
    XMLSize_t   getBufCur()     const;

    inline 
    XMLSize_t   getBufCurAccumulated()     const;

    inline 
    XMLSize_t   getBufCount()    const;

    void                  trace(char*)     const;

private:
    // -----------------------------------------------------------------------
    //  Unimplemented constructors and operators
    // -----------------------------------------------------------------------
	XSerializeEngine();
    XSerializeEngine(const XSerializeEngine&);
	XSerializeEngine& operator=(const XSerializeEngine&);

    /***
      *
      *   Store Pool Opertions
      *
      ***/
           XSerializedObjectId_t  lookupStorePool(void* const objectPtr) const;
           void                   addStorePool(void* const objectPtr);

    /***
      *
      *   Load Pool Opertions
      *
      ***/
           XSerializable* lookupLoadPool(XSerializedObjectId_t objectTag) const;
           void           addLoadPool(void* const objectPtr);
   
    /***
      *   
      *    Intenal Buffer Operations
      *
      ***/
    inline void           checkAndFillBuffer(int bytesNeedToRead);

    inline void           checkAndFlushBuffer(int bytesNeedToWrite);

           void           fillBuffer();

           void           flushBuffer();

           void           pumpCount();

    inline void           resetBuffer();

    /***
      *   
      *    Helper
      *
      ***/
    inline void            ensureStoring()                          const;

    inline void            ensureLoading()                          const;

    inline void            ensureStoreBuffer()                      const;

    inline void            ensureLoadBuffer()                       const;

    inline void            ensurePointer(void* const)               const;

    inline void            ensureBufferLen(int  bufferLen)          const;

    inline void            Assert(bool  toEval
                                , const XMLExcepts::Codes toThrow)  const;


    inline XMLSize_t          calBytesNeeded(XMLSize_t)  const;

    inline XMLSize_t          alignAdjust(XMLSize_t)     const;

    inline void            alignBufCur(XMLSize_t);

    // Make XTemplateSerializer friend of XSerializeEngine so that
    // we can call lookupStorePool and lookupLoadPool in the case of
    // annotations.
    friend class XTemplateSerializer;

    // -------------------------------------------------------------------------------
    //  data
    //
    //  fStoreLoad: 
    //               Indicator: storing(serialization) or loading(de-serialization)
    //
    //  fStorerLevel:
    //              The level of the serialize engine which created the binary 
    //              stream that this serialize engine is loading
    //
    //              It is set by GrammarPool when loading
    //
    //  fGrammarPool:
    //               Thw owning GrammarPool which instantiate this SerializeEngine 
    //               instance
    //
    //  fInputStream:
    //               Binary stream to read from (de-serialization), provided
    //               by client application, not owned.
    //
    //  fOutputStream:
    //               Binary stream to write to (serialization), provided 
    //               by client application, not owned.
    //
    //  fBufSize:
    //               The size of the internal buffer
    //
    //  fBufStart/fBufEnd:
    //               
    //               The internal buffer.
    //  fBufEnd:
    //               one beyond the last valid cell
    //               fBufEnd === (fBufStart + fBufSize)
    //
    //  fBufCur:
    //               The cursor of the buffer
    //
    //  fBufLoadMax:
    //               Indicating the end of the valid content in the buffer
    //
    //  fStorePool:
    //                Object collection for storing
    //
    //  fLoadPool:
    //                Object collection for loading
    //
    //  fMapCount:
    // -------------------------------------------------------------------------------
    const short                            fStoreLoad;
    short                                  fStorerLevel;

    XMLGrammarPool*  const                 fGrammarPool;
    BinInputStream*  const                 fInputStream;
    BinOutputStream* const                 fOutputStream;

    XMLSize_t                              fBufCount;

    //buffer
    const XMLSize_t                        fBufSize;
	XMLByte* const                         fBufStart;
	XMLByte* const                         fBufEnd; 
    XMLByte*                               fBufCur;
    XMLByte*                               fBufLoadMax; 



    /***
     *   Map for storing object
     *
     *   key:   XSerializable*
     *          XProtoType*
     *
     *   value: XMLInteger*, owned
     *
     ***/
    RefHashTableOf<XSerializedObjectId>*   fStorePool;  

    /***
     *   Vector for loading object, objects are NOT owned
     *
     *   data:   XSerializable*
     *           XProtoType*
     *
     ***/
    ValueVectorOf<void*>*                  fLoadPool;    

    /***
     *   object counter
     ***/
	XSerializedObjectId_t                  fObjectCount;

    //to allow grammar pool to set storer level when loading
    friend class XMLGrammarPoolImpl;
};

inline bool XSerializeEngine::isStoring() const
{
    return (fStoreLoad == mode_Store);
}

inline bool XSerializeEngine::isLoading() const
{
    return (fStoreLoad == mode_Load);
}

inline XSerializeEngine& operator<<(XSerializeEngine&       serEng
                                  , XSerializable* const    serObj)
{
	serEng.write(serObj); 
    return serEng; 
}

inline void XSerializeEngine::ensureStoring() const
{
	Assert(isStoring(), XMLExcepts::XSer_Storing_Violation);
}

inline void XSerializeEngine::ensureLoading() const
{
	Assert(isLoading(), XMLExcepts::XSer_Loading_Violation);
}



inline void XSerializeEngine::Assert(bool toEval
                                   , const XMLExcepts::Codes toThrow) const
{
    if (!toEval)
    {
        ThrowXMLwithMemMgr(XSerializationException, toThrow, getMemoryManager());  
    }

}

inline void XSerializeEngine::readString(XMLCh*&        toRead
                                       , int&           bufferLen)
{
    int  dummyDataLen;                
    readString(toRead, bufferLen, dummyDataLen);
}

inline void XSerializeEngine::readString(XMLCh*&        toRead)
{
    int  dummyBufferLen;
    int  dummyDataLen;
    readString(toRead, dummyBufferLen, dummyDataLen);
}

inline void XSerializeEngine::readString(XMLByte*&      toRead
                                       , int&           bufferLen)
{
    int  dummyDataLen;
    readString(toRead, bufferLen, dummyDataLen);
}

inline void XSerializeEngine::readString(XMLByte*&      toRead)
{
    int  dummyBufferLen;
    int  dummyDataLen;
    readString(toRead, dummyBufferLen, dummyDataLen);
}

inline 
XMLSize_t XSerializeEngine::getBufSize() const
{
    return fBufSize;
}

inline 
XMLSize_t XSerializeEngine::getBufCur() const
{
    return XMLSize_t (fBufCur-fBufStart);
}

inline 
XMLSize_t XSerializeEngine::getBufCurAccumulated() const
{
    return (fBufCount - (isStoring() ? 0: 1)) * fBufSize + (fBufCur-fBufStart);
}

inline 
XMLSize_t XSerializeEngine::getBufCount() const
{
    return fBufCount;
}

inline 
unsigned short XSerializeEngine::getStorerLevel() const
{
    return fStorerLevel;
}

/***
 *  Ought to be nested class
 ***/
class XSerializedObjectId : public XMemory
{
public:

    ~XSerializedObjectId(){};

private:

    inline XSerializedObjectId(XSerializeEngine::XSerializedObjectId_t val):
        fData(val) { };

    inline XSerializeEngine::XSerializedObjectId_t getValue() const {return fData; };

    friend class XSerializeEngine;

private:
    // -----------------------------------------------------------------------
    //  Unimplemented constructors and operators
    // -----------------------------------------------------------------------
	XSerializedObjectId();
    XSerializedObjectId(const XSerializedObjectId&);
	XSerializedObjectId& operator=(const XSerializedObjectId&);

    XSerializeEngine::XSerializedObjectId_t    fData;

};


XERCES_CPP_NAMESPACE_END

#endif
