#define WIN32_LEAN_AND_MEAN

#ifndef __PLMISC_CPP
#define __PLMISC_CPP
#endif


#include <windows.h>

#include "plmisc.h"


///////////////////////////////////////////////////////////////////////////////
//
// defines
//
///////////////////////////////////////////////////////////////////////////////


///////////////////////////////////////////////////////////////////////////////
//
// globals
//
///////////////////////////////////////////////////////////////////////////////

// index to access tls space
DWORD TlsIndex = -1;


///////////////////////////////////////////////////////////////////////////////
//
// functions
//
///////////////////////////////////////////////////////////////////////////////

///////////////////////////////////////////////////////////////////////////////
//
// get/put strings, pointers and int's from/to hashes, arrays and scalars
// safely
//
///////////////////////////////////////////////////////////////////////////////

PWSTR WStrFromHash(PERL_CALL HV *hash, PSTR idx, BOOL isRef)
{
  if (isRef && hash) {
    if (!(hash = SvROK(hash) ? (HV*)SvRV(hash) : NULL))
      return NULL;

    if (SvTYPE(hash) != SVt_PVHV)
      return NULL;
  }

  SV **item = hash ? hv_fetch(hash, idx, strlen(idx), 0) : NULL;

  if (item && *item)
    return S2W(SvPV(*item, PL_na));
  else
    return NULL;
}


PSTR StrFromHash(PERL_CALL HV *hash, PSTR idx, BOOL isRef)
{
  if (isRef && hash) {
    if (!(hash = SvROK(hash) ? (HV*)SvRV(hash) : NULL))
      return NULL;

    if (SvTYPE(hash) != SVt_PVHV)
      return NULL;
  }

  SV **item = hash ? hv_fetch(hash, idx, strlen(idx), 0) : NULL;

  if (item && *item)
    return SvPV(*item, PL_na);
  else
    return NULL;
}


int SLenFromHash(PERL_CALL HV *hash, PSTR idx, BOOL isRef)
{
  if (isRef && hash) {
    if (!(hash = SvROK(hash) ? (HV*)SvRV(hash) : NULL))
      return NULL;

    if (SvTYPE(hash) != SVt_PVHV)
      return NULL;
  }

  SV **item = hash ? hv_fetch(hash, idx, strlen(idx), 0) : NULL;

  if (item && *item)
    return SvLEN(*item) - 1;
  else
    return NULL;
}


PVOID PtrFromHash(PERL_CALL HV *hash, PSTR idx, unsigned *len, BOOL isRef)
{
  if (isRef && hash) {
    if (!(hash = SvROK(hash) ? (HV*)SvRV(hash) : NULL))
      return NULL;

    if (SvTYPE(hash) != SVt_PVHV)
      return NULL;
  }

  SV **item = hash ? hv_fetch(hash, idx, strlen(idx), 0) : NULL;

  if (item && *item)
    return len ? SvPV(*item, *len) : SvPV(*item, PL_na);
  else
    return NULL;
}


int IntFromHash(PERL_CALL HV *hash, PSTR idx, BOOL isRef)
{
  if (isRef && hash) {
    if (!(hash = SvROK(hash) ? (HV*)SvRV(hash) : NULL))
      return NULL;

    if (SvTYPE(hash) != SVt_PVHV)
      return NULL;
  }

  SV **item = hash ? hv_fetch(hash, idx, strlen(idx), 0) : NULL;

  if (item && *item)
    return SvIV(*item);
  else
    return NULL;
}


HV *HashFromHash(PERL_CALL HV *hash, PSTR idx, BOOL isRef, BOOL convRef)
{
  if (isRef && hash) {
    if (!(hash = SvROK(hash) ? (HV*)SvRV(hash) : NULL))
      return NULL;

    if (SvTYPE(hash) != SVt_PVHV)
      return NULL;
  }

  SV **item = hash ? hv_fetch(hash, idx, strlen(idx), 0) : NULL;

  if (item && *item) {
    SV *itemDeRef = convRef && SvTYPE(*item) == SVt_RV ? SvRV(*item) : *item;

    return SvTYPE(itemDeRef) == SVt_PVHV ? (HV*)itemDeRef : NULL;
  }
  else
    return NULL;
}


