// @doc
#include "win32types.h"
#include "PyWinObjects.h"

#define CHECK_PFN(fname)    \
	if (pfn##fname == NULL) \
		return PyErr_Format(PyExc_NotImplementedError, "%s is not available on this platform", #fname);
typedef HANDLE(WINAPI *CreateTransactionfunc)(LPSECURITY_ATTRIBUTES, LPGUID, DWORD, DWORD, DWORD, DWORD, LPWSTR);
static CreateTransactionfunc pfnCreateTransaction = NULL;
typedef BOOL(WINAPI *RollbackTransactionfunc)(HANDLE);
static RollbackTransactionfunc pfnRollbackTransaction = NULL;
typedef BOOL(WINAPI *RollbackTransactionAsyncfunc)(HANDLE);
static RollbackTransactionAsyncfunc pfnRollbackTransactionAsync = NULL;
typedef BOOL(WINAPI *CommitTransactionfunc)(HANDLE);
static CommitTransactionfunc pfnCommitTransaction = NULL;
typedef BOOL(WINAPI *CommitTransactionAsyncfunc)(HANDLE);
static CommitTransactionAsyncfunc pfnCommitTransactionAsync = NULL;
typedef BOOL(WINAPI *GetTransactionIdfunc)(HANDLE, LPGUID);
static GetTransactionIdfunc pfnGetTransactionId = NULL;
typedef HANDLE(WINAPI *OpenTransactionfunc)(DWORD, LPGUID);
static OpenTransactionfunc pfnOpenTransaction = NULL;

typedef BOOL(WINAPI *GetTransactionInformationfunc)(HANDLE, PDWORD, PDWORD, PDWORD, PDWORD, DWORD, LPWSTR);
static GetTransactionInformationfunc pfnGetTransactionInformation = NULL;
typedef BOOL(WINAPI *SetTransactionInformationfunc)(HANDLE, DWORD, DWORD, DWORD, LPWSTR);
static SetTransactionInformationfunc pfnSetTransactionInformation = NULL;
// static char *keywords[]={"TransactionHandle","IsolationLevel","IsolationFlags","Timeout","Description", NULL};
typedef HANDLE(WINAPI *OpenResourceManagerfunc)(DWORD, HANDLE, LPGUID);
static OpenResourceManagerfunc pfnOpenResourceManager = NULL;
// static char *keywords[]={"DesiredAccess","TmHandle","RmGuid", NULL};
typedef HANDLE(WINAPI *CreateTransactionManagerfunc)(LPSECURITY_ATTRIBUTES, LPWSTR, ULONG, ULONG);
static CreateTransactionManagerfunc pfnCreateTransactionManager = NULL;
// static char *keywords[]={"TransactionAttributes","LogFileName","CreateOptions","CommitStrength", NULL};
typedef HANDLE(WINAPI *CreateResourceManagerfunc)(LPSECURITY_ATTRIBUTES, LPGUID, DWORD, HANDLE, LPWSTR);
static CreateResourceManagerfunc pfnCreateResourceManager = NULL;
// static char *keywords[]={"ResourceManagerAttributes","ResourceManagerID","CreateOptions","TmHandle","Description", NULL};
typedef HANDLE(WINAPI *OpenEnlistmentfunc)(DWORD, HANDLE, LPGUID);
static OpenEnlistmentfunc pfnOpenEnlistment = NULL;
// static char *keywords[]={"DesiredAccess","ResourceManagerHandle","EnlistmentId", NULL};
typedef HANDLE(WINAPI *CreateEnlistmentfunc)(LPSECURITY_ATTRIBUTES, HANDLE, HANDLE, DWORD, DWORD, PVOID);
static CreateEnlistmentfunc pfnCreateEnlistment = NULL;
// static char *keywords[]={"EnlistmentrAttributes","ResourceManagerHandle","TransactionHandle","NotificationMask","CreateOptions","EnlistmentKey", NULL};
typedef HANDLE(WINAPI *OpenTransactionManagerfunc)(LPWSTR, ACCESS_MASK, ULONG);
static OpenTransactionManagerfunc pfnOpenTransactionManager = NULL;
// static char *keywords[]={"LogFileName","DesiredAccess","OpenOptions", NULL};

// @pymethod <o PyHANDLE>|win32transaction|CreateTransaction|Creates a transaction
// @pyseeapi CreateTransaction
static PyObject *PyCreateTransaction(PyObject *self, PyObject *args, PyObject *kwargs)
{
	CHECK_PFN(CreateTransaction);
	WCHAR *description = NULL;
	PyObject *obsa = Py_None, *obuow = Py_None, *obdescription = Py_None;
	DWORD createoptions = 0, isolationlevel = 0, isolationflags = 0, timeout = 0;
	PSECURITY_ATTRIBUTES psa = NULL;
	GUID *uow = NULL;
	HANDLE hret;
	static char *keywords[] = {"TransactionAttributes", "UOW", "CreateOptions", "IsolationLevel",
							   "IsolationFlags", "Timeout", "Description", NULL};

	if (!PyArg_ParseTupleAndKeywords(args, kwargs, "|OOkkkkO:CreateTransaction", keywords,
									 &obsa,			  // @pyparm <o PySECURITY_ATTRIBUTES>|TransactionAttributes|None|Security and inheritance for the transaction, can be None
									 &obuow,		  // @pyparm <o PyIID>|UOW|None|Reserved, use only None
									 &createoptions,  // @pyparm int|CreateOptions|0|TRANSACTION_DO_NOT_PROMOTE is only defined flag
									 &isolationlevel, // @pyparm int|IsolationLevel|0|Reserved, use only 0
									 &isolationflags, // @pyparm int|IsolationFlags|0|Reserved, use only 0
									 &timeout,		  // @pyparm int|Timeout|0|Abort timeout in milliseconds
									 &obdescription)) // @pyparm <o PyUnicode>|Description|None|Text description of transaction, can be None
		return NULL;
	if (!PyWinObject_AsSECURITY_ATTRIBUTES(obsa, &psa, TRUE))
		return NULL;
	if (obuow != Py_None)
	{
		PyErr_SetString(PyExc_TypeError, "UOW must be None");
		return NULL;
	}
	if (!PyWinObject_AsWCHAR(obdescription, &description, TRUE))
		return NULL;
	Py_BEGIN_ALLOW_THREADS
		hret = (*pfnCreateTransaction)(psa, uow, createoptions, isolationlevel, isolationflags, timeout, description);
	Py_END_ALLOW_THREADS
		PyWinObject_FreeWCHAR(description);
	if (hret == INVALID_HANDLE_VALUE)
		return PyWin_SetAPIError("CreateTransaction");
	return PyWinObject_FromHANDLE(hret);
}

// @pymethod |win32transaction|RollbackTransaction|Rolls back a transaction
// @pyseeapi RollbackTransaction
static PyObject *PyRollbackTransaction(PyObject *self, PyObject *args, PyObject *kwargs)
{
	CHECK_PFN(RollbackTransaction);
	PyObject *obtrans;
	HANDLE htrans;
	BOOL ret;
	static char *keywords[] = {"TransactionHandle", NULL};
	if (!PyArg_ParseTupleAndKeywords(args, kwargs, "O:RollbackTransaction", keywords,
									 &obtrans)) // @pyparm <o PyHANDLE>|TransactionHandle||Handle to a transaction
		return NULL;
	if (!PyWinObject_AsHANDLE(obtrans, &htrans))
		return NULL;
	Py_BEGIN_ALLOW_THREADS
		ret = (*pfnRollbackTransaction)(htrans);
	Py_END_ALLOW_THREADS if (!ret) return PyWin_SetAPIError("RollbackTransaction");
	Py_INCREF(Py_None);
	return Py_None;
}

// @pymethod |win32transaction|RollbackTransactionAsync|Rolls back a transaction asynchronously
// @pyseeapi RollbackTransactionAsync
static PyObject *PyRollbackTransactionAsync(PyObject *self, PyObject *args, PyObject *kwargs)
{
	CHECK_PFN(RollbackTransactionAsync);
	PyObject *obtrans;
	HANDLE htrans;
	BOOL ret;
	static char *keywords[] = {"TransactionHandle", NULL};
	if (!PyArg_ParseTupleAndKeywords(args, kwargs, "O:RollbackTransactionAsync", keywords,
									 &obtrans)) // @pyparm <o PyHANDLE>|TransactionHandle||Handle to a transaction
		return NULL;
	if (!PyWinObject_AsHANDLE(obtrans, &htrans))
		return NULL;
	Py_BEGIN_ALLOW_THREADS
		ret = (*pfnRollbackTransactionAsync)(htrans);
	Py_END_ALLOW_THREADS if (!ret) return PyWin_SetAPIError("RollbackTransactionAsync");
	Py_INCREF(Py_None);
	return Py_None;
}

// @pymethod |win32transaction|CommitTransaction|Commits a transaction
// @pyseeapi CommitTransaction
static PyObject *PyCommitTransaction(PyObject *self, PyObject *args, PyObject *kwargs)
{
	CHECK_PFN(CommitTransaction);
	PyObject *obtrans;
	HANDLE htrans;
	BOOL ret;
	static char *keywords[] = {"TransactionHandle", NULL};
	if (!PyArg_ParseTupleAndKeywords(args, kwargs, "O:CommitTransaction", keywords,
									 &obtrans)) // @pyparm <o PyHANDLE>|TransactionHandle||Handle to a transaction
		return NULL;
	if (!PyWinObject_AsHANDLE(obtrans, &htrans))
		return NULL;
	Py_BEGIN_ALLOW_THREADS
		ret = (*pfnCommitTransaction)(htrans);
	Py_END_ALLOW_THREADS if (!ret) return PyWin_SetAPIError("CommitTransaction");
	Py_INCREF(Py_None);
	return Py_None;
}

// @pymethod |win32transaction|CommitTransactionAsync|Commits a transaction asynchronously
// @pyseeapi CommitTransactionAsync
static PyObject *PyCommitTransactionAsync(PyObject *self, PyObject *args, PyObject *kwargs)
{
	CHECK_PFN(CommitTransactionAsync);
	PyObject *obtrans;
	HANDLE htrans;
	BOOL ret;
	static char *keywords[] = {"TransactionHandle", NULL};
	if (!PyArg_ParseTupleAndKeywords(args, kwargs, "O:CommitTransactionAsync", keywords,
									 &obtrans)) // @pyparm <o PyHANDLE>|TransactionHandle||Handle to a transaction
		return NULL;
	if (!PyWinObject_AsHANDLE(obtrans, &htrans))
		return NULL;
	Py_BEGIN_ALLOW_THREADS
		ret = (*pfnCommitTransactionAsync)(htrans);
	Py_END_ALLOW_THREADS if (!ret) return PyWin_SetAPIError("CommitTransactionAsync");
	Py_INCREF(Py_None);
	return Py_None;
}

// @pymethod <o PyIID>|win32transaction|GetTransactionId|Returns the transaction's GUID
static PyObject *PyGetTransactionId(PyObject *self, PyObject *args, PyObject *kwargs)
{
	CHECK_PFN(GetTransactionId);
	PyObject *obtrans;
	HANDLE htrans;
	BOOL ret;
	GUID guid;
	static char *keywords[] = {"TransactionHandle", NULL};
	if (!PyArg_ParseTupleAndKeywords(args, kwargs, "O:GetTransactionId", keywords,
									 &obtrans)) // @pyparm <o PyHANDLE>|TransactionHandle||Handle to a transaction
		return NULL;
	if (!PyWinObject_AsHANDLE(obtrans, &htrans))
		return NULL;
	Py_BEGIN_ALLOW_THREADS
		ret = (*pfnGetTransactionId)(htrans, &guid);
	Py_END_ALLOW_THREADS if (!ret) return PyWin_SetAPIError("GetTransactionId");
	return PyWinObject_FromIID(guid);
}

// @pymethod <o PyHANDLE>|win32transaction|OpenTransaction|Creates a handle to an existing transaction
static PyObject *PyOpenTransaction(PyObject *self, PyObject *args, PyObject *kwargs)
{
	CHECK_PFN(OpenTransaction);
	PyObject *obguid;
	HANDLE htrans;
	DWORD access;
	GUID guid;
	static char *keywords[] = {"DesiredAccess", "TransactionId", NULL};
	if (!PyArg_ParseTupleAndKeywords(args, kwargs, "kO:OpenTransaction", keywords,
									 &access,  // @pyparm int|DesiredAccess||Combination of TRANSACTION_* access rights
									 &obguid)) // @pyparm <o PyIID>|TransactionId||GUID identifying the transaction
		return NULL;
	if (!PyWinObject_AsIID(obguid, &guid))
		return NULL;
	Py_BEGIN_ALLOW_THREADS
		htrans = (*pfnOpenTransaction)(access, &guid);
	Py_END_ALLOW_THREADS if (htrans == INVALID_HANDLE_VALUE) return PyWin_SetAPIError("OpenTransaction");
	return PyWinObject_FromHANDLE(htrans);
}

// @module win32transaction|Module wrapping Kernal Transaction Manager functions, as used with
//	transacted NTFS and transacted registry functions.
// @comm These functions are only available on Vista and later.
// @comm All functions accept keyword arguments.
static PyMethodDef win32transaction_functions[] = {
	// @pymeth CreateTransaction|Creates a transaction
	{"CreateTransaction", (PyCFunction)PyCreateTransaction, METH_KEYWORDS | METH_VARARGS},
	// @pymeth RollbackTransaction|Rolls back a transaction
	{"RollbackTransaction", (PyCFunction)PyRollbackTransaction, METH_KEYWORDS | METH_VARARGS},
	// @pymeth RollbackTransactionAsync|Rolls back a transaction asynchronously
	{"RollbackTransactionAsync", (PyCFunction)PyRollbackTransactionAsync, METH_KEYWORDS | METH_VARARGS},
	// @pymeth CommitTransaction|Commits a transaction
	{"CommitTransaction", (PyCFunction)PyCommitTransaction, METH_KEYWORDS | METH_VARARGS},
	// @pymeth CommitTransactionAsync|Commits a transaction asynchronously
	{"CommitTransactionAsync", (PyCFunction)PyCommitTransactionAsync, METH_KEYWORDS | METH_VARARGS},
	// @pymeth GetTransactionId|Returns the transaction's GUID
	{"GetTransactionId", (PyCFunction)PyGetTransactionId, METH_KEYWORDS | METH_VARARGS},
	// @pymeth OpenTransaction|Creates a handle to an existing transaction
	{"OpenTransaction", (PyCFunction)PyOpenTransaction, METH_KEYWORDS | METH_VARARGS},
	{NULL, NULL}};

PYWIN_MODULE_INIT_FUNC(_win32transaction)
{
	PYWIN_MODULE_INIT_PREPARE(_win32transaction, win32transaction_functions,
							  "Module wrapping Kernal Transaction Manager functions, as used with"
							  " transacted NTFS and transacted registry functions.");

	// Load dll and function pointers to avoid dependency on newer libraries and headers
	HMODULE hmodule = GetModuleHandle(L"Ktmw32.dll");
	if (hmodule == NULL)
		hmodule = LoadLibrary(L"Ktmw32.dll");
	if (hmodule)
	{
		pfnCreateTransaction = (CreateTransactionfunc)GetProcAddress(hmodule, "CreateTransaction");
		pfnRollbackTransaction = (RollbackTransactionfunc)GetProcAddress(hmodule, "RollbackTransaction");
		pfnRollbackTransactionAsync = (RollbackTransactionAsyncfunc)GetProcAddress(hmodule, "RollbackTransactionAsync");
		pfnCommitTransaction = (CommitTransactionfunc)GetProcAddress(hmodule, "CommitTransaction");
		pfnCommitTransactionAsync = (CommitTransactionAsyncfunc)GetProcAddress(hmodule, "CommitTransactionAsync");
		pfnGetTransactionId = (GetTransactionIdfunc)GetProcAddress(hmodule, "GetTransactionId");
		pfnGetTransactionInformation = (GetTransactionInformationfunc)GetProcAddress(hmodule, "GetTransactionInformation");
		pfnSetTransactionInformation = (SetTransactionInformationfunc)GetProcAddress(hmodule, "SetTransactionInformation");
		pfnOpenTransaction = (OpenTransactionfunc)GetProcAddress(hmodule, "OpenTransaction");
		pfnOpenResourceManager = (OpenResourceManagerfunc)GetProcAddress(hmodule, "OpenResourceManager");
		pfnCreateTransactionManager = (CreateTransactionManagerfunc)GetProcAddress(hmodule, "CreateTransactionManager");
		pfnCreateResourceManager = (CreateResourceManagerfunc)GetProcAddress(hmodule, "CreateResourceManager");
		pfnOpenEnlistment = (OpenEnlistmentfunc)GetProcAddress(hmodule, "OpenEnlistment");
		pfnCreateEnlistment = (CreateEnlistmentfunc)GetProcAddress(hmodule, "CreateEnlistment");
		pfnOpenTransactionManager = (OpenTransactionManagerfunc)GetProcAddress(hmodule, "OpenTransactionManager");
	}

	Py_INCREF(PyWinExc_ApiError);
	PyDict_SetItemString(dict, "error", PyWinExc_ApiError);

	PYWIN_MODULE_INIT_RETURN_SUCCESS;
}
