/*
  Name:             registry
  Copyright:        Copyright (C) SIL International.
  Documentation:
  Description:
  Create Date:      13 Jul 2007

  Modified Date:    20 Nov 2012
  Authors:          mcdurdin
  Related Files:
  Dependencies:

  Bugs:
  Todo:
  Notes:
  History:          13 Jul 2007 - mcdurdin - I934 - Prep for x64
                    30 Nov 2009 - mcdurdin - I934 - Prep for x64 - registry keys
                    11 Dec 2009 - mcdurdin - I934 - x64 - Initial version
                    12 Mar 2010 - mcdurdin - I934 - x64 - Complete
                    12 Mar 2010 - mcdurdin - I2229 - Remove hints and warnings
                    25 May 2010 - mcdurdin - I1632 - Keyboard Options - add support for wide registry strings
                    20 Nov 2012 - mcdurdin - I3584 - V9.0 - IntRecursiveDeleteKey needed a buf len reset after each iteration
*/

#include "pch.h"
#include "../include/registry.h"

RegistryReadOnly::RegistryReadOnly(HKEY AhRootKey)
{
	FhRootKey = AhRootKey;
	FhKey = NULL;
}

RegistryReadOnly::~RegistryReadOnly()
{
	if(FhKey != NULL) CloseKey();
}

BOOL RegistryReadOnly::CloseKey(void)
{
	BOOL Result = FALSE;
	if(FhKey != NULL) Result = WrapError(RegCloseKey(FhKey));
	FhKey = NULL;
	return TRUE;
}


BOOL RegistryFullAccess::CreateKey(LPCSTR AKey)
{
	DWORD dwDisposition;
	return WrapError(RegCreateKeyEx(FhKey ? FhKey : FhRootKey, AKey, 0, NULL, 0, KEY_ALL_ACCESS, NULL,
		&FhKey, &dwDisposition));
}

BOOL RegistryFullAccess::RecursiveDeleteKey(LPCSTR AKey)
{
	HKEY hkey;

	if(RegOpenKeyEx(FhKey ? FhKey : FhRootKey, AKey, 0, KEY_ALL_ACCESS, &hkey) != ERROR_SUCCESS)
		return FALSE;

	if(!IntRecursiveDeleteKey(hkey))
	{
		RegCloseKey(hkey);
		return FALSE;
	}

	if(RegCloseKey(hkey) != ERROR_SUCCESS) return FALSE;
	return WrapError(RegDeleteKey(hkey, AKey));
}

BOOL RegistryFullAccess::IntRecursiveDeleteKey(HKEY hkey)
{
	char buf[260];
	DWORD dwlen = 260;
	FILETIME ftime;
	HKEY hsubkey;
	LONG n = RegEnumKeyEx(hkey, 0, buf, &dwlen, NULL, NULL, NULL, &ftime);
	while(n == ERROR_MORE_DATA || n == ERROR_SUCCESS)
	{
		if(RegOpenKeyEx(hkey, buf, 0, KEY_ALL_ACCESS, &hsubkey) != ERROR_SUCCESS) return FALSE;
		IntRecursiveDeleteKey(hsubkey);
		if(RegCloseKey(hsubkey) != ERROR_SUCCESS) return FALSE;
		if(RegDeleteKey(hkey, buf) != ERROR_SUCCESS) return FALSE;
    dwlen = 260;   // I3584
		n = RegEnumKeyEx(hkey, 0, buf, &dwlen, NULL, NULL, NULL, &ftime);
	}
	return TRUE;
}

BOOL RegistryFullAccess::DeleteKey(LPCSTR AKey)
{
	return WrapError(RegDeleteKey(FhKey, AKey));
}

BOOL RegistryFullAccess::DeleteValue(LPCSTR AName)
{
	return WrapError(RegDeleteValue(FhKey, AName));
}

HKEY RegistryFullAccess::GetKey(LPCSTR AKey)
{
	HKEY Result;
	if(RegOpenKeyEx(FhKey ? FhKey : FhRootKey, AKey, 0, KEY_ALL_ACCESS, &Result) != ERROR_SUCCESS)
		return 0;
	return Result;
}

HKEY RegistryReadOnly::GetKeyReadOnly(LPCSTR AKey)
{
	HKEY Result;
	if(RegOpenKeyEx(FhKey ? FhKey : FhRootKey, AKey, 0, KEY_READ, &Result) != ERROR_SUCCESS)
		return 0;
	return Result;
}


BOOL RegistryReadOnly::GetKeyNames(LPSTR AKey, int len, int n)
{
	DWORD dwlen = len;
	FILETIME ftime;
	return WrapError(RegEnumKeyEx(FhKey, n, AKey, &dwlen, NULL, NULL, NULL, &ftime));
}

BOOL RegistryReadOnly::GetValueNames(LPSTR AName, int len, int n)
{
	DWORD dwlen = len;
	return WrapError(RegEnumValue(FhKey, n, AName, &dwlen, NULL, NULL, NULL, NULL));
}