AV *ArrayFromHash(PERL_CALL HV *hash, PSTR idx, BOOL isRef, BOOL convRef)
{
  if (isRef && hash) {
    if (!(hash = SvROK(hash) ? (HV*)SvRV(hash) : NULL))
      return NULL;

    if (SvTYPE(hash) != SVt_PVHV)
      return NULL;
  }

  SV **item = hash ? hv_fetch(hash, idx, strlen(idx), 0) : NULL;

  if (item && *item) {
    SV *itemDeRef = convRef && SvTYPE(*item) == SVt_RV ? SvRV(*item) : *item;

    return SvTYPE(itemDeRef) == SVt_PVAV ? (AV*)itemDeRef : NULL;
  }
  else
    return NULL;
}


PWSTR WStrFromArray(PERL_CALL AV *array, int idx, BOOL isRef)
{
  if (isRef && array) {
    if (!(array = SvROK(array) ? (AV*)SvRV(array) : NULL))
      return NULL;

    if (SvTYPE(array) != SVt_PVAV)
      return NULL;
  }

  SV **item = array ? av_fetch(array, idx, 0) : NULL;

  if (item && *item)
    return S2W(SvPV(*item, PL_na));
  else
    return NULL;
}


PSTR StrFromArray(PERL_CALL AV *array, int idx, BOOL isRef)
{
  if (isRef && array) {
    if (!(array = SvROK(array) ? (AV*)SvRV(array) : NULL))
      return NULL;

    if (SvTYPE(array) != SVt_PVAV)
      return NULL;
  }

  SV **item = array ? av_fetch(array, idx, 0) : NULL;

  if (item && *item)
    return SvPV(*item, PL_na);
  else
    return NULL;
}


int SLenFromArray(PERL_CALL AV *array, int idx, BOOL isRef)
{
  if (isRef && array) {
    if (!(array = SvROK(array) ? (AV*)SvRV(array) : NULL))
      return NULL;

    if (SvTYPE(array) != SVt_PVAV)
      return NULL;
  }

  SV **item = array ? av_fetch(array, idx, 0) : NULL;

  if (item && *item)
    return SvLEN(*item) - 1;
  else
    return NULL;
}


PVOID PtrFromArray(PERL_CALL AV *array, int idx, unsigned *len, BOOL isRef)
{
  if (isRef && array) {
    if (!(array = SvROK(array) ? (AV*)SvRV(array) : NULL))
      return NULL;

    if (SvTYPE(array) != SVt_PVAV)
      return NULL;
  }

  SV **item = array ? av_fetch(array, idx, 0) : NULL;

  if (item && *item)
    return len ? SvPV(*item, *len) : SvPV(*item, PL_na);
  else
    return NULL;
}


int IntFromArray(PERL_CALL AV *array, int idx, BOOL isRef)
{
  if (isRef && array) {
    if (!(array = SvROK(array) ? (AV*)SvRV(array) : NULL))
      return NULL;

    if (SvTYPE(array) != SVt_PVAV)
      return NULL;
  }

  SV **item = array ? av_fetch(array, idx, 0) : NULL;

  if (item && *item)
    return SvIV(*item);
  else
    return NULL;
}


HV *HashFromArray(PERL_CALL AV *array, int idx, BOOL isRef, BOOL convRef)
{
  if (isRef && array) {
    if (!(array = SvROK(array) ? (AV*)SvRV(array) : NULL))
      return NULL;

    if (SvTYPE(array) != SVt_PVAV)
      return NULL;
  }

  SV **item = array ? av_fetch(array, idx, 0) : NULL;

  if (item && *item) {
    SV *itemDeRef = convRef && SvTYPE(*item) == SVt_RV ? SvRV(*item) : *item;

    return SvTYPE(itemDeRef) == SVt_PVHV ? (HV*)itemDeRef : NULL;
  }
  else
    return NULL;
}


