#include <common/regkey.hpp>
#include <common/string.hpp>

RegKey::RegKey(void)
: mhKey((HKEY)CurrentUser), mIsOpenKey(FALSE), mDisposition(InvalidKey)
{
}

RegKey::RegKey(Key regKey)
: mhKey((HKEY)regKey), mIsOpenKey(FALSE), mDisposition(InvalidKey)
{
}

RegKey::RegKey(const RegKey &someRegKey)
: mhKey((HKEY)CurrentUser), mIsOpenKey(FALSE), mDisposition(InvalidKey)
{
	*this=someRegKey;
}

RegKey::~RegKey()
{
	closeKey();
}

RegKey &RegKey::operator=(const RegKey &someRegKey)
{
	closeKey();
	mhKey=someRegKey.mhKey;
	mDisposition=KeepKey;
	isOpenKey(someRegKey.isOpenKey());
  return *this;
}

WORD RegKey::operator==(const RegKey &someRegKey)
{
	return mhKey==someRegKey.mhKey;
}

RegKey::operator HKEY(void)const
{
	return mhKey;
}

WORD RegKey::createKey(const RegKey &openKey,const String &subKey,const String &className,const RegSam &regSam)
{
	DWORD resData(0L);
	DWORD keyDisposition;
	long keyReturn;

	if(subKey.isNull())return FALSE;
	if(*this==openKey&&isOpenKey())return createDescendKey(subKey,className,regSam);
	closeKey();
	keyReturn=::RegCreateKeyEx((HKEY)*this,(LPSTR)subKey,resData,className.isNull()?(LPSTR)0:(LPSTR)className,REG_OPTION_NON_VOLATILE,regSam,(LPSECURITY_ATTRIBUTES)0,&mhKey,&keyDisposition);
	if(ERROR_SUCCESS!=keyReturn)return FALSE;
	if(REG_OPENED_EXISTING_KEY==keyDisposition)closeKey();
	return TRUE;
}

WORD RegKey::createKey(const String &subKey,const String &className,const RegSam &regSam)
{
	DWORD resData(0L);
	DWORD keyDisposition;
	long keyReturn;
	HKEY hKey;

	keyReturn=::RegCreateKeyEx((HKEY)*this,(LPSTR)subKey,resData,(LPSTR)className,REG_OPTION_NON_VOLATILE,regSam,(LPSECURITY_ATTRIBUTES)0,&hKey,&keyDisposition);
	if(ERROR_SUCCESS!=keyReturn)return FALSE;
	if(REG_OPENED_EXISTING_KEY==keyDisposition)::RegCloseKey(hKey);
	return TRUE;
}

WORD RegKey::createKey(Key regKey,const String &subKey,const RegSam &regSam)
{
  closeKey();
	mhKey=(HKEY)regKey;
	return createKey(*this,subKey,String(""),regSam);
}

WORD RegKey::openKey(Key regKey,const String &subKey,const RegSam &regSam)
{
  closeKey();
	mhKey=(HKEY)regKey;
	return openKey(subKey,regSam);
}

WORD RegKey::openKey(const RegKey &openKey,const String &subKey,const RegSam &regSam)
{
	long keyResult;

	if(*this==openKey&&isOpenKey())return descendKey(openKey,subKey,regSam);
	closeKey();
	keyResult=::RegOpenKeyEx((HKEY)openKey,subKey,0L,regSam,&mhKey);
	return ERROR_SUCCESS==keyResult;
}

WORD RegKey::openKey(const String &subKey,const RegSam &regSam)
{
	long keyResult;
	HKEY hKey;

	closeKey();
	keyResult=::RegOpenKeyEx((HKEY)*this,subKey,0L,(DWORD)regSam,&hKey);
	if(ERROR_SUCCESS==keyResult){mIsOpenKey=TRUE;mhKey=hKey;mDisposition=CloseKey;}
	return ERROR_SUCCESS==keyResult;
}

void RegKey::closeKey(void)
{
	if(!isOpenKey()||KeepKey==mDisposition)return;
	::RegCloseKey(mhKey);
	isOpenKey(FALSE);
	mDisposition=InvalidKey;
}

WORD RegKey::connectRegistry(const String &computerName,const RegKey &regKey)
{
	long keyResult;
	closeKey();

	if((HKEY)regKey!=(HKEY)LocalMachine&&(HKEY)regKey!=(HKEY)Users)return FALSE;
	keyResult=::RegConnectRegistry((LPSTR)computerName,regKey,&mhKey);
	if(ERROR_SUCCESS!=keyResult)return FALSE;
	mDisposition=CloseKey;
	mIsOpenKey=TRUE;
  return TRUE;
}

WORD RegKey::deleteKey(const String &subKey)
{
	long keyResult;

	if(!isOkay()||subKey.isNull())return FALSE;
	keyResult=::RegDeleteKey((HKEY)*this,(LPSTR)subKey);
	if(ERROR_SUCCESS!=keyResult)return FALSE;
	return TRUE;
}

WORD RegKey::deleteValue(const String &namedValue)
{
  long keyResult;

	if(!isOkay()||namedValue.isNull())return FALSE;
	keyResult=::RegDeleteValue((HKEY)*this,(LPSTR)namedValue);
	if(ERROR_SUCCESS!=keyResult)return FALSE;
	return TRUE;
}

WORD RegKey::descendKey(const RegKey &openKey,const String &subKey,const RegSam &regSam)
{
	HKEY hKey;
	long keyResult;

	if(subKey.isNull())return FALSE;
	keyResult=::RegOpenKeyEx((HKEY)openKey,subKey,0L,(DWORD)regSam,&hKey);
	closeKey();
	if(ERROR_SUCCESS!=keyResult)return FALSE;
	mhKey=hKey;
	mDisposition=CloseKey;
	mIsOpenKey=TRUE;
	return TRUE;
}

