#include "oyagame.h"

COMDATA::COMDATA(IDispatch* obj){
	ZeroMemory(this,sizeof(*this));
	pComClass=obj;
	ObjectList()->RegisterObject(obj);
}
COMDATA::~COMDATA(){
	if (StructureAddress && !ImportedStructure) delete StructureAddress;
	ObjectList()->UnregisterObject(pComClass);
}

// Geteral
IDispatch* COMDATA::pIDispatch(){
	return pComClass;
}

int Redim(void** *buffer,int *num){
	// Array is re-dedifined every 64 times (256 bytes).
	// usage: buff[Redim((void***)&buff,&num)]=obj; (IDispatch** buff; etc)
	int i=*num;
	(*num)++;
	if ((i & 63) == 0) {
		int newSize=i+64;
		void** bufferOrg=*buffer;
		void** bufferNew=new void*[newSize];
		for(int j=0;j<i;j++) bufferNew[j]=bufferOrg[j];
		*buffer=bufferNew;
		delete bufferOrg;
	}
	return i; // Returns current available record number.
}
void Discard(void** buffer,int num,int *total){
	// Discard a member from array.
	// The "num"th member is discarded, and the "total" is dicremented.
	// Don't forget to use delete statement before calling this function.
	// The delete statement cannot be moved here because the components
	// of array may not be pointers.
	// Usage: Discard((void**)buff,num,&total); (IDispatch** buff; etc)
	for (int i=num;i<(*total)-1;i++) buffer[i]=buffer[i+1];
	buffer[--(*total)]=NULL;
}

// String mainenance
// Note that the copied strings by using COMDATA object will be
// mainteined until the object will be deleted.
LPWSTR COMDATA::CopySTR(LPWSTR str){
	return strdata.TempUNI(str);
}

void COMDATA::DeleteSTR(LPWSTR str){
	strdata.Discard((void*)str);
}

LPWSTR COMDATA::CopySTR(VARIANTARG* rgvarg){
	return CopySTR(ResolveSTR(rgvarg));
}
LPWSTR ResolveSTR(VARIANTARG* rgvarg){
	if ((rgvarg->vt & VT_BYREF) && (rgvarg->vt & VT_VARIANT)) rgvarg=rgvarg->pvarVal;
	switch(rgvarg->vt){
		case VT_BSTR:
			return rgvarg->bstrVal;
		case VT_BSTR | VT_BYREF:
			return *rgvarg->pbstrVal;
		case VT_EMPTY:
			throw CERTSRV_E_PROPERTY_EMPTY;
		default:
			return NULL;
	}
}

