
#include <stdio.h>
#include <stdlib.h>
#include <dlfcn.h>
#include <string.h>
#include <pthread.h>


#include "type.h"
#include "typelocator.h"

struct s_TypeLocator
{
    hshTvoid_list   *types;
    pthread_rwlock_t    types_lock;
};

static TypeLocator *g_locator = NULL;

TypeLocator *
typelocator_new()
{
    TypeLocator *locator = (TypeLocator *) malloc (sizeof (TypeLocator));

    locator->types = hshFvoid_init (NULL);

    pthread_rwlock_init (&locator->types_lock, NULL);

    return (locator);
}

TypeLocator *
typelocator_getLocator()
{
    if (g_locator == NULL)
    {
        g_locator = typelocator_new();
    }

    return (g_locator);
}

#if 0
static bool
typelocator_hasType (TypeLocator *locator, const char *typeName)
{
    bool ret;

    pthread_rwlock_rdlock (&locator->types_lock);

    ret = hshFvoid_contains (locator->types, typeName);

    pthread_rwlock_unlock (&locator->types_lock);

    return (ret);
}
#endif

static Type *
typelocator_findType (TypeLocator *locator, const char *typeName)
{
    Type *type;

    pthread_rwlock_rdlock (&locator->types_lock);

    type = (Type *) hshFvoid_find (locator->types, typeName);

    pthread_rwlock_unlock (&locator->types_lock);

    return (type);

}

static bool
typelocator_addType (TypeLocator *locator, Type *type)
{
    bool ret;

    pthread_rwlock_wrlock (&locator->types_lock);

    /* this will only add an entry once */
    if (hshFvoid_add (locator->types, type->name, type))
    {
        ret = true;
    }
    else
    {
        ret = false;
    }

    pthread_rwlock_unlock (&locator->types_lock);

    return (ret);
}

Type *
typelocator_getType (TypeLocator *locator, const char *typeName)
{
    Type *type;

    type = typelocator_findType (locator, typeName);

    if (type != NULL)
    {
        return (type);
    }
    else
    {
        dynTstring *string = dynFinit();
        const char *err;

        /* don't want to include the null */
        dynFappend (string, "type_declaration_", sizeof ("type_declaration_") - 1);
        dynFsappend (string, typeName);

        type = dlsym (NULL, dynFgetstr (string));

        dynFfree (string);

        if (type == NULL)
        {
            err = dlerror();

            if (err != NULL)
            {
                logFmsg (CONT_LOG_ERROR, "dlsym: %s", err);

                return (NULL);
            }
        }
        else
        {
            if (strcmp (typeName, type->name) != 0)
            {
                logFmsg (CONT_LOG_ERROR, "type missmatch: asked for \"%s\", found \"%s\"",
                        typeName, type->name);

                return (NULL);
            }
        }

        typelocator_addType (locator, type);

        return (type);
    }
}
