#ifndef __OSC__SIGCOMP_MESSAGE
#define __OSC__SIGCOMP_MESSAGE 1

/* ***********************************************************************
   Open SigComp -- Implementation of RFC 3320 Signaling Compression

   Copyright 2005 Estacado Systems, LLC

   Your use of this code is governed by the license under which it
   has been provided to you. Unless you have a written and signed
   document from Estacado Systems, LLC stating otherwise, your license
   is as provided by the GNU General Public License version 2, a copy
   of which is available in this project in the file named "LICENSE."
   Alternately, a copy of the licence is available by writing to
   the Free Software Foundation, Inc., 59 Temple Place, Suite 330,
   Boston, MA 02110-1307 USA

   Unless your use of this code is goverened by a written and signed
   contract containing provisions to the contrary, this program is
   distributed WITHOUT ANY WARRANTY; without even the implied warranty
   of MERCHANTABILITY of FITNESS FOR A PARTICULAR PURPOSE. See the
   license for additional details.

   To discuss alternate licensing terms, contact info@estacado.net
 *********************************************************************** */

/**
  @file SigcompMessage.h
  @brief Header file for osc::SigcompMessage class.
*/


#include "Types.h"
#include "NackCodes.h"
#include "BitBuffer.h"

#ifdef DEBUG
#include <ostream>
#endif

namespace osc
{
  class Buffer;

  /**
    Encapsulates a SigComp message.

    SigcompMessage is used for parsing, accessing, constructing, and
    serializing SigComp messages. It also performs stream escaping and
    framing when seralizing for stream transports.

    The SigcompMessage class operates on one or more buffers
    of bytes which it may or may not own, depending on how it is
    constructed. Internally, it has three pointers that may or may not
    reference separate buffers:

    @li SigComp header (always one byte long, never deleted)

    @li Returned Feedback information (may be null, never deleted)

    @li Partial State Identifier or Code Length/Code Destination/Uploaded
        Bytecodes, followed by Input data or NACK details

    If a SigComp message is received from the wire in a single buffer,
    then the SigcompMessage class does not take possession of the
    buffer. At construction time, it initializes the pointers to
    appropriate places within the SigComp message, and sets the Boolean
    flag associated with the pointer so that the destructor does not
    attempt to delete the buffer.

    When a compressor creates a SigcompMessage from scratch, these
    buffers are initialized as follows:

    @li The SigComp header pointer is initialized to point to a one-byte
        field inside the SigcompMessage class itself.

    @li The Returned Feedback information points directly into the
        Compartment. This compartment contains the Returned Feedback
        - if any - generated by the most recent run of the UDVM
        associated with that compartment. See RFC 3320 section 9.4.9
        for a discussion of how Returned Feedback is handled.  The user
        of the SigComp stack needs to copy this information out of the
        SigcompMessage (either explicitly or by calling an appropriate
        method that copies it into the kernel space, like "sendto")
        before the Compartment is released.

    @li For compressed messages, the final field is a statically allocated
        buffer, allocated by the Stack object. The SigcompMessage class
        will not delete it.

    @li For NACK objects, the final field is a dynamically allocated
        buffer, allocated by the SigcompMessage object itself. The
        SigcompMessage object is responsible for deleting this buffer.

    Accessors on the SigcompMessage include a mechanism to retrieve an
    iovec, suitable for use in the "sendmsg" function defined by
    POSIX, to allow low-copy implementations.

    @todo Add zero-copy accessor for message contents
  */

  class SigcompMessage
  {
    public:

      SigcompMessage(const osc::byte_t *buffer,
                     size_t bufferLength);

      SigcompMessage(osc::byte_t *byteCodes,
                     osc::u16     codeLength,
                     osc::u16     codeDestination,
                     osc::u16     inputSize);

      SigcompMessage(const osc::byte_t *stateId,
                     osc::u16     stateIdLength,
                     osc::u16     inputSize);

      SigcompMessage(osc::nack_code_t      reason,
                     osc::u8               opcode,
                     osc::u16              pc,
                     osc::SigcompMessage  &failedMessage,
                     const osc::byte_t    *errorDetails = 0,
                     size_t                errorDetailLength = 0);


      ~SigcompMessage();

