#!/usr/bin/env python3
import os
import sys

def get_values(entry_dict, entry_type):
    values = []
    if '*' == entry_type:
        for key in list(entry_dict.keys()):
            if entry_type in key:
                values += entry_dict[key]
                entry_dict.pop(key, None)
    elif entry_type in dict(entry_dict):
        values = entry_dict[entry_type]
        entry_dict.pop(entry_type, None)
    if values:
        return sorted(values)
    return values

def write_entry(f, entry_dict, entry_type, entry_name):
    values = get_values(entry_dict, entry_type)
    if not values:
        return

    f.write('#define have_' + entry_name.lower() + '_list_indices\n')
    f.write('static const size_t ' + entry_name.lower() + '_list_indices[] =\n')
    f.write('{\n')

    for val in values:
        f.write('\tFreeRDP_' + val + ',\n')

    f.write('};\n\n')

def write_str_case(f, entry_type, val):
    f.write('\t\t{FreeRDP_' + val + ', ' + str(entry_type) + ', "FreeRDP_' + val + '"},\n')

def write_str(f, entry_dict):
    f.write('struct settings_str_entry {\n')
    f.write('\tsize_t id;\n')
    f.write('\tsize_t type;\n')
    f.write('\tconst char* str;\n')
    f.write('};\n')
    f.write('static const struct settings_str_entry settings_map[] =\n')
    f.write('{\n')

    entry_types = ['BOOL', 'UINT16', 'INT16', 'UINT32', 'INT32', 'UINT64', 'INT64', 'char*', '*']
    for entry_type in entry_types:
        values = get_values(entry_dict, entry_type)
        if values:
            for val in values:
                write_str_case(f, entry_types.index(entry_type), val)
    f.write('};\n\n')
    f.write('\n')
    f.write('BOOL freerdp_settings_clone_keys(rdpSettings* dst, const rdpSettings* src)\n')
    f.write('{\n')
    f.write('\tsize_t x;\n')
    f.write('\tfor(x=0; x<ARRAYSIZE(settings_map); x++)\n')
    f.write('\t{\n')
    f.write('\t\tconst struct settings_str_entry* cur = &settings_map[x];\n')
    f.write('\t\tswitch(cur->type)\n')
    f.write('\t\t{\n')
    f.write('\t\t\tcase 0: /* bool */\n')
    f.write('\t\t\t{\n')
    f.write('\t\t\t\tBOOL sval = freerdp_settings_get_bool(src, cur->id);\n')
    f.write('\t\t\t\tif (!freerdp_settings_set_bool(dst, cur->id, sval))\n')
    f.write('\t\t\t\t\treturn FALSE;\n')
    f.write('\t\t\t}\n')
    f.write('\t\t\tbreak;\n')
    f.write('\t\t\tcase 1: /* UINT16 */\n')
    f.write('\t\t\t{\n')
    f.write('\t\t\t\tUINT16 sval = freerdp_settings_get_uint16(src, cur->id);\n')
    f.write('\t\t\t\tif (!freerdp_settings_set_uint16(dst, cur->id, sval))\n')
    f.write('\t\t\t\t\treturn FALSE;\n')
    f.write('\t\t\t}\n')
    f.write('\t\t\tbreak;\n')
    f.write('\t\t\tcase 2: /* INT16 */\n')
    f.write('\t\t\t{\n')
    f.write('\t\t\t\tINT16 sval = freerdp_settings_get_int16(src, cur->id);\n')
    f.write('\t\t\t\tif (!freerdp_settings_set_int16(dst, cur->id, sval))\n')
    f.write('\t\t\t\t\treturn FALSE;\n')
    f.write('\t\t\t}\n')
    f.write('\t\t\tbreak;\n')
    f.write('\t\t\tcase 3: /* UINT32 */\n')
    f.write('\t\t\t{\n')
    f.write('\t\t\t\tUINT32 sval = freerdp_settings_get_uint32(src, cur->id);\n')
    f.write('\t\t\t\tif (!freerdp_settings_set_uint32(dst, cur->id, sval))\n')
    f.write('\t\t\t\t\treturn FALSE;\n')
    f.write('\t\t\t}\n')
    f.write('\t\t\tbreak;\n')
    f.write('\t\t\tcase 4: /* INT32 */\n')
    f.write('\t\t\t{\n')
    f.write('\t\t\t\tINT32 sval = freerdp_settings_get_int32(src, cur->id);\n')
    f.write('\t\t\t\tif (!freerdp_settings_set_int32(dst, cur->id, sval))\n')
    f.write('\t\t\t\t\treturn FALSE;\n')
    f.write('\t\t\t}\n')
    f.write('\t\t\tbreak;\n')
    f.write('\t\t\tcase 5: /* UINT64 */\n')
    f.write('\t\t\t{\n')
    f.write('\t\t\t\tUINT64 sval = freerdp_settings_get_uint64(src, cur->id);\n')
    f.write('\t\t\t\tif (!freerdp_settings_set_uint64(dst, cur->id, sval))\n')
    f.write('\t\t\t\t\treturn FALSE;\n')
    f.write('\t\t\t}\n')
    f.write('\t\t\tbreak;\n')
    f.write('\t\t\tcase 6: /* INT64 */\n')
    f.write('\t\t\t{\n')
    f.write('\t\t\t\tINT64 sval = freerdp_settings_get_int64(src, cur->id);\n')
    f.write('\t\t\t\tif (!freerdp_settings_set_int64(dst, cur->id, sval))\n')
    f.write('\t\t\t\t\treturn FALSE;\n')
    f.write('\t\t\t}\n')
    f.write('\t\t\tbreak;\n')
    f.write('\t\t\tcase 7: /* strings */\n')
    f.write('\t\t\t{\n')
    f.write('\t\t\t\tconst char* sval = freerdp_settings_get_string(src, cur->id);\n')
    f.write('\t\t\t\tif (!freerdp_settings_set_string_(dst, cur->id, sval, FALSE))\n')
    f.write('\t\t\t\t\treturn FALSE;\n')
    f.write('\t\t\t}\n')
    f.write('\t\t\tbreak;\n')
    f.write('\t\t\tcase 8: /* pointer */\n')
    f.write('\t\t\t{\n')
    f.write('\t\t\t\tconst void* sval = freerdp_settings_get_pointer(src, cur->id);\n')
    f.write('\t\t\t\tif (!freerdp_settings_set_pointer(dst, cur->id, sval))\n')
    f.write('\t\t\t\t\treturn FALSE;\n')
    f.write('\t\t\t}\n')
    f.write('\t\t\tbreak;\n')

    f.write('\t\t}\n')
    f.write('\t}\n')
    f.write('\treturn TRUE;\n')
    f.write('}\n')
    f.write('\n')
    f.write('void freerdp_settings_free_keys(rdpSettings* dst, BOOL cleanup)\n')
    f.write('{\n')
    f.write('\tsize_t x;\n')
    f.write('\tfor(x=0; x<ARRAYSIZE(settings_map); x++)\n')
    f.write('\t{\n')
    f.write('\t\tconst struct settings_str_entry* cur = &settings_map[x];\n')
    f.write('\t\tswitch(cur->type)\n')
    f.write('\t\t{\n')
    f.write('\t\t\tcase 7: /* strings */\n')
    f.write('\t\t\t\tfreerdp_settings_set_string_(dst, cur->id, NULL, cleanup);\n')
    f.write('\t\t\tbreak;\n')
    f.write('\t\t\tcase 8: /* pointer */\n')
    f.write('\t\t\t\tfreerdp_settings_set_pointer(dst, cur->id, NULL);\n')
    f.write('\t\t\tbreak;\n')
    f.write('\t\t}\n')
    f.write('\t}\n')
    f.write('}\n')
    f.write('\n')

    f.write('SSIZE_T freerdp_settings_get_key_for_name(const char* value)\n')
    f.write('{\n')
    f.write('\tsize_t x;\n')
    f.write('\tfor(x=0; x<ARRAYSIZE(settings_map); x++)\n')
    f.write('\t{\n')
    f.write('\t\tconst struct settings_str_entry* cur = &settings_map[x];\n')
    f.write('\t\tif (strcmp(value, cur->str) == 0)\n')
    f.write('\t\t\treturn cur->id;\n')
    f.write('\t}\n')
    f.write('\treturn -1;\n')
    f.write('}\n')
    f.write('\n')
    f.write('SSIZE_T freerdp_settings_get_type_for_name(const char* value)\n')
    f.write('{\n')
    f.write('\tsize_t x;\n')
    f.write('\tfor(x=0; x<ARRAYSIZE(settings_map); x++)\n')
    f.write('\t{\n')
    f.write('\t\tconst struct settings_str_entry* cur = &settings_map[x];\n')
    f.write('\t\tif (strcmp(value, cur->str) == 0)\n')
    f.write('\t\t\treturn cur->type;\n')
    f.write('\t}\n')
    f.write('\treturn -1;\n')
    f.write('}\n')
    f.write('\n')
    f.write('SSIZE_T freerdp_settings_get_type_for_key(size_t key)\n')
    f.write('{\n')
    f.write('\tsize_t x;\n')
    f.write('\tfor(x=0; x<ARRAYSIZE(settings_map); x++)\n')
    f.write('\t{\n')
    f.write('\t\tconst struct settings_str_entry* cur = &settings_map[x];\n')
    f.write('\t\tif (cur->id == key)\n')
    f.write('\t\t\treturn cur->type;\n')
    f.write('\t}\n')
    f.write('\treturn -1;\n')
    f.write('}\n')
    f.write('\n')
    f.write('const char* freerdp_settings_get_name_for_key(size_t key)\n')
    f.write('{\n')
    f.write('\tsize_t x;\n')
    f.write('\tfor(x=0; x<ARRAYSIZE(settings_map); x++)\n')
    f.write('\t{\n')
    f.write('\t\tconst struct settings_str_entry* cur = &settings_map[x];\n')
    f.write('\t\tif (cur->id == key)\n')
    f.write('\t\t\treturn cur->str;\n')
    f.write('\t}\n')
    f.write('\treturn NULL;\n')
    f.write('}\n')
    f.write('\n')

