#ifndef SCARD_REG_H
#define SCARD_REG_H

#include "reader/support.h"

#define INVALID_ATR ((size_t)-1)
#define INVALID_CHAR ((unsigned char)-1)

static SUP_INLINE unsigned char c2d(TCHAR c)
{
    if (c >= '0' && c <= '9') return (unsigned char)(c - '0');
    if (c >= 'A' && c <= 'F') return (unsigned char)(c - 'A' + 10);
    if (c >= 'a' && c <= 'f') return (unsigned char)(c - 'a' + 10);
    return INVALID_CHAR;
}

static SUP_INLINE size_t str2atr(const TCHAR *str, unsigned char *atr)
{
    size_t length = 0;
    for (; *str; str += 2, atr++, length++)
    {
	unsigned char a, b;
	if (!str[1]) return INVALID_ATR;
	a = c2d(*str);
	b = c2d(str[1]);
	if (a == INVALID_CHAR || b == INVALID_CHAR)
	    return INVALID_ATR;
	*atr = (a << 4) | b;
    }
    return length;
}

static SUP_INLINE TCHAR d2c(unsigned char d)
{
    if (d >= 10) return d - 10 + 'A';
    return d + '0';
}

static  SUP_INLINE void atr2str(const unsigned char *atr, size_t length,
    TCHAR *str)
{
    for (; length; length--, str += 2, atr++)
    {
	*str = d2c(*atr >> 4);
	str[1] = d2c(*atr & 0xF);
    }
    *str = 0;
}
static const TCHAR BASE_PATH[] = _TEXT("\\config\\KeyCarriers\\");
static const TCHAR ATR[] = _TEXT("ATR");
static const TCHAR MASK[] = _TEXT("Mask");
static const TCHAR NAME[] = _TEXT("Name");
static const TCHAR FOLDERS[] = _TEXT("Folders");
static const TCHAR * default_connect = _TEXT("Default");

static SUP_INLINE DWORD register_carrier_internal(const TCHAR* carrier, const TCHAR* connect, const BYTE* atr, size_t atr_len, const BYTE* mask, size_t mask_len, const TCHAR* name, const TCHAR* folders) {
    const TCHAR* cfg = connect
	? connect
	: default_connect;
    const TCHAR* format = _TEXT("%s\\%s\\%s\\%s");
    size_t path_length = _tcslen(format)
	+ _tcslen(BASE_PATH)
	+ _tcslen(carrier)
	+ _tcslen(cfg)
	+ _tcslen(ATR)
	+ _tcslen(MASK)
	+ 0;
    TCHAR *path = (TCHAR*)malloc(path_length * sizeof(TCHAR));
    if (!path)
	return (DWORD)NTE_NO_MEMORY;

    _sntprintf(path, path_length, format, BASE_PATH, carrier, cfg, ATR);
    support_registry_put_hex(path, atr_len, atr);

    _sntprintf(path, path_length, format, BASE_PATH, carrier, cfg, MASK);
    support_registry_put_hex(path, mask_len, mask);

    if (name) {
	_sntprintf(path, path_length, format, BASE_PATH, carrier, cfg, NAME);
	support_registry_put_string(path, name);
    }
    if (folders) {
	_sntprintf(path, path_length, format, BASE_PATH, carrier, cfg, FOLDERS);
	support_registry_put_string(path, folders);
    }
    free(path);

    return (DWORD)ERROR_SUCCESS;
}

static SUP_INLINE DWORD	register_carrier(const TCHAR* carrier, const TCHAR* connect, const TCHAR* atr, const TCHAR* mask, const TCHAR* name, const TCHAR* folder) {
    size_t atr_len_in_bytes = _tcslen(atr) / 2;
    size_t mask_len_in_bytes = _tcslen(mask) / 2;
    BYTE* atr_bytes = (BYTE*)malloc(atr_len_in_bytes);
    DWORD err = 0;
    if (!atr_bytes)
	return (DWORD)NTE_NO_MEMORY;
    BYTE* mask_bytes = (BYTE*)malloc(mask_len_in_bytes);
    if (!mask_bytes) {
	free(atr_bytes);
	return (DWORD)NTE_NO_MEMORY;
    }
    str2atr(atr, atr_bytes);
    str2atr(mask, mask_bytes);
    err = register_carrier_internal(carrier, connect, atr_bytes, atr_len_in_bytes, mask_bytes, mask_len_in_bytes, name, folder);
    free(atr_bytes);
    free(mask_bytes);
    return err;
}
 
#endif //SCARD_REG_H