AV *ArrayFromArray(PERL_CALL AV *array, int idx, BOOL isRef, BOOL convRef)
{
  if (isRef && array) {
    if (!(array = SvROK(array) ? (AV*)SvRV(array) : NULL))
      return NULL;

    if (SvTYPE(array) != SVt_PVAV)
      return NULL;
  }

  SV **item = array ? av_fetch(array, idx, 0) : NULL;

  if (item && *item) {
    SV *itemDeRef = convRef && SvTYPE(*item) == SVt_RV ? SvRV(*item) : *item;

    return SvTYPE(itemDeRef) == SVt_PVAV ? (AV*)itemDeRef : NULL;
  }
  else
    return NULL;
}


PWSTR WStrFromScalar(PERL_CALL SV *string, BOOL isRef)
{
  if (!string)
    return NULL;

  if (isRef && !(string = SvROK(string) ? SvRV(string) : NULL))
    return NULL;

  return S2W(SvPV(string, PL_na));
}


PSTR StrFromScalar(PERL_CALL SV *string, BOOL isRef)
{
  if (!string)
    return NULL;

  if (isRef && !(string = SvROK(string) ? SvRV(string) : NULL))
    return NULL;

  return SvPV(string, PL_na);
}


int SLenFromScalar(PERL_CALL SV *string, BOOL isRef)
{
  if (!string)
    return NULL;

  if (isRef && !(string = SvROK(string) ? SvRV(string) : NULL))
    return NULL;

  return SvLEN(string) - 1;
}


int IntFromScalar(PERL_CALL SV *string, BOOL isRef)
{
  if (!string)
    return NULL;

  if (isRef && !(string = SvROK(string) ? SvRV(string) : NULL))
    return NULL;

  return SvIV(string);
}


int WStrToHash(PERL_CALL HV *hash, PSTR idx, PWSTR str)
{
  if (!hash || !idx)
    return 0;

  PSTR strPtr = str ? W2S(str) : NULL;

  if (strPtr)
    hv_store(hash, idx, strlen(idx), newSVpv(strPtr, strlen(strPtr)), 0);

  FreeStr(strPtr);

  return 1;
}


int WNStrToHash(PERL_CALL HV *hash, PSTR idx, PWSTR str, DWORD strLen)
{
  if (!hash || !idx)
    return 0;

  PSTR strPtr = str ? W2S(str, strLen) : NULL;

  if (strPtr) {
    strPtr[strLen - 1] = 0;
    hv_store(hash, idx, strlen(idx), newSVpv(strPtr, strlen(strPtr)), 0);
  }

  FreeStr(strPtr);

  return 1;
}


int StrToHash(PERL_CALL HV *hash, PSTR idx, PSTR str)
{
  if (!hash || !idx)
    return 0;

  if (str)
    hv_store(hash, idx, strlen(idx), newSVpv(str, strlen(str)), 0);

  return 1;
}


int PtrToHash(PERL_CALL HV *hash, PSTR idx, PVOID ptr, int len)
{
  if (!hash || !idx)
    return 0;

  if (ptr)
    hv_store(hash, idx, strlen(idx), newSVpv((PSTR)ptr, len), 0);

  return 1;
}


int IntToHash(PERL_CALL HV *hash, PSTR idx, int val)
{
  if (!hash || !idx)
    return 0;

  hv_store(hash, idx, strlen(idx), newSViv(val), 0);

  return 1;
}


int RefToHash(PERL_CALL HV *hash, PSTR idx, PVOID ptr)
{
  if (!hash || !idx)
    return 0;

  if (ptr)
    hv_store(hash, idx, strlen(idx), (SV*)newRV((SV*)ptr), 0);

  return 1;
}


