#ifndef GETFRIENDCSP_H
#define GETFRIENDCSP_H

#include "compiler_attributes.h"
#include "reader/support.h"
//"достаточно" большой размер буфера - подойдет для путей в конфиге, параметров и тд.
#define GET_FRIEND_CSP_BUFFER 260

typedef struct TProviderDescrition_{
    TCHAR provider_name[GET_FRIEND_CSP_BUFFER +1];
    DWORD provider_type;
} TProviderDescription;

static SUP_INLINE DWORD get_csp_type(const TCHAR* csp, DWORD* csptype) ATTR_USERES;
static SUP_INLINE DWORD get_csp_string_param(const TCHAR* csp, const TCHAR* param, size_t* len, TCHAR* buf) ATTR_USERES;
static SUP_INLINE DWORD get_csp_media_type(const TCHAR* csp, size_t* len, TCHAR* buf) ATTR_USERES;
static SUP_INLINE DWORD get_csp_module(const TCHAR* csp, size_t* len, TCHAR* buf) ATTR_USERES;
static SUP_INLINE DWORD find_friend_providers_ex(const TCHAR* module_name, const TCHAR* media_type, TProviderDescription* items, size_t *items_size) ATTR_USERES;
static SUP_INLINE DWORD find_friend_providers(const TCHAR* csp_name, size_t * items_size, TProviderDescription* items) ATTR_USERES;

static SUP_INLINE ATTR_USERES DWORD find_friend_providers(const TCHAR* csp_name,  size_t * items_size, TProviderDescription* items) {
    TCHAR module_name[GET_FRIEND_CSP_BUFFER + 1];
    size_t module_name_len = GET_FRIEND_CSP_BUFFER;
    TCHAR media_type_buffer[GET_FRIEND_CSP_BUFFER + 1];
    size_t media_type_buffer_len = GET_FRIEND_CSP_BUFFER;
    TCHAR* media_type = NULL;
    DWORD code = get_csp_module(csp_name, &module_name_len, module_name);
    if (code)
	return code;
    code = get_csp_media_type(csp_name, &media_type_buffer_len, media_type_buffer);
    if (code && (code != (DWORD)ERROR_FILE_NOT_FOUND))
	return code;
    if (code == ERROR_SUCCESS)
	media_type = media_type_buffer;
    return find_friend_providers_ex(module_name, media_type, items, items_size);
}

static SUP_INLINE ATTR_USERES DWORD get_csp_type(const TCHAR* csp, DWORD* csptype) {
    const TCHAR* defaults_path = TEXT("\\CRYPTOGRAPHY\\Defaults\\Provider\\");
    const TCHAR* prov_type_param = TEXT("\\Type");
    long type = 0;
    TCHAR param_full_path[GET_FRIEND_CSP_BUFFER +1];
    DWORD code = 0;
    _tcscpy(param_full_path, defaults_path);
    _tcscat(param_full_path, csp);
    _tcscat(param_full_path, prov_type_param);
    code = support_registry_get_long(param_full_path, &type);
    if (code)
	return code;
    *csptype = (DWORD)type;
    return ERROR_SUCCESS;
}

static SUP_INLINE ATTR_USERES DWORD get_csp_string_param(const TCHAR* csp, const TCHAR* param, size_t* len, TCHAR* buf) {
    TCHAR param_full_path[GET_FRIEND_CSP_BUFFER +1];
    const TCHAR* defaults_path = TEXT("\\CRYPTOGRAPHY\\Defaults\\Provider\\");
    if (_tcslen(csp) + _tcslen(param) + _tcslen(defaults_path) > GET_FRIEND_CSP_BUFFER)
	return (DWORD)NTE_BAD_DATA;
    _tcscpy(param_full_path, defaults_path);
    _tcscat(param_full_path, csp);
    _tcscat(param_full_path, param);
    return support_registry_get_string(param_full_path, len, buf);
}

static SUP_INLINE ATTR_USERES DWORD get_csp_media_type(const TCHAR* csp, size_t* len, TCHAR* buf) {
    return get_csp_string_param(csp, TEXT("\\Media"), len, buf);
}

static SUP_INLINE ATTR_USERES DWORD get_csp_module(const TCHAR* csp, size_t* len, TCHAR* buf) {
    return get_csp_string_param(csp, TEXT("\\Image Path"), len, buf);
}

static SUP_INLINE ATTR_USERES DWORD find_friend_providers_ex(const TCHAR* module_name, const TCHAR* media_type, TProviderDescription* items, size_t *items_size) {
    const TCHAR* defaults_path = TEXT("\\CRYPTOGRAPHY\\Defaults\\Provider\\");
    size_t real_items_amount = 0;
    TCHAR cur_prov_name[GET_FRIEND_CSP_BUFFER + 1];
    TCHAR cur_module_name[GET_FRIEND_CSP_BUFFER + 1];
    TCHAR cur_media_type[GET_FRIEND_CSP_BUFFER + 1];
    size_t media_type_len = GET_FRIEND_CSP_BUFFER;
    DWORD code = 0;
    TSupportRegistrySearchContext* context = NULL;
    size_t cur_module_name_len = GET_FRIEND_CSP_BUFFER;
    if (!items_size || !module_name || (*items_size != 0 && items == NULL))
	return (DWORD)ERROR_INVALID_PARAMETER;
    //NULL вместо &max_len, т.к. на UNIX возвращается MAX_PATH (4096), что избыточно. 
    //далее мы везде используем буфер размера GET_FRIEND_CSP_BUFFER(260) и явно передаем его длину, ее хватает для обычных кейсов.
    code = support_registry_search_open(defaults_path, &context, NULL, 1);
    if (code)
	return code;
    for (;;) {
	code = support_registry_get_param(context, GET_FRIEND_CSP_BUFFER, (TCHAR*)cur_prov_name);
	if (code && code != ERROR_FILE_NOT_FOUND)
	    goto done;
	if (code == ERROR_FILE_NOT_FOUND) {
	    code = ERROR_SUCCESS;
	    goto done;
	}
	cur_module_name_len = GET_FRIEND_CSP_BUFFER;
	code = get_csp_module(cur_prov_name, &cur_module_name_len, cur_module_name);
	if (code && code != ERROR_FILE_NOT_FOUND)
	    goto done;
	if (code == ERROR_FILE_NOT_FOUND)
	    continue;
	if (_tcscmp(cur_module_name, module_name) == 0) {
	    if (media_type) {//if we don't have media_type as criteria, skip this check.
		media_type_len = GET_FRIEND_CSP_BUFFER;
		code = get_csp_media_type(cur_prov_name, &media_type_len, cur_media_type);
		if (code && code != ERROR_FILE_NOT_FOUND)
		    goto done;
		else if (code == ERROR_FILE_NOT_FOUND) //if provider doesn't have media type, or it's media type is different, we skip it. 
		    continue;
		if (_tcscmp(media_type, cur_media_type) != 0)
		    continue;
	    }
	    real_items_amount++;
	    if (*items_size == 0) {
		continue;
	    }
	    if (real_items_amount * sizeof(TProviderDescription) > *items_size) {
		code = (DWORD)ERROR_MORE_DATA;
		goto done;
	    }
	    if (items) {
		_tcscpy(items[real_items_amount - 1].provider_name, cur_prov_name);
		code = get_csp_type(cur_prov_name, &items[real_items_amount - 1].provider_type);
		if (code)
		    goto done;
	    }
	}
    }
done:
    support_registry_search_close(context);
    *items_size = real_items_amount * sizeof(TProviderDescription);
    return code;
}

#endif //GETFRIENDCSP_H