WORD RegKey::createDescendKey(const String &subKey,const String &className,const RegSam &regSam)
{
	HKEY hKey;
	long keyResult;
	DWORD resData(0L);
	DWORD keyDisposition;

	if(subKey.isNull())return FALSE;
	keyResult=::RegCreateKeyEx((HKEY)*this,(LPSTR)subKey,resData,className.isNull()?(LPSTR)0:(LPSTR)className,REG_OPTION_NON_VOLATILE,regSam,(LPSECURITY_ATTRIBUTES)0,&hKey,&keyDisposition);
	if(ERROR_SUCCESS!=keyResult)return FALSE;
  mhKey=hKey;
	if(REG_OPENED_EXISTING_KEY==keyDisposition)closeKey();
	return TRUE;
}

WORD RegKey::queryValue(const String &valueName,String &stringData)const
{
	BYTE valueData[MaxDataLength];
	DWORD sizeData(sizeof(valueData));
	DWORD valueType(REG_SZ);
	long keyReturn;

	if(!isOkay())return FALSE;
	::memset(valueData,0,sizeof(valueData));
	keyReturn=::RegQueryValueEx((HKEY)*this,(LPSTR)valueName,0,&valueType,valueData,&sizeData);
	if(ERROR_SUCCESS!=keyReturn||REG_SZ!=valueType)return FALSE;
	stringData=(char*)valueData;
	return TRUE;
}

WORD RegKey::queryValue(const String &valueName,DWORD &longData)const
{
	BYTE valueData[MaxDataLength];
	DWORD sizeData(sizeof(valueData));
	DWORD valueType(REG_DWORD);
	long keyReturn;

  if(!isOkay()||valueName.isNull())return FALSE;
	::memset(valueData,0,sizeof(valueData));
	keyReturn=::RegQueryValueEx((HKEY)*this,(LPSTR)valueName,0,&valueType,valueData,&sizeData);
	if(ERROR_SUCCESS!=keyReturn||REG_DWORD!=valueType)return FALSE;
	longData=*((DWORD*)valueData);
  return TRUE;
}

WORD RegKey::setValue(const String &valueName,const String &stringData)
{
	DWORD resData(0);
  long keyReturn;

  if(!isOkay()||valueName.isNull())return FALSE;
	if(stringData.isNull())keyReturn=::RegSetValueEx((HKEY)*this,valueName.isNull()?0:(LPSTR)valueName,resData,REG_SZ,0,0);
	else keyReturn=::RegSetValueEx((HKEY)*this,valueName.isNull()?0:(LPSTR)valueName,resData,REG_SZ,(const unsigned char*)(LPSTR)stringData,stringData.length()+1);
	return ERROR_SUCCESS==keyReturn;
}

WORD RegKey::setValue(const String &valueName,DWORD longData)
{
	DWORD resData(0);
	long keyReturn;

	if(!isOkay()||valueName.isNull())return FALSE;
	keyReturn=::RegSetValueEx((HKEY)*this,(LPSTR)valueName,resData,REG_DWORD,(const unsigned char*)(LPSTR)&longData,sizeof(DWORD));
	return ERROR_SUCCESS==keyReturn;
}

WORD RegKey::enumValue(DWORD index,String &nameValue,String &value)
{
  LONG keyResult;
	DWORD nameLength;
	DWORD valueLength;
	DWORD typeCode(REG_SZ);

  if(!isOkay())return FALSE;
	nameValue.reserve(MaxDataLength);
	value.reserve(MaxDataLength);
	nameLength=MaxDataLength;
	valueLength=MaxDataLength;
	keyResult=::RegEnumValue((HKEY)*this,index,(LPSTR)nameValue,&nameLength,0,&typeCode,(LPBYTE)(LPSTR)value,&valueLength);
	return (ERROR_SUCCESS==keyResult);
}

WORD RegKey::enumValue(DWORD index,String &nameValue,DWORD &longData)
{
  LONG keyResult;
	DWORD nameLength;
	DWORD valueLength;
	DWORD typeCode(REG_DWORD);

  if(!isOkay())return FALSE;
	nameValue.reserve(MaxDataLength);
  nameLength=MaxDataLength;
	valueLength=sizeof(DWORD);
	keyResult=::RegEnumValue((HKEY)*this,index,(LPSTR)nameValue,&nameLength,0,&typeCode,(LPBYTE)&longData,&valueLength);
	return (ERROR_SUCCESS==keyResult);
}

WORD RegKey::enumKey(DWORD index,String &strKey)
{
  LONG keyResult;

	if(!isOkay())return FALSE;
	strKey.reserve(MaxDataLength);
	keyResult=::RegEnumKey((HKEY)*this,index,strKey,MaxDataLength);
	return (ERROR_SUCCESS==keyResult);
}

WORD RegKey::isOpenKey(void)const
{
	return mIsOpenKey;
}

void RegKey::isOpenKey(WORD isOpenKey)
{
	mIsOpenKey=isOpenKey;
}

WORD RegKey::isOkay(void)const
{
	if(!isOpenKey()&&isPredefinedKey())return TRUE;
	return isOpenKey();
}

WORD RegKey::isPredefinedKey(void)const
{
	if(mhKey==(HKEY)ClassesRoot||mhKey==(HKEY)CurrentUser||mhKey==(HKEY)LocalMachine||mhKey==(HKEY)Users)return TRUE;
	return FALSE;
}