int WStrToArray(PERL_CALL AV *array, PWSTR str)
{
  if (!array)
    return 0;

  PSTR strPtr = str ? W2S(str) : NULL;

  if (strPtr)
    av_push(array, newSVpv(strPtr, strlen(strPtr)));

  FreeStr(strPtr);

  return 1;
}


int WNStrToArray(PERL_CALL AV *array, PWSTR str, DWORD strLen)
{
  if (!array)
    return 0;

  PSTR strPtr = str ? W2S(str, strLen) : NULL;

  if (strPtr) {
    strPtr[strLen - 1] = 0;
    av_push(array, newSVpv(strPtr, strlen(strPtr)));
  }

  FreeStr(strPtr);

  return 1;
}


int StrToArray(PERL_CALL AV *array, PSTR str)
{
  if (!array)
    return 0;

  if (str)
    av_push(array, newSVpv(str, strlen(str)));

  return 1;
}


int IntToArray(PERL_CALL AV *array, int val)
{
  if (!array)
    return 0;

  av_push(array, newSViv(val));

  return 1;
}


int PtrToArray(PERL_CALL AV *array, PVOID ptr, int len)
{
  if (!array)
    return 0;

  if (ptr)
    av_push(array, newSVpv((PSTR)ptr, len));

  return 1;
}


int RefToArray(PERL_CALL AV *array, PVOID ptr)
{
  if (!array)
    return 0;

  if (ptr)
    av_push(array, (SV*)newRV((SV*)ptr));

  return 1;
}


int WStrToScalar(PERL_CALL SV *string, PWSTR str)
{
  if (!string)
    return 0;

  PSTR strPtr = str ? W2S(str) : NULL;

  if (strPtr)
    sv_setpv(string, strPtr);

  FreeStr(strPtr);

  return 1;
}


int StrToScalar(PERL_CALL SV *string, PSTR str)
{
  if (!string)
    return 0;

  if (str)
    sv_setpv(string, str);

  return 1;
}


int IntToScalar(PERL_CALL SV *string, int val)
{
  if (!string)
    return 0;

  sv_setiv(string, val);

  return 1;
}


int PtrToScalar(PERL_CALL SV *string, PVOID ptr, int len)
{
  if (!string)
    return 0;

  if (ptr)
    sv_setpvn(string, (PSTR)ptr, len);

  return 1;
}

///////////////////////////////////////////////////////////////////////////////
//
// create new hashes, arrays or references; if there is not enougth memory an
// execption will be raised; use the NewHV/AV/RV macros to call it
//
///////////////////////////////////////////////////////////////////////////////

HV *NewHash(PERL_CALL_SINGLE)
{
  HV *hash = newHV();

  if (!hash)
    RaiseException(STATUS_NO_MEMORY, 0, 0, NULL);

  return hash;
}

AV *NewArray(PERL_CALL_SINGLE)
{
  AV *array = newAV();

  if (!array)
    RaiseException(STATUS_NO_MEMORY, 0, 0, NULL);

  return array;
}

SV *NewReference(PERL_CALL SV *refObj)
{
  SV *reference = NULL;

  if (!refObj || !(reference = newRV(refObj)))
    RaiseException(STATUS_NO_MEMORY, 0, 0, NULL);

  return reference;
}

///////////////////////////////////////////////////////////////////////////////
//
// sets the last error variable for the current thread
//
// param:  error	- error value to set
//
// return: last error variable of the current thread
//
///////////////////////////////////////////////////////////////////////////////

DWORD LastError(DWORD error)
{
  TlsSetValue(TlsIndex, (PVOID)error);

  return error;
}


///////////////////////////////////////////////////////////////////////////////
//
// returns the last error variable for the current thread
//
// param:
//
// return: last error variable of the current thread
//
///////////////////////////////////////////////////////////////////////////////

DWORD LastError()
{
  return (DWORD)TlsGetValue(TlsIndex);
}