BOOL RegistryReadOnly::GetValueNames(LPWSTR AName, int len, int n)
{
	DWORD dwlen = len;
	return WrapError(RegEnumValueW(FhKey, n, AName, &dwlen, NULL, NULL, NULL, NULL));
}

BOOL RegistryReadOnly::KeyExists(LPCSTR AKey)
{
	HKEY hkey = GetKeyReadOnly(AKey);
	if(hkey != 0) RegCloseKey(hkey);
	return hkey != 0;
}

BOOL RegistryFullAccess::OpenKey(LPCSTR AKey, BOOL ACreate)
{
	if(ACreate)
		if(!KeyExists(AKey))
			return CreateKey(AKey);
		else
			return OpenKey(AKey, FALSE);
	else
	{
		HKEY hkey = GetKey(AKey);
		if(!hkey) return FALSE;
		if(FhKey) CloseKey();
		FhKey = hkey;
		return TRUE;
	}
}


BOOL RegistryReadOnly::OpenKeyReadOnly(LPCSTR AKey)
{
	HKEY hkey = GetKeyReadOnly(AKey);
	if(!hkey) return FALSE;
	if(FhKey) CloseKey();
	FhKey = hkey;
	return TRUE;
}

int RegistryReadOnly::ReadInteger(LPCSTR AName)
{
	int buf = 0;
	DWORD dwlen = sizeof(long);
	if(RegQueryValueEx(FhKey, AName, NULL, NULL, (LPBYTE) &buf, &dwlen) == ERROR_SUCCESS)
		return buf;
	return 0;
	//return WrapError();
}

BOOL RegistryReadOnly::ReadString(LPCSTR AName, LPSTR AValue, int len)
{
	DWORD dwlen = len;
	return WrapError(RegQueryValueEx(FhKey, AName, NULL, NULL, (LPBYTE) AValue, &dwlen));
}

BOOL RegistryReadOnly::ReadString(LPCWSTR AName, LPWSTR AValue, int len)
{
	DWORD dwlen = len * 2; // byte size
	return WrapError(RegQueryValueExW(FhKey, AName, NULL, NULL, (LPBYTE) AValue, &dwlen));
}

BOOL RegistryReadOnly::ValueExists(LPCSTR AName)
{
	DWORD dwType;
	return WrapError(RegQueryValueEx(FhKey,  AName, NULL, &dwType, NULL, NULL));
}

BOOL RegistryReadOnly::ValueExists(LPCWSTR AName)
{
	DWORD dwType;
	return WrapError(RegQueryValueExW(FhKey,  AName, NULL, &dwType, NULL, NULL));
}

BOOL RegistryReadOnly::WrapError(DWORD res)
{
	/*if(res != ERROR_SUCCESS)
	{
		__logerror(0,KDS_PROGRAM,0,"RegistryFullAccess::WrapError = %d", res);
	}*/
	return res == ERROR_SUCCESS;
}

BOOL RegistryFullAccess::WriteInteger(LPCSTR AName, const int AValue)
{
	return WrapError(RegSetValueEx(FhKey, AName, 0, REG_DWORD, (LPBYTE) &AValue, sizeof(int)));
}

BOOL RegistryFullAccess::WriteString(LPCSTR AName, LPCSTR AValue) {
	return WrapError(RegSetValueEx(FhKey, AName, 0, REG_SZ, (LPBYTE) AValue, (DWORD) strlen(AValue)+1));
}

BOOL RegistryFullAccess::WriteString(LPCWSTR AName, LPCWSTR AValue) {
	return WrapError(RegSetValueExW(FhKey, AName, 0, REG_SZ, (LPBYTE) AValue, (DWORD) wcslen(AValue)*2+2));
}

RegistryReadOnly *Reg_GetKeymanInstalledKeyboard(LPSTR kbname)
{
	RegistryReadOnly *reg = new RegistryReadOnly(HKEY_LOCAL_MACHINE);
	if(!reg->OpenKeyReadOnly(REGSZ_KeymanInstalledKeyboardsLM) || !reg->OpenKeyReadOnly(kbname))
	{
		delete reg;
		reg = new RegistryReadOnly(HKEY_CURRENT_USER);
		if(!reg->OpenKeyReadOnly(REGSZ_KeymanInstalledKeyboardsCU) || !reg->OpenKeyReadOnly(kbname))
		{
			delete reg;
			return NULL;
		}
	}
	return reg;
}

RegistryReadOnly *Reg_GetKeymanActiveKeyboard(LPSTR kbname)
{
	RegistryReadOnly *reg = new RegistryReadOnly(HKEY_CURRENT_USER);
	if(!reg->OpenKeyReadOnly(REGSZ_KeymanActiveKeyboards) || !reg->OpenKeyReadOnly(kbname))
	{
		delete reg;
		return NULL;
	}
	return reg;
}
