import json

def configure(conf):
    pass

def generate_apps_table(task):
    in_node = task.inputs[0]
    registry = task.outputs[0]
    app_enum = task.outputs[1]
    # apps that are assumed to exist by source and must always have a define
    default_app_enums = [
        'ALARMS',
        'GOLF',
        'MUSIC',
        'SETTINGS',
        'SPORTS',
    ]
    system_apps = []
    resource_apps = []
    definition = {}
    with open(in_node.abspath(), 'r') as f_in:
        definition.update(json.load(f_in))

    system_apps.extend(definition['system_apps'])
    resource_apps.extend(definition['resource_apps'])

    def uuid_to_byte_hex_str(uuid):
        uuid = uuid.replace("-", "")

        # split string into groups of twos
        n = 2
        pieces = [uuid[i:i+n] for i in range(0, len(uuid), n)]
        # prefix every group of two with 0x and add commas
        hex_str = "0x" + ", 0x".join(pieces)
        return hex_str

    def generate_registry(f_out):
        # write generated header
        f_out.write('// @'+'generated -- DO NOT EDIT\n\n')

        # write out includes
        f_out.write('#include "system_app_ids.auto.h"\n')
        f_out.write('#include "process_management/pebble_process_md.h"\n')
        f_out.write('#include "resource/resource_ids.auto.h"\n\n')

        # zeroed app id's indicate disabled apps
        enabled_system_apps = list(filter(lambda e: e['id'] != 0, system_apps))

        # write out function declarations for system apps
        for entry in enabled_system_apps:
            f_out.write('extern const PebbleProcessMd *'
                "{cb_str}(void);\n".format(
                cb_str=entry['md_fn']))

        # write variable name
        f_out.write('\n\nstatic const AppRegistryEntry '
                    'APP_RECORDS[] = {\n')

        # comment for system apps
        f_out.write('\n  // System Apps\n')

        # write all entries for system apps
        for entry in enabled_system_apps:
            f_out.write(
                '  {{\n'
                '    .id = APP_ID_{enum},\n'
                '    .type = AppInstallStorageFw,\n'
                '    .md_fn = &{cb_str},\n'
                '    .color.argb = {color_argb8},\n'
                '  }},\n'.format(
                enum=entry['enum'],
                cb_str=entry['md_fn'],
                color_argb8=entry.get('color_argb8', 'GColorClearARGB8')))

        # comment for resource apps
        f_out.write('\n  // Resource (stored) Apps\n')

        # write all entries for resource apps
        for entry in resource_apps:
            f_out.write(
                '  {{\n'
                '    .id = APP_ID_{enum},\n'
                '    .type = AppInstallStorageResources,\n'
                '    .name = "{name}",\n'
                '    .uuid = {{ {uuid} }},\n'
                '    .bin_resource_id = {bin_id},\n'
                '    .icon_resource_id = {icon_id},\n'
                '    .color.argb = {color_argb8},\n'
                '  }},\n'.format(
                enum=entry['enum'],
                name=entry['name'],
                uuid=uuid_to_byte_hex_str(entry['uuid']),
                bin_id=entry['bin_resource_id'],
                icon_id=entry['icon_resource_id'],
                color_argb8=entry.get('color_argb8', 'GColorClearARGB8')))

        f_out.write('};\n')

    def generate_app_enum(f_out):
        # write generated header
        f_out.write('// @'+'generated -- DO NOT EDIT\n\n')

        # write out includes
        f_out.write('#include "process_management/app_install_types.h"\n\n')

        # write all entries for resource apps
        built_app_enums = []

        def write_app_enum(enum, id):
            f_out.write('#define APP_ID_{enum} ((AppInstallId) {id})\n'.format(enum=enum, id=id))

        for entry in (system_apps + resource_apps):
            built_app_enums.append(entry['enum'])
            write_app_enum(enum=entry['enum'], id=entry['id'])

        # write remaining default apps that are not being built
        for enum in [x for x in default_app_enums if x not in built_app_enums]:
            write_app_enum(enum=enum, id=0)


    def _entry_in_test_apps_list(entry):
        return str(entry['id']) in task.env.test_apps_list

    # If the current build config allows the app with the given list of defines to be added.
    # Disabled entries have no entry in the app registry.
    def entry_enabled(entry):

        defines = entry.get("ifdefs") or []
        platforms = entry.get("target_platforms") or []

        # Disabled overrides any define
        if "DISABLED" in defines:
            return False

        if (platforms) and (task.generator.bld.get_platform_name() not in platforms):
            # If we have a platform list and the current platform isn't listed, return false
            return False

        # keep around for legacy purposes
        if "DEFAULT" in defines:
            return True

        if defines:
            # If we have a defines list, run through all of them and see if we should be including
            # this application

            # if test apps are enabled and we have a list
            test_apps_enabled = "ENABLE_TEST_APPS" in task.env.DEFINES
            entry_is_test_app = "ENABLE_TEST_APPS" in defines
            has_test_app_list = len(task.env.test_apps_list) > 0
            if test_apps_enabled and has_test_app_list:
                # check if app is allowed. If not, return False
                desired_test_app = _entry_in_test_apps_list(entry)
                if entry_is_test_app and not desired_test_app:
                    return False
            else:
                if entry_is_test_app and not test_apps_enabled:
                    return False

            if all(define in task.env.DEFINES for define in defines):
                return True
        else:
            # if there are no defines, it means we should include it by default
            return True

        return False

    def _set_disabled_app_ids_to_zero():
        for entry in system_apps:
            if not entry_enabled(entry):
                entry['id'] = 0

    def _move_desired_test_apps_to_top():
        for app_list in [system_apps, resource_apps]:
            temp_list = []
            for entry in app_list:
                if _entry_in_test_apps_list(entry):
                    temp_list.insert(0, entry)
                else:
                    temp_list.append(entry)
            app_list[:] = temp_list

    _set_disabled_app_ids_to_zero()
    _move_desired_test_apps_to_top()

    with open(registry.abspath(), 'w') as f_registry:
        generate_registry(f_registry)

    with open(app_enum.abspath(), 'w') as f_app_enum:
        generate_app_enum(f_app_enum)


def build(bld):
    if bld.variant == 'prf':
        shell_name = 'prf'
    else:
        shell_name = bld.env.NORMAL_SHELL

    in_node = bld.path.find_node('%s/system_app_registry_list.json' % shell_name)

    registry = bld.path.get_bld().make_node('system_app_registry_list.auto.h')
    app_enum = bld.path.get_bld().make_node('system_app_ids.auto.h')

    bld(rule=generate_apps_table,
        source=[in_node],
        target=[registry, app_enum],
        vars=['DEFINES', 'test_apps_list'])

# vim:filetype=python