def write_getter_case(f, val):
    f.write('\t\tcase FreeRDP_' + val + ':\n')
    f.write('\t\t\treturn settings->' + val + ';\n\n')

def write_getter(f, entry_dict, entry_type, entry_name):
    isString = 'string' in entry_name
    isPointer = 'pointer' in entry_name
    values = get_values(entry_dict, entry_type)

    if isPointer:
        f.write('const void*')
    elif isString:
        f.write('const ' + entry_type)
    else:
        f.write(entry_type)
    f.write(' freerdp_settings_get_' + entry_name.lower() + '(const rdpSettings* settings, size_t id)\n')
    f.write('{\n')
    f.write('\tif (!settings)\n')
    f.write('\t\treturn FALSE;\n\n')
    f.write('\tswitch (id)\n')
    f.write('\t{\n')
    if values:
        for val in values:
            write_getter_case(f, val)
    f.write('\t\tdefault:\n')
    f.write('\t\t\tWLog_ERR(TAG, "[%s] Invalid key index %"PRIuz, __FUNCTION__,  id);\n')
    f.write('\t\t\treturn FALSE;\n')
    f.write('\t}\n')
    f.write('}\n\n')

def write_setter_case(f, val, isString, isPointer):
    f.write('\t\tcase FreeRDP_' + val + ':\n')
    if isPointer:
        f.write('\t\t\tsettings->' + val + ' = (void*)val;\n')
        f.write('\t\t\tbreak;\n\n')
    elif not isString:
        f.write('\t\t\tsettings->' + val + ' = val;\n')
        f.write('\t\t\tbreak;\n\n')
    else:
        f.write('\t\t\tif (cleanup)\n')
        f.write('\t\t\t\tfree(settings->' + val + ');\n')
        f.write('\t\t\tsettings->' + val + ' = (val ? _strdup(val) : NULL);\n')
        f.write('\t\t\treturn (!val || settings->' + val + ' != NULL);\n\n')