      void takeOwnership(osc::byte_t *b);

      void addReturnedFeedback(const osc::byte_t *feedback,
                               osc::u16 feedbackLength);
      void addReturnedFeedback(const osc::Buffer &);
 
      SigcompMessage * operator &(){ return this; }
      SigcompMessage const * operator &() const { return this; }

      osc::byte_t *getDatagramMessage();
      size_t getDatagramLength() const;

      osc::byte_t *getStreamMessage();
      size_t getStreamLength();

      void setInputLength(size_t length);

      void getSha1Hash(osc::byte_t buffer[20], size_t length=20) const;

#if 0
      /* Zero-copy accessors for transmission */
      struct iovec *getVector();
      size_t getVectorLength();
#endif

      bool isValid() const { return m_status == osc::OK; }
      bool isNack() const { return m_isNack; }
      osc::nack_code_t getStatus() const { return m_status; } 

      const osc::byte_t *getReturnedFeedback() const 
        { return m_returnedFeedback; }

      size_t getReturnedFeedbackLength() const
        { return m_returnedFeedbackLength; }

      const osc::byte_t *getStateId() const { return m_stateId; }
      size_t getStateIdLength() const { return m_stateIdLength; }

      osc::byte_t *getInput() { return m_input; }
      const osc::byte_t *getInput() const { return m_input; }

      size_t getInputLength() const { return m_inputLength; }
      osc::BitBuffer &getBitBuffer() { return m_inputBuffer; }

      const osc::byte_t *getBytecodes() const { return m_bytecodes; }
      osc::u16 getBytecodeLength() const { return m_codeLength; }
      osc::u16 getBytecodeDestination() const { return m_codeDestination; }

      osc::u16 getHeaderLength() const { return m_headerLength; }

      // Nack decoding; this is all fixed-field within the input
      // section of the message.
      osc::nack_code_t getNackReason()  const
        { return static_cast<osc::nack_code_t>(m_input[0]); }
      osc::u8 getNackOpcode() const { return m_input[1]; }
      osc::u16 getNackPc() const { return (m_input[2] << 8) | m_input[3]; }
      const osc::sha1_t &getNackSha1() const 
        {
          return *reinterpret_cast<const osc::sha1_t*>(m_input + 4);
        }
      osc::byte_t *getNackDetails() const { return m_input + 24; }
      size_t getNackDetailLength() const { return m_inputLength - 24; }

#ifdef DEBUG
      void dump(std::ostream &, unsigned int indent = 0) const;
#endif
     
    protected:

    private:
      /* if you define these, move them to public */
      SigcompMessage(SigcompMessage const &);
      SigcompMessage& operator=(SigcompMessage const &);

      osc::byte_t m_header;

      /** This is set only if we need to take custody of a message
          received from the wire. */
      osc::byte_t *m_buffer;

      osc::byte_t *m_returnedFeedback;
      size_t m_returnedFeedbackLength;
      bool m_ownReturnedFeedback;
     
      osc::byte_t *m_body;
      size_t m_bodySize;
      bool m_ownBody;

      osc::nack_code_t m_status;

      osc::byte_t *m_stateId;
      size_t m_stateIdLength;

      osc::byte_t *m_input;
      size_t m_inputLength;
      osc::BitBuffer m_inputBuffer;

      osc::u16 m_codeLength;
      osc::u16 m_codeDestination;
      osc::byte_t *m_bytecodes;

      bool m_isNack;

      osc::u16 m_headerLength;

      // Various internal buffers that may or may not be
      // allocated, depending on what has been requested
      osc::byte_t *m_streamBuffer;
      size_t       m_streamBufferLength;

      osc::byte_t *m_datagramBuffer;

      enum {RETURNED_FEEDBACK_PRESENT = 0x04};
      
      /*!
        @param buffer a buffer containing the data that is desired to be escaped
        @returns a buffer of escaped bytes
        Escapes a set of byte using the method described in RFC 3320:4.2
      */
    //osc::buffer_descriptor_t * escapeBytes(osc::buffer_descriptor_t * buffer);
  };

#ifdef DEBUG
  std::ostream& operator<< (std::ostream &, const osc::SigcompMessage &);
#endif

}

#endif
