// Windows/PropVariant.cpp

#include "StdAfx.h"

#include "../Common/Defs.h"

#include "PropVariant.h"

namespace NWindows {
namespace NCOM {

BSTR AllocBstrFromAscii(const char *s) throw()
{
  if (!s)
    return NULL;
  UINT len = (UINT)strlen(s);
  BSTR p = ::SysAllocStringLen(NULL, len);
  if (p)
  {
    for (UINT i = 0; i <= len; i++)
      p[i] = (Byte)s[i];
  }
  return p;
}

HRESULT PropVarEm_Alloc_Bstr(PROPVARIANT *p, unsigned numChars) throw()
{
  p->bstrVal = ::SysAllocStringLen(NULL, numChars);
  if (!p->bstrVal)
  {
    p->vt = VT_ERROR;
    p->scode = E_OUTOFMEMORY;
    return E_OUTOFMEMORY;
  }
  p->vt = VT_BSTR;
  return S_OK;
}

HRESULT PropVarEm_Set_Str(PROPVARIANT *p, const char *s) throw()
{
  p->bstrVal = AllocBstrFromAscii(s);
  if (p->bstrVal)
  {
    p->vt = VT_BSTR;
    return S_OK;
  }
  p->vt = VT_ERROR;
  p->scode = E_OUTOFMEMORY;
  return E_OUTOFMEMORY;
}

CPropVariant::CPropVariant(const PROPVARIANT &varSrc)
{
  vt = VT_EMPTY;
  InternalCopy(&varSrc);
}

CPropVariant::CPropVariant(const CPropVariant &varSrc)
{
  vt = VT_EMPTY;
  InternalCopy(&varSrc);
}

CPropVariant::CPropVariant(BSTR bstrSrc)
{
  vt = VT_EMPTY;
  *this = bstrSrc;
}

CPropVariant::CPropVariant(LPCOLESTR lpszSrc)
{
  vt = VT_EMPTY;
  *this = lpszSrc;
}

CPropVariant& CPropVariant::operator=(const CPropVariant &varSrc)
{
  InternalCopy(&varSrc);
  return *this;
}

CPropVariant& CPropVariant::operator=(const PROPVARIANT &varSrc)
{
  InternalCopy(&varSrc);
  return *this;
}

CPropVariant& CPropVariant::operator=(BSTR bstrSrc)
{
  *this = (LPCOLESTR)bstrSrc;
  return *this;
}

static const char * const kMemException = "out of memory";

CPropVariant& CPropVariant::operator=(LPCOLESTR lpszSrc)
{
  InternalClear();
  vt = VT_BSTR;
  wReserved1 = 0;
  bstrVal = ::SysAllocString(lpszSrc);
  if (!bstrVal && lpszSrc)
  {
    throw kMemException;
    // vt = VT_ERROR;
    // scode = E_OUTOFMEMORY;
  }
  return *this;
}

CPropVariant& CPropVariant::operator=(const UString &s)
{
  InternalClear();
  vt = VT_BSTR;
  wReserved1 = 0;
  bstrVal = ::SysAllocStringLen(s, s.Len());
  if (!bstrVal)
    throw kMemException;
  return *this;
}

CPropVariant& CPropVariant::operator=(const UString2 &s)
{
  /*
  if (s.IsEmpty())
    *this = L"";
  else
  */
  {
    InternalClear();
    vt = VT_BSTR;
    wReserved1 = 0;
    bstrVal = ::SysAllocStringLen(s.GetRawPtr(), s.Len());
    if (!bstrVal)
      throw kMemException;
    /* SysAllocStringLen probably appends a null-terminating character for NULL string.
       But it doesn't specified in MSDN.
       But we suppose that it works

    if (!s.GetRawPtr())
    {
      *bstrVal = 0;
    }
    */

    /* MSDN: Windows CE: SysAllocStringLen() : Passing invalid (and under some circumstances NULL)
                         pointers to this function causes  an unexpected termination of the application.
       Is it safe? Maybe we must chamnge the code for that case ? */
  }
  return *this;
}

CPropVariant& CPropVariant::operator=(const char *s)
{
  InternalClear();
  vt = VT_BSTR;
  wReserved1 = 0;
  bstrVal = AllocBstrFromAscii(s);
  if (!bstrVal)
  {
    throw kMemException;
    // vt = VT_ERROR;
    // scode = E_OUTOFMEMORY;
  }
  return *this;
}

CPropVariant& CPropVariant::operator=(bool bSrc) throw()
{
  if (vt != VT_BOOL)
  {
    InternalClear();
    vt = VT_BOOL;
  }
  boolVal = bSrc ? VARIANT_TRUE : VARIANT_FALSE;
  return *this;
}

BSTR CPropVariant::AllocBstr(unsigned numChars)
{
  if (vt != VT_EMPTY)
    InternalClear();
  vt = VT_BSTR;
  wReserved1 = 0;
  bstrVal = ::SysAllocStringLen(NULL, numChars);
  if (!bstrVal)
  {
    throw kMemException;
    // vt = VT_ERROR;
    // scode = E_OUTOFMEMORY;
  }
  return bstrVal;
}

#define SET_PROP_FUNC(type, id, dest) \
  CPropVariant& CPropVariant::operator=(type value) throw() \
  { if (vt != id) { InternalClear(); vt = id; } \
    dest = value; return *this; }

SET_PROP_FUNC(Byte, VT_UI1, bVal)
// SET_PROP_FUNC(Int16, VT_I2, iVal)
SET_PROP_FUNC(Int32, VT_I4, lVal)
SET_PROP_FUNC(UInt32, VT_UI4, ulVal)
SET_PROP_FUNC(UInt64, VT_UI8, uhVal.QuadPart)
SET_PROP_FUNC(Int64, VT_I8, hVal.QuadPart)
SET_PROP_FUNC(const FILETIME &, VT_FILETIME, filetime)

HRESULT PropVariant_Clear(PROPVARIANT *prop) throw()
{
  switch (prop->vt)
  {
    case VT_EMPTY:
    case VT_UI1:
    case VT_I1:
    case VT_I2:
    case VT_UI2:
    case VT_BOOL:
    case VT_I4:
    case VT_UI4:
    case VT_R4:
    case VT_INT:
    case VT_UINT:
    case VT_ERROR:
    case VT_FILETIME:
    case VT_UI8:
    case VT_R8:
    case VT_CY:
    case VT_DATE:
      prop->vt = VT_EMPTY;
      prop->wReserved1 = 0;
      prop->wReserved2 = 0;
      prop->wReserved3 = 0;
      prop->uhVal.QuadPart = 0;
      return S_OK;
  }
  return ::VariantClear((VARIANTARG *)prop);
  // return ::PropVariantClear(prop);
  // PropVariantClear can clear VT_BLOB.
}

HRESULT CPropVariant::Clear() throw()
{
  if (vt == VT_EMPTY)
    return S_OK;
  return PropVariant_Clear(this);
}

HRESULT CPropVariant::Copy(const PROPVARIANT* pSrc) throw()
{
  ::VariantClear((tagVARIANT *)this);
  switch (pSrc->vt)
  {
    case VT_UI1:
    case VT_I1:
    case VT_I2:
    case VT_UI2:
    case VT_BOOL:
    case VT_I4:
    case VT_UI4:
    case VT_R4:
    case VT_INT:
    case VT_UINT:
    case VT_ERROR:
    case VT_FILETIME:
    case VT_UI8:
    case VT_R8:
    case VT_CY:
    case VT_DATE:
      memmove((PROPVARIANT*)this, pSrc, sizeof(PROPVARIANT));
      return S_OK;
  }
  return ::VariantCopy((tagVARIANT *)this, (tagVARIANT *)const_cast<PROPVARIANT *>(pSrc));
}


HRESULT CPropVariant::Attach(PROPVARIANT *pSrc) throw()
{
  HRESULT hr = Clear();
  if (FAILED(hr))
    return hr;
  memcpy(this, pSrc, sizeof(PROPVARIANT));
  pSrc->vt = VT_EMPTY;
  return S_OK;
}

HRESULT CPropVariant::Detach(PROPVARIANT *pDest) throw()
{
  if (pDest->vt != VT_EMPTY)
  {
    HRESULT hr = PropVariant_Clear(pDest);
    if (FAILED(hr))
      return hr;
  }
  memcpy(pDest, this, sizeof(PROPVARIANT));
  vt = VT_EMPTY;
  return S_OK;
}

HRESULT CPropVariant::InternalClear() throw()
{
  if (vt == VT_EMPTY)
    return S_OK;
  HRESULT hr = Clear();
  if (FAILED(hr))
  {
    vt = VT_ERROR;
    scode = hr;
  }
  return hr;
}

void CPropVariant::InternalCopy(const PROPVARIANT *pSrc)
{
  HRESULT hr = Copy(pSrc);
  if (FAILED(hr))
  {
    if (hr == E_OUTOFMEMORY)
      throw kMemException;
    vt = VT_ERROR;
    scode = hr;
  }
}

int CPropVariant::Compare(const CPropVariant &a) throw()
{
  if (vt != a.vt)
    return MyCompare(vt, a.vt);
  switch (vt)
  {
    case VT_EMPTY: return 0;
    // case VT_I1: return MyCompare(cVal, a.cVal);
    case VT_UI1: return MyCompare(bVal, a.bVal);
    case VT_I2: return MyCompare(iVal, a.iVal);
    case VT_UI2: return MyCompare(uiVal, a.uiVal);
    case VT_I4: return MyCompare(lVal, a.lVal);
    case VT_UI4: return MyCompare(ulVal, a.ulVal);
    // case VT_UINT: return MyCompare(uintVal, a.uintVal);
    case VT_I8: return MyCompare(hVal.QuadPart, a.hVal.QuadPart);
    case VT_UI8: return MyCompare(uhVal.QuadPart, a.uhVal.QuadPart);
    case VT_BOOL: return -MyCompare(boolVal, a.boolVal);
    case VT_FILETIME: return ::CompareFileTime(&filetime, &a.filetime);
    case VT_BSTR: return 0; // Not implemented
    default: return 0;
  }
}

}}
