// **********************************************************************
//
// Copyright (c) 2002
// IONA Technologies, Inc.
// Waltham, MA, USA
//
// All Rights Reserved
//
// **********************************************************************

#ifndef OB_VALUE_READER_WRITER_H
#define OB_VALUE_READER_WRITER_H

#include <OB/ValueReaderWriter_fwd.h>
#include <OB/Stream_fwd.h>
#include <OB/Object_fwd.h>
#include <OB/ValueBase_fwd.h>
#include <OB/AbstractBase_fwd.h>
#include <OB/TypeCode_fwd.h>
#include <OB/ORBInstance_fwd.h>
#include <OB/OCIBuffer_fwd.h>

#include <OB/Any.h>
#include <OB/TypeCodeConst.h>
#include <OB/OCITypes.h>
#include <OB/Hashtable.h>

namespace OB
{

//
// The ValueReader class
//
class ValueReader : public RefCount
{
    ValueReader(const ValueReader&);
    ValueReader& operator=(const ValueReader&);

public: // required by some compilers

    //
    // Chunk data
    //
    struct ChunkState
    {
        bool chunked;
        CORBA::Long nestingLevel;
        CORBA::Octet* chunkStart;
        CORBA::Long chunkSize;

        ChunkState()
            : chunked(false), nestingLevel(0), chunkStart(0), chunkSize(0)
        {
        }

        ChunkState(const ChunkState& v)
            : chunked(v.chunked), nestingLevel(v.nestingLevel),
              chunkStart(v.chunkStart), chunkSize(v.chunkSize)
        {
        }
    };

    //
    // Valuetype header data
    //
    struct Header
    {
        CORBA::Long tag;
        CORBA::Octet* headerPos;
        CORBA::Octet* dataPos;
        OB::StrSeq< int > ids;
        ChunkState state;
    };

private:

    struct OctetPtrHasher
    {
        static CORBA::ULong
        hash(const CORBA::Octet* k)
        {
            return (CORBA::ULong)((long)k);
        }

        static bool
        comp(const CORBA::Octet* k1, const CORBA::Octet* k2)
        {
            return k1 == k2;
        }
    };

    ORBInstance_var orbInstance_;
    InputStreamImpl* in_;
    OCI::BufferImpl* buf_;

    typedef Hashtable<const CORBA::Octet*, CORBA::ValueBase_var,
        OctetPtrHasher> InstanceTable;
    InstanceTable* instanceTable_;
    typedef Hashtable<const CORBA::Octet*, Header, OctetPtrHasher> HeaderTable;
    HeaderTable* headerTable_;
    typedef Hashtable<CORBA::Long, CORBA::Long, LongHasher> PositionTable;
    PositionTable* positionTable_;

    //
    // TODO: revisit removal of remarshalDepth and the reset of the
    // positionTable_ at each top level remarshal call.
    //

    ChunkState chunkState_;

    void readHeader(Header&);
    void readChunk(ChunkState&);
    void initHeader(Header&);
    void skipChunk();
    CORBA::ValueBase* createValue(const char*, const Header&);
    CORBA::ValueBase* readIndirection(const char*, const Header&);
    CORBA::ValueBase* read(const char*, const Header&);
    void copyValueState(CORBA::TypeCode_ptr, OutputStreamImpl*);
    CORBA::TypeCode_ptr findTypeCode(const char*, CORBA::TypeCode_ptr);

public:

    ValueReader(InputStreamImpl*);
    virtual ~ValueReader();

    static inline ValueReader_ptr _duplicate(ValueReader_ptr p)
    { if(p) p -> _OB_incRef(); return p; }

    static inline ValueReader_ptr _nil()
    { return 0; }

    CORBA::ValueBase* readValue();
    CORBA::ValueBase* readValue(const char*);
    CORBA::AbstractBase_ptr readAbstractInterface();

    CORBA::TypeCode_ptr remarshalValue(CORBA::TypeCode_ptr, OutputStreamImpl*);

    void readValueAny(CORBA::Any&, CORBA::TypeCode_ptr);

    void beginValue();
    void endValue();

    void checkChunk();
};

//
// The ValueWriter class
//
class ValueWriter : public RefCount
{
    //
    // Hide copy-constructor and assignment operator
    //
    ValueWriter(const ValueWriter&);
    ValueWriter& operator=(const ValueWriter&);

    //
    // The OutputStreamImpl
    //
    OutputStreamImpl* out_;

    //
    // The BufferImpl
    //
    OCI::BufferImpl* buf_;

    //
    // Are we currently writing chunks?
    //
    bool chunked_;

    //
    // The nesting level of chunked valuetypes
    //
    CORBA::Long nestingLevel_;

    //
    // True if we need to start a new chunk before the next write to the
    // output stream
    //
    bool needChunk_;

    //
    // The position in the buffer at which we need to patch the chunk size
    //
    CORBA::ULong chunkSizePos_;

    //
    // The position in the buffer of the last end tag
    //
    CORBA::ULong lastEndTagPos_;

    struct ValueBasePtrHasher
    {
        static CORBA::ULong
        hash(const CORBA::ValueBase* k)
        {
            return (CORBA::ULong)((long)k);
        }

        static bool
        comp(const CORBA::ValueBase* k1, const CORBA::ValueBase* k2)
        {
            return k1 == k2;
        }
    };

    //
    // These types are needed for indirection in valuetype marshalling.
    // InstanceTable records the position at which a valuetype was
    // marshalled. IdTable records the position of a repository ID.
    // IdListTable records the position of a list of repository IDs.
    //
    typedef Hashtable<CORBA::ValueBase*, CORBA::Long,
        ValueBasePtrHasher> InstanceTable;
    InstanceTable* instanceTable_;

    typedef Hashtable<CORBA::String_var, CORBA::Long,OB::StringHasher> IdTable;
    IdTable* idTable_;

    typedef Hashtable<OB::StrSeq<int>, CORBA::Long, 
	OB::StringSeqHasher> IdListTable;
    IdListTable* idListTable_;

    bool checkIndirection(CORBA::ValueBase*);
    void beginChunk();
    void endChunk();

public:

    ValueWriter(OutputStreamImpl*);
    ~ValueWriter();

    static inline ValueWriter_ptr _duplicate(ValueWriter_ptr p)
    { if(p) p -> _OB_incRef(); return p; }

    static inline ValueWriter_ptr _nil()
    { return 0; }

    void writeValue(CORBA::ValueBase*, const char* = 0);

    void writeAbstractInterface(CORBA::AbstractBase_ptr);

    CORBA::Long beginValue(CORBA::Long, const OB::StrSeq< int >&, bool);
    void endValue();

    void checkBeginChunk();
};

} // End of namespace OB

namespace CORBA
{

inline void
release(OB::ValueReader_ptr p)
{
    if(p)
        p -> _OB_decRef();
}

inline Boolean
is_nil(OB::ValueReader_ptr p)
{
    return p == 0;
}

inline void
release(OB::ValueWriter_ptr p)
{
    if(p)
        p -> _OB_decRef();
}

inline Boolean
is_nil(OB::ValueWriter_ptr p)
{
    return p == 0;
}

} // End of namespace CORBA

#endif
