////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// 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/ScriptingRuntime.h 1.2 2009/06/05 11:51:38 arturoc Exp $
//                                                                                                                    //
///////// 120 columns, Tab = Insert 3 Spaces ///////////////////////////////////////////////////////////////////////////

/**
   This header file imports the Microsoft Scripting Runtime type library as well as defines code that deals with the
   Scripting Runtime in a general fashion.
*/
#pragma once

// Import the "Scripting" type library (which is the windows-supplied COM library that has a "helpstring" of 
// "Microsoft Scripting Runtime"), so that we can use the Scripting.Dictionary object.
// Exclude the things in the library that we're not using and cause compiler errors because the symbols that are
// generated conflict with other things from other libraries.
//
#import "LibID:420B2830-E718-11CF-893D-00A0C9054228"                             \
            exclude("IFileSystem", "IFileSystem3", "IDrive", "IDriveCollection", \
                    "IFolder", "IFolderCollection", "IFile", "IFileCollection")  \
            raw_interfaces_only

#include "Exception.h"
#include "Preproc.h" 

namespace ComSerSdk
{
namespace Detail
{

void SetLastError(const std::string & sMsg, HRESULT hRresult);

/**
   Uses RAII to detach a _variant_t object from the variant that it manages when the PutItem() function exits scope.
*/
class CVariantDetacher
{
   _variant_t * m_poVarToDetachOnDestruct;

public:

   CVariantDetacher():
      m_poVarToDetachOnDestruct(NULL)
   {}
   
   void SetVariant(_variant_t * poVarToDetachOnDestruct)
   {
      m_poVarToDetachOnDestruct = poVarToDetachOnDestruct;
   }

   ~CVariantDetacher()
   {
      if(m_poVarToDetachOnDestruct)
      {
         m_poVarToDetachOnDestruct->Detach();
      }
   }
};

/**
   Places oItem into spoDictionary, associating it with oKey, throwing an exception (configured with oCodeLocation)
   if the operation fails.
*/
inline HRESULT PutItem(Scripting::IDictionaryPtr & spoDictionary,
                    _variant_t oKey,
                    const _variant_t & oItem,
                    const CCodeLocation & oCodeLocation,
                    bool bThrowOnError = true)
{
   _variant_t oItemToPut;
   CVariantDetacher oVariantDetacher;

   if((oItem.vt & 0xFF00) == VT_ARRAY)
   {
      // Make sure to do only shallow copies of SAFEARRAYs.

      oItemToPut.vt     = oItem.vt;
      oItemToPut.parray = oItem.parray;
      oVariantDetacher.SetVariant(&oItemToPut);
   }
   else
   {
      oItemToPut = oItem;
   }

   const HRESULT hResult = spoDictionary->put_Item(&oKey, &oItemToPut);

   if(FAILED(hResult))
   {
      std::ostringstream oStrStream;
      oStrStream << "IDictionary.PutItem(Key=" << oKey << "(VARIANT::vt=" << oKey.vt << "),Item=" << oItem << 
                    "(VARIANT::vt=" << oItem.vt << "))";
      if (bThrowOnError)
      {
         throw CHresultException("Could not put item into dictionary.",
                                 hResult,
                                 oStrStream.str(),
                                 oCodeLocation);
      }
      else
      {
         /*_AtlModule.*/SetLastError("Could not put item into dictionary. " + oStrStream.str(), hResult);
      }

   }
   return hResult;
}

/**
   Helper for determining if oKey exists in spoDictionary, throwing an exception configured with oCodeLocation if the
   operation fails.
*/
inline bool Exists(Scripting::IDictionaryPtr & spoDictionary, _variant_t oKey, const CCodeLocation & oCodeLocation)
{
   VARIANT_BOOL bExists = VARIANT_FALSE;
   const HRESULT hResult = spoDictionary->Exists(&oKey, &bExists);
   if(FAILED(hResult))
   {
      std::ostringstream oStrStream;
      oStrStream << "spoDictionary->Exists(Key=" << oKey << ")";
      throw CHresultException("Could not check if key exists in dictionary.",
                              hResult,
                              oStrStream.str(),
                              oCodeLocation);
   }
   return bExists != VARIANT_FALSE;
}

}// end namespace Detail
}// end namespace ComSerSdk
