////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// This is a part of the COM Serialization SDK VC++ Library.
//  
// Copyright 2009 BPM Microsystems
// 
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT
// SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE
// FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE,
// ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
// DEALINGS IN THE SOFTWARE.
//                                                                                                                    //
// $Header: //sesca/src/Winprog/COMSerializationSDK/VC++/HeaderLib/ComSerSdk/rcs/ByteArray.h 1.1 2009/08/17 12:41:36 arturoc Exp $
//                                                                                                                    //
///////// 120 columns, Tab = Insert 3 Spaces ///////////////////////////////////////////////////////////////////////////

#pragma once
#include "Exception.h"
namespace ComSerSdk
{
namespace Detail
{

/**
   Base class for the very simple wrappers around CComSafeArray<BYTE>.
*/
class CByteArrayBase
{
public:

   /**
      Gets the value of an element in the SAFEARRAY.
      Both concretes of this class should have this member function, so it's defined here.
   */
   unsigned char GetAtIndex(unsigned uIndex) const
   {
      ThrowIfOutOfBounds(uIndex);
      return m_oWrappedSafeArray.GetAt(uIndex);
   }

   /**
      Gets the number of elements in the array.
      Both concretes of this class should have this member function, so it's defined here.
   */
   unsigned Size() const
   {
      return m_oWrappedSafeArray.GetCount();
   }

protected:

   /**
      So that concretes can perform operations on the encapsulated CComSafeArray.
   */
   CComSafeArray<unsigned char> & WrappedSafeArray()
   {
      return m_oWrappedSafeArray;
   }

   /**
      Both concretes want access to the raw pointer, but one wants a mutable version, the other must be const.
   */
   unsigned char * ProtectedGetRawPtr() const
   {
      return static_cast<unsigned char *>(m_oWrappedSafeArray.m_psa->pvData);
   }

   /**
      Helper for checking that the supplied index into the array is not out of the bounds of the array.
   */
   void ThrowIfOutOfBounds(unsigned uIndex) const
   {
      if(uIndex >= Size())
      {
         std::ostringstream oOutStrm;
         oOutStrm << "Could not access the value of an element at index " << uIndex << " because the index is "
                     "out-of-bounds.  The size of the array is " << Size() << ".";
         throw CLibraryException(oOutStrm.str(), COMSERSDK_CODELOCATION);
      }
   }

private:

   /**
      The CComSafeArray we wrap.
   */
   CComSafeArray<unsigned char> m_oWrappedSafeArray;
   
   /**
      Only CServerBase can create us.
   */
   friend class CServerBase;
};

}//end Detail namespace

/**
    Supplies simplified, read-only access to the inbound DDS data.  The SAFEARRAY that this wraps already exists and
    this class takes pains to ensure the data in the already-existing SAFEARRAY isn't needlessly copied.
*/
class CInBoundByteArray : public Detail::CByteArrayBase
{
public:

   /**
      The data in the SAFEARRAY we wrap isn't really ours to destroy, so this destructor innocuously detaches the
      CComSafeArray from it.
   */
   ~CInBoundByteArray()
   {
      WrappedSafeArray().Detach();
   }

   /**
      Provide the user of the library ready-only access to the data in the inbound SAFEARRAY through this and
      GetAtIndex() from the CByteArrayBase base class.
   */
   const unsigned char * GetRawPtr() const
   {
      return ProtectedGetRawPtr();
   }

private:
   
   /**
      Only CServerBase can create us.
   */
   friend class Detail::CServerBase;

   /**
      Does not copy the data in the array, merely attaches to the SAFEARRAY so that the library-user may have
      read-only access to it.
   */
   explicit CInBoundByteArray(VARIANT & oDataIn)
   {
      WrappedSafeArray().Attach(oDataIn.parray);
   }
};

/**
   Supplies simplified access to the data in a single-dimension SAFEARRAY of BYTEs, allowing the user of the library
   to specify the amount of data (set its size) and set the values of its elements.
*/
class COutBoundByteArray : public Detail::CByteArrayBase
{
private:
   
   /**
      Only CServerBase can create us.
   */
   friend class Detail::CServerBase;

public:
   
   /**
      Library user must first call this function to set the size of the array.
   */
   void Create(unsigned uNumBytes)
   {
      WrappedSafeArray().Create(uNumBytes);
   }

   /**
      Allows unchecked, writeable access to the byte array via a pointer.
   */
   unsigned char * GetRawPtr()
   {
      return ProtectedGetRawPtr();
   }

   /**
      Set a single element's value in the array.
   */
   void SetAtIndex(unsigned uIndex, unsigned char uVal)
   {
      ThrowIfOutOfBounds(uIndex);     
      WrappedSafeArray().SetAt(uIndex, uVal);
   }
};
}