def write_setter(f, entry_dict, entry_type, entry_name):
    isString = 'string' in entry_name
    isPointer = 'pointer' in entry_name
    values = get_values(entry_dict, entry_type)

    f.write('BOOL freerdp_settings_set_' + entry_name.lower())
    if isString:
        f.write('_')
    f.write('(rdpSettings* settings, size_t id, ')
    if isString or isPointer:
        f.write('const ')
    if not isPointer:
        f.write(entry_type + ' val')
    else:
        f.write('void* val')
    if isString:
        f.write(', BOOL cleanup)\n')
    else:
        f.write(')\n')
    f.write('{\n')
    f.write('\tif (!settings)\n')
    f.write('\t\treturn FALSE;\n\n')
    f.write('\tswitch (id)\n')
    f.write('\t{\n')
    if values:
        for val in values:
            write_setter_case(f, val, isString, isPointer)
    f.write('\t\tdefault:\n')
    f.write('\t\t\tWLog_ERR(TAG, "[%s] Invalid key index %"PRIuz, __FUNCTION__,  id);\n')
    f.write('\t\t\treturn FALSE;\n')
    f.write('\t}\n')
    f.write('\treturn TRUE;\n')
    f.write('}\n\n')
    f.write('\n')
    if isString:
        f.write('BOOL freerdp_settings_set_' + entry_name.lower() + '(rdpSettings* settings, size_t id, ')
        if isString or isPointer:
            f.write('const ')
        if not isPointer:
            f.write(entry_type + ' val')
        else:
            f.write('void* val')
        f.write(')\n')
        f.write('{\n')
        f.write('\treturn freerdp_settings_set_' + entry_name.lower() + '_(settings, id, val, TRUE);\n')
        f.write('}\n')
        f.write('\n')