// Temporary memory space constructor
// to be used for both ANSI and UNICODE strings
// The connection of two strings is also possible here.
TempMemory::TempMemory(){
	num=0;
	buff=NULL;
}
TempMemory::~TempMemory(){
	for(int i=0;i<num;i++) delete buff[i];
	delete buff;
}
void TempMemory::Discard(void* data){
	for (int i=0;i<num;i++) {
		if (buff[i]!=data) continue;
		delete data;
		::Discard((void**)buff,i,&num);
		return;
	}
}
int* TempMemory::TempIntArray(int arraylen){
	return (int*)(buff[Redim((void***)&buff,&num)]=new int[arraylen]);
}
LPSTR TempMemory::TempANSI(int strlen){
	return (LPSTR)(buff[Redim((void***)&buff,&num)]=new char[strlen]);
}
LPWSTR TempMemory::TempUNI(int strlen){
	return (LPWSTR)(buff[Redim((void***)&buff,&num)]=new WCHAR[strlen]);
}
LPSTR TempMemory::TempANSI(LPSTR str){
	if (!str) return NULL;
	int l=strlen(str);
	LPSTR ret=TempANSI(l+1);
	memcpy(ret,str,l+1);
	return ret;
}
LPWSTR TempMemory::TempUNI(LPWSTR str){
	if (!str) return NULL;
	int l=wcslen(str);
	LPWSTR ret=TempUNI(l+1);
	wmemcpy(ret,str,l+1);
	return ret;
}
LPWSTR TempMemory::TempUNI(LPSTR str){
	if (!str) return NULL;
	int l=strlen(str);
	LPWSTR ret=TempUNI(l+1);
	if (mbstowcs_s(NULL,ret,l+1,str,l+1)) throw NTE_NO_MEMORY;
	return ret;
}
LPSTR TempMemory::TempANSI(LPWSTR str){
	if (!str) return NULL;
	int l=wcslen(str);
	LPSTR ret=TempANSI(l+1);
	if (wcsrtombs_s(NULL,ret,l+1,(const wchar_t **)&str,l+1,NULL)) throw NTE_NO_MEMORY;
	return ret;
}
LPSTR TempMemory::TempANSI(LPSTR str1, LPSTR str2){
	if (!str1) return TempANSI(str2);
	if (!str2) return TempANSI(str1);
	int l1=strlen(str1);
	int l2=strlen(str2);
	LPSTR ret=TempANSI(l1+l2+1);
	memcpy(ret,str1,l1);
	memcpy(ret+l1,str2,l2+1);
	return ret;
}
LPWSTR TempMemory::TempUNI(LPWSTR str1, LPWSTR str2){
	if (!str1) return TempUNI(str2);
	if (!str2) return TempUNI(str1);
	int l1=wcslen(str1);
	int l2=wcslen(str2);
	LPWSTR ret=TempUNI(l1+l2+1);
	wmemcpy(ret,str1,l1);
	wmemcpy(ret+l1,str2,l2+1);
	return ret;
}
LPSTR TempMemory::TempANSI(VARIANTARG* rgvarg){
	LPWSTR str=ResolveSTR(rgvarg);
	if (!str) return NULL;
	return TempANSI(str);
}

VARIANTARG* TempMemory::TempRgvarg(){
	VARIANTARG* ret=(VARIANTARG*)(buff[Redim((void***)&buff,&num)]=new VARIANTARG);
	ZeroMemory(ret,sizeof(VARIANTARG));
	return ret;
}
VARIANTARG* TempMemory::TempRgvarg(VARIANTARG* rgvarg){
	VARIANTARG* ret=TempRgvarg();
	memcpy(ret,rgvarg,sizeof(VARIANTARG));
	return ret;
}


// Numeric maintenance
int CopyINT(VARIANTARG* rgvarg){
	if ((rgvarg->vt & VT_BYREF) && (rgvarg->vt & VT_VARIANT)) rgvarg=rgvarg->pvarVal;
	switch(rgvarg->vt){
		case VT_I1:
		case VT_UI1:
			return (int)rgvarg->bVal;
		case VT_I2:
			return (int)rgvarg->iVal;
		case VT_I4:
			return (int)rgvarg->lVal;
		case VT_R4:
			return (int)rgvarg->fltVal;
		case VT_R8:
			return (int)rgvarg->dblVal;
		case VT_BOOL:
			return (int)rgvarg->boolVal;
		case VT_DISPATCH:
			return (int)GetDataFromComClass(rgvarg->pdispVal)->pStructure();
		case VT_EMPTY:
			throw CERTSRV_E_PROPERTY_EMPTY;
		default:
			return 0;
	}
}

double CopyDBL(VARIANTARG* rgvarg){
	if ((rgvarg->vt & VT_BYREF) && (rgvarg->vt & VT_VARIANT)) rgvarg=rgvarg->pvarVal;
	switch(rgvarg->vt){
		case VT_I1:
		case VT_UI1:
			return (double)rgvarg->bVal;
		case VT_I2:
			return (double)rgvarg->iVal;
		case VT_I4:
			return (double)rgvarg->lVal;
		case VT_R4:
			return (double)rgvarg->fltVal;
		case VT_R8:
			return (double)rgvarg->dblVal;
		case VT_BOOL:
			return (double)rgvarg->boolVal;
		case VT_DISPATCH:
			return (double)(int)GetDataFromComClass(rgvarg->pdispVal)->pStructure();
		case VT_EMPTY:
			throw CERTSRV_E_PROPERTY_EMPTY;
		default:
			return 0;
	}
}