name = os.path.dirname(os.path.realpath(__file__))
begin = "WARNING: this data structure is carefully padded for ABI stability!"
end = "WARNING: End of ABI stable zone!"

print('begin parsing settings header')
try:
    type_list = dict()

    with open(name + "/../include/freerdp/settings.h", "r") as f:
        lines = f.readlines()
        started = False
        for line in lines:
            if not started:
                if begin in line:
                    started = True
                continue

            if end in line:
                break

            sline = line.strip()
            if not sline:
                continue
            if sline.startswith('/'):
                continue
            if sline.startswith('*'):
                continue
            if 'padding' in sline:
                continue

            if sline.startswith('ALIGN64'):
                sline = sline[7:].strip()

            sline = sline[:sline.find(';')]
            pair = sline.split()
            if pair[0] in type_list:
                type_list[pair[0]].append(pair[1])
            else:
                type_list[pair[0]] = [pair[1]]

    with open(name + '/../libfreerdp/common/settings_getters.c', 'w+') as f:
        f.write('/* Generated by ' + ''  + ' */\n\n')
        f.write('#include <freerdp/settings.h>\n')
        f.write('#include <freerdp/log.h>\n\n')
        f.write('#define TAG FREERDP_TAG("common.settings")\n\n')

        getter_list = dict(type_list)
        setter_list = dict(type_list)
        write_getter(f, getter_list, 'BOOL', 'bool')
        write_setter(f, setter_list, 'BOOL', 'bool')
        write_getter(f, getter_list, 'UINT16', 'uint16')
        write_setter(f, setter_list, 'UINT16', 'uint16')
        write_getter(f, getter_list, 'INT16', 'int16')
        write_setter(f, setter_list, 'INT16', 'int16')
        write_getter(f, getter_list, 'UINT32', 'uint32')
        write_setter(f, setter_list, 'UINT32', 'uint32')
        write_getter(f, getter_list, 'INT32', 'int32')
        write_setter(f, setter_list, 'INT32', 'int32')
        write_getter(f, getter_list, 'UINT64', 'uint64')
        write_setter(f, setter_list, 'UINT64', 'uint64')
        write_getter(f, getter_list, 'INT64', 'int64')
        write_setter(f, setter_list, 'INT64', 'int64')
        write_getter(f, getter_list, 'char*', 'string')
        write_setter(f, setter_list, 'char*', 'string')
        write_getter(f, getter_list, '*', 'pointer')
        write_setter(f, setter_list, '*', 'pointer')

        f.write('\n')

    with open(name + '/../libfreerdp/common/settings_str.c', 'w+') as f:
        f.write('/* Generated by ' + ''  + ' */\n\n')
        f.write('#include <freerdp/settings.h>\n')
        f.write('#include <freerdp/log.h>\n\n')
        f.write('#include "../core/settings.h"\n\n')
        f.write('#define TAG FREERDP_TAG("common.settings")\n\n')

        getter_list = dict(type_list)
        write_str(f, getter_list)
        f.write('\n')


    with open(name + '/../libfreerdp/core/test/settings_property_lists.h', 'w+') as f:
        f.write('#ifndef TEST_SETTINGS_PROPERTY_LISTS\n')
        f.write('#define TEST_SETTINGS_PROPERTY_LISTS\n\n')

        write_entry(f, type_list, 'BOOL', 'bool')
        write_entry(f, type_list, 'UINT16', 'uint16')
        write_entry(f, type_list, 'INT16', 'int16')
        write_entry(f, type_list, 'UINT32', 'uint32')
        write_entry(f, type_list, 'INT32', 'int32')
        write_entry(f, type_list, 'UINT64', 'uint64')
        write_entry(f, type_list, 'INT64', 'int64')
        write_entry(f, type_list, 'char*', 'string')
        write_entry(f, type_list, '*', 'pointer')

        f.write('#endif /* TEST_SETTINGS_PROPERTY_LISTS */\n\n')

        print('remaining:\n' + str(type_list))
except IOError as e:
    print('failed to parse settings header ' + str(e))
    sys.exit(-1)
print('ended parsing settings header')
