/* Copyright (C) 2018-2020  Philipp "ph3-der-loewe" Schafft <lion@lion.leolix.org>
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Library General Public
 * License as published by the Free Software Foundation; either
 * version 2 of the License, or (at your option) any later version.
 *
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Library General Public License for more details.
 *
 * You should have received a copy of the GNU Library General Public
 * License along with this library; if not, write to the
 * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
 * Boston, MA  02110-1301, USA.
 */

#ifdef HAVE_CONFIG_H
#include <config.h>
#endif

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

#include <igloo/config.h>
#include <igloo/error.h>
#include <igloo/ro.h>

#include "private.h"

#define MAX_RECURSIONS 32

struct igloo_ro_object_group_tag {
    igloo_ro_full_t __parent;
    /* lock for igloo_ro_*() */
    igloo_rwlock_t lock;
    /* instance */
    igloo_ro_t instance;
    bool is_default_group;
};

static igloo_error_t igloo_ro_full_new(igloo_ro_t self, const igloo_ro_type_t *type, va_list ap);

static igloo_error_t igloo_ro_object_group_new(igloo_ro_t self, const igloo_ro_type_t *type, va_list ap);
static void igloo_ro_object_group_free(igloo_ro_t self);

igloo_FEATURE_PUBLIC(igloo_ro_feature_ownlock);

static const igloo_feature_t ** features_full[] = {
    &igloo_ro_feature_ownlock,
    NULL
};

igloo_RO_OPEN_TYPE(igloo_ro_stub_t, igloo_ro_stub_t,
        igloo_RO_TYPEDECL_NEW_NOOP()
        );

igloo_RO_OPEN_TYPE(igloo_ro_tiny_t, igloo_ro_stub_t,
        igloo_RO_TYPEDECL_NEW_NOOP()
        );

igloo_RO_OPEN_TYPE(igloo_ro_full_t, igloo_ro_stub_t,
        igloo_RO_TYPEDECL_NEW(igloo_ro_full_new),
        igloo_RO_TYPEDECL_FEATURES(features_full)
        );

igloo_RO_OPEN_TYPE(igloo_ro_object_group_t, igloo_ro_full_t,
        igloo_RO_TYPEDECL_NEW(igloo_ro_object_group_new),
        igloo_RO_TYPEDECL_FREE(igloo_ro_object_group_free)
        );

igloo_RO_ALIAS_TYPE(igloo_ro_t, igloo_ro_stub_t);

/* ---[ Validation functions ]--- */
static inline igloo_ATTR_F_HOT igloo_ATTR_F_CONST bool is_valid_object_or_type_pointer(const void *addr)
{
    intptr_t ptr = (intptr_t)addr;

    if (ptr < 1024)
        return false;

#ifdef igloo_ATTR_T_ALIGNED_EFFECTIVE
    if (ptr & 0x7)
        return false;
#endif

    return true;
}

static inline igloo_error_t check_type(const igloo_ro_type_t *type)
{
    const igloo_ro_type_t *cur = type;
    int loop = 0;
    size_t max_size = type->type_length;

    if (!is_valid_object_or_type_pointer(type))
        return igloo_ERROR_FAULT;

    do {
        size_t size = cur->type_length;

        if (!igloo_CONTROL_CHECK3(cur->control, igloo_RO__CONTROL_VERSION, sizeof(igloo_ro_type_t), igloo_CONTROL_TYPE__RO_TYPE))
            return igloo_ERROR_NSVERSION;

        if (cur != type && !cur->type_newcb)
            return igloo_ERROR_NOSYS;

        if (size > max_size || size < sizeof(igloo_ro_stub_t))
            return igloo_ERROR_FAULT;

        max_size = size;

        loop++;

        if (loop == MAX_RECURSIONS)
            return igloo_ERROR_LOOP;
    } while (cur != cur->type_parent && (cur = cur->type_parent));

    return igloo_ERROR_NONE;
}

static inline igloo_ATTR_F_HOT igloo_ATTR_F_PURE bool is_valid_object(igloo_ro_t object)
{
    igloo_ro_stub_t *stub = igloo_RO__GETSTUB(object);

    if (igloo_ro_is_null(object))
        return false;

    if (!is_valid_object_or_type_pointer(stub))
        return false;

    if (!is_valid_object_or_type_pointer(igloo_RO_GET_TYPE(object)))
        return false;

    if (!stub->refc && !stub->wrefc)
        return false;

    return true;
}

static inline igloo_ATTR_F_HOT igloo_ATTR_F_PURE bool is_object_of_type(igloo_ro_t object, const igloo_ro_type_t *type)
{
    const igloo_ro_type_t *cur;

    if (!is_valid_object(object))
        return false;

    cur = igloo_RO_GET_TYPE(object);

    do {
        if (cur == type)
            return true;
    } while (cur != cur->type_parent && (cur = cur->type_parent));

    return false;
}

static inline igloo_ATTR_F_HOT igloo_ATTR_F_PURE bool is_object_strong(igloo_ro_t object)
{
    return is_valid_object(object) && igloo_RO__GETSTUB(object)->refc ? true : false;
}

static inline igloo_ATTR_F_HOT igloo_ATTR_F_PURE bool is_object_of_type_strong(igloo_ro_t object, const igloo_ro_type_t *type)
{
    return is_object_of_type(object, type) && is_object_strong(object) ? true : false;
}

static inline igloo_ATTR_F_HOT igloo_ATTR_F_PURE bool is_object_full(igloo_ro_t object)
{
    return is_object_of_type(object, igloo_RO_GET_TYPE_BY_SYMBOL(igloo_ro_full_t));
}
/* ---[ End of validation functions ]--- */

/* ---[ Public validation functions ]--- */
bool            igloo_RO_HAS_TYPE_raw(igloo_ro_t object, const igloo_ro_type_t *type)
{
    return is_object_of_type(object, type);
}
bool            igloo_RO_IS_VALID_raw(igloo_ro_t object, const igloo_ro_type_t *type)
{
    return is_object_of_type_strong(object, type);
}
igloo_ro_t      igloo_RO_TO_TYPE_raw(igloo_ro_t object, const igloo_ro_type_t *type)
{
    return is_object_of_type_strong(object, type) ? object : igloo_RO_NULL;
}
/* ---[ End of public validation functions ]--- */

/* ---[ Type specific functions ]--- */
/* This is not static as it is used by igloo_RO_TYPEDECL_NEW_NOOP() */
igloo_error_t igloo_ro_new__return_zero(igloo_ro_t self, const igloo_ro_type_t *type, va_list ap)
{
    (void)self, (void)type, (void)ap;
    return igloo_ERROR_NONE;
}

static igloo_error_t igloo_ro_full_new(igloo_ro_t self, const igloo_ro_type_t *type, va_list ap)
{
    igloo_ro_full_t *full = igloo_ro_to_type(self, igloo_ro_full_t);

    (void)type, (void)ap;

    igloo_rwlock_init(&(full->lock));

    return igloo_ERROR_NONE;
}

static igloo_error_t igloo_ro_object_group_new(igloo_ro_t self, const igloo_ro_type_t *type, va_list ap)
{
    igloo_ro_stub_t *stub = igloo_RO__GETSTUB(self);
    igloo_ro_object_group_t *group = igloo_ro_to_type(self, igloo_ro_object_group_t);
    igloo_error_t error;

    (void)type, (void)ap;

    igloo_rwlock_init(&(group->lock));

    if (stub->group) {
        error = igloo_ro_get_instance(self, &(group->instance));
        if (error != igloo_ERROR_NONE)
            return error;

        error = igloo_ro_unref(&(stub->group));
        if (error != igloo_ERROR_NONE)
            return error;
        stub->group = group;
    }

    return igloo_ERROR_NONE;
}

static void igloo_ro_object_group_free(igloo_ro_t self)
{
    igloo_ro_object_group_t *group = igloo_ro_to_type(self, igloo_ro_object_group_t);

    if (group->is_default_group) {
        igloo_ro_weak_unref(&(group->instance));
    } else {
        igloo_ro_unref(&(group->instance));
    }

    igloo_rwlock_destroy(&(group->lock));
}
/* ---[ End of type specific functions ]--- */

/* ---[ Object locking functions ]--- */
#define LOCK_FUNC(func, action) \
static inline igloo_ATTR_F_HOT void igloo_ro_ ## action ## lock (igloo_ro_t self, bool full) \
{ \
    if (full) { \
        func(&(self.subtype__igloo_ro_full_t->lock)); \
    } else { \
        func(&(igloo_RO__GETSTUB(self)->group->lock)); \
    } \
}

LOCK_FUNC(igloo_rwlock_rlock, r)
LOCK_FUNC(igloo_rwlock_wlock, rw)
LOCK_FUNC(igloo_rwlock_unlock, un)
LOCK_FUNC(igloo_rwlock_destroy, destroy)
/* ---[ End of object locking functions ]--- */

/* ---[ Object creation functions ]--- */
igloo_error_t   igloo_ro_new__raw_checked(igloo_ro_t *out, const igloo_ro_type_t *type, igloo_ro_t group, ...)
{
    const igloo_ro_type_t *cur = type->type_parent;
    igloo_ro_new_t newcb[MAX_RECURSIONS];
    size_t newcb_fill = 0;
    igloo_ro_stub_t *obj = NULL;
    igloo_error_t error;
    va_list ap;

    do {
        if (newcb_fill == (sizeof(newcb)/sizeof(*newcb)))
            return igloo_ERROR_FAULT;

        if (!cur->type_newcb)
            return igloo_ERROR_FAULT;

        newcb[newcb_fill++] = cur->type_newcb;
    } while (cur != cur->type_parent && (cur = cur->type_parent));

    if (newcb_fill == 0)
        return igloo_ERROR_FAULT;

    obj = calloc(1, type->type_length);
    if (!obj)
        return igloo_ERROR_NOMEM;

    obj->refc = 1;
    obj->type = type;

    if (!igloo_ro_is_null(group)) {
        error = igloo_ro_get_object_group(group, &(obj->group));
        if (error != igloo_ERROR_NONE) {
            igloo_ro_unref(&obj);
            return error;
        }
    }

    va_start(ap, group);
    do {
        va_list aq;
        newcb_fill--;
        va_copy(aq, ap);
        error = newcb[newcb_fill](obj, type, aq);
        va_end(aq);
        if (error != igloo_ERROR_NONE) {
            va_end(ap);
            igloo_ro_unref(&obj);
            return error;
        }
    } while (newcb_fill);
    va_end(ap);

    *out = (igloo_ro_t)obj;
    return igloo_ERROR_NONE;
}

igloo_error_t   igloo_ro_new__raw(igloo_ro_t *out, const igloo_ro_type_t *type, igloo_ro_t group)
{
    igloo_error_t error;

    if (!out || !type || !igloo_ro_is_valid(group, igloo_ro_stub_t))
        return igloo_ERROR_FAULT;

    error = check_type(type);
    if (error != igloo_ERROR_NONE)
        return error;

    return igloo_ro_new__raw_checked(out, type, group);
}

igloo_error_t   igloo_ro_new__simple(igloo_ro_t *out, const igloo_ro_type_t *type, igloo_ro_t group, ...)
{
    igloo_error_t error;
    va_list ap;

    if (!out || !type || !igloo_ro_is_valid(group, igloo_ro_stub_t))
        return igloo_ERROR_FAULT;

    error = check_type(type);
    if (error != igloo_ERROR_NONE)
        return error;

    if (!type->type_newcb)
        return igloo_ERROR_NOSYS;

    error = igloo_ro_new__raw_checked(out, type, group);
    if (error != igloo_ERROR_NONE)
        return error;

    va_start(ap, group);
    error = type->type_newcb(*out, type, ap);
    va_end(ap);

    if (error != igloo_ERROR_NONE) {
        igloo_ro_unref(out);
        return error;
    }

    return igloo_ERROR_NONE;
}
/* ---[ End of object creation functions ]--- */

igloo_error_t   igloo_ro_ref_raw(igloo_ro_t self, igloo_ro_t *out, const igloo_ro_type_t *type)
{
    igloo_ro_stub_t *stub = igloo_RO__GETSTUB(self);
    bool full = is_object_full(self);

    if (!stub || !out)
        return igloo_ERROR_FAULT;

    if (!is_object_of_type(self, type))
        return igloo_ERROR_TYPEMM;

    igloo_ro_rwlock(self, full);
    if (!stub->refc) {
        igloo_ro_unlock(self, full);
        return igloo_ERROR_GONE;
    }

    stub->refc++;
    *out = self;

    igloo_ro_unlock(self, full);
    return igloo_ERROR_NONE;
}

static void igloo_ro_ungroup(igloo_ro_stub_t *stub)
{
    if (stub->group != NULL && (igloo_ro_stub_t*)stub->group != stub)
        igloo_ro_unref(&(stub->group));
}

static igloo_error_t   igloo_ro_destroy(igloo_ro_t *self, bool full)
{
    igloo_ro_stub_t *stub = igloo_RO__GETSTUB(*self);
    igloo_ro_unlock(stub, full);
    igloo_ro_ungroup(stub);
    if (full)
        igloo_ro_destroylock(stub, full);
    free(stub);
    *self = igloo_RO_NULL;
    return igloo_ERROR_NONE;
}

static void igloo_ro_run_free(igloo_ro_stub_t *stub)
{
    const igloo_ro_type_t *cur = igloo_RO_GET_TYPE(stub);

    do {
        if (cur->type_freecb) {
            cur->type_freecb(stub);
            return;
        }
    } while (cur != cur->type_parent && (cur = cur->type_parent));
}

igloo_error_t   igloo_ro_unref_raw(igloo_ro_t *self)
{
    igloo_ro_stub_t *stub;
    bool full;

    if (!self)
        return igloo_ERROR_FAULT;

    if (igloo_ro_is_null(*self))
        return igloo_ERROR_NONE;

    stub = igloo_ro_to_type(*self, igloo_ro_stub_t);

    if (!stub)
        return igloo_ERROR_FAULT;

    full = is_object_full(*self);

    igloo_ro_rwlock(*self, full);

    if (stub->refc > 1) {
        stub->refc--;
        igloo_ro_unlock(*self, full);
        *self = igloo_RO_NULL;
        return igloo_ERROR_NONE;
    }

    igloo_ro_run_free(stub);

    if (full)
        igloo_ro_ungroup(stub);

    stub->refc--;

    if (stub->wrefc) {
        igloo_ro_unlock(*self, full);
        *self = igloo_RO_NULL;
        return igloo_ERROR_NONE;
    } else {
        return igloo_ro_destroy(self, full);
    }
}

igloo_error_t   igloo_ro_weak_ref_raw(igloo_ro_t self, igloo_ro_t *out, const igloo_ro_type_t *type)
{
    igloo_ro_stub_t *stub = igloo_RO__GETSTUB(self);
    bool full = is_object_full(self);

    if (!stub || !out)
        return igloo_ERROR_FAULT;

    if (!is_object_of_type(self, type))
        return igloo_ERROR_TYPEMM;

    igloo_ro_rwlock(self, full);
    stub->wrefc++;
    *out = self;
    igloo_ro_unlock(self, full);

    return igloo_ERROR_NONE;
}

igloo_error_t   igloo_ro_weak_unref_raw(igloo_ro_t *self)
{
    igloo_ro_stub_t *stub;
    bool full;

    if (!self)
        return igloo_ERROR_FAULT;

    stub = igloo_RO__GETSTUB(*self);

    if (!stub)
        return igloo_ERROR_NONE;

    if (!is_valid_object(*self))
        return igloo_ERROR_FAULT;

    full = is_object_full(*self);

    igloo_ro_rwlock(*self, full);
    stub->wrefc--;

    if (stub->refc || stub->wrefc) {
        igloo_ro_unlock(*self, full);
        *self = igloo_RO_NULL;
        return igloo_ERROR_NONE;
    } else {
        return igloo_ro_destroy(self, full);
    }
}

igloo_error_t   igloo_ro_ref_replace_raw(igloo_ro_t self, igloo_ro_t *out, const igloo_ro_type_t *type)
{
    igloo_ro_t ref = igloo_RO_NULL;
    igloo_error_t error;

    if (!out)
        return igloo_ERROR_FAULT;

    if (!igloo_ro_is_null(self)) {
        if (!is_object_of_type_strong(self, type))
            return igloo_ERROR_INVAL;

        error = igloo_ro_ref_transparent(self, &ref);
        if (error != igloo_ERROR_NONE)
            return error;
    }

    error = igloo_ro_unref(out);
    if (error != igloo_ERROR_NONE)
        return error;

    *out = ref;
    return igloo_ERROR_NONE;
}

igloo_error_t   igloo_ro_weak_ref_replace_raw(igloo_ro_t self, igloo_ro_t *out, const igloo_ro_type_t *type)
{
    igloo_ro_t ref = igloo_RO_NULL;
    igloo_error_t error;

    if (!out)
        return igloo_ERROR_FAULT;

    if (!igloo_ro_is_null(self)) {
        if (!is_object_of_type_strong(self, type))
            return igloo_ERROR_INVAL;

        error = igloo_ro_weak_ref_transparent(self, &ref);
        if (error != igloo_ERROR_NONE)
            return error;
    }

    error = igloo_ro_weak_unref(out);
    if (error != igloo_ERROR_NONE)
        return error;

    *out = ref;
    return igloo_ERROR_NONE;
}

void            igloo_ro_locked_start(igloo_ro_t self, const igloo_ro_type_t *type, bool writelock)
{
    (void)type;
    if (writelock) {
        igloo_ro_rwlock(self, is_object_full(self));
    } else {
        igloo_ro_rlock(self, is_object_full(self));
    }
}

void            igloo_ro_locked_end(igloo_ro_t self, const igloo_ro_type_t *type)
{
    (void)type;
    igloo_ro_unlock(self, is_object_full(self));
}

igloo_error_t   igloo_ro_get_instance(igloo_ro_t self, igloo_ro_t *instance)
{
    igloo_ro_stub_t *stub = igloo_ro_to_type(self, igloo_ro_stub_t);

    if (!stub || !instance)
        return igloo_ERROR_FAULT;

    if (!stub->group)
        return igloo_ERROR_GONE;

    return igloo_ro_ref_transparent(stub->group->instance, instance);
}

igloo_error_t   igloo_ro_get_object_group(igloo_ro_t self, igloo_ro_object_group_t **object_group)
{
    igloo_ro_stub_t *stub = igloo_ro_to_type(self, igloo_ro_stub_t);

    if (!stub || !object_group)
        return igloo_ERROR_FAULT;

    if (!stub->group)
        return igloo_ERROR_GONE;

    return igloo_ro_ref(stub->group, object_group, igloo_ro_object_group_t);
}


igloo_error_t   igloo_ro_get_error_raw(igloo_ro_t self, igloo_error_t *result, const igloo_ro_type_t *type)
{
    igloo_error_t error = igloo_ERROR_NOSYS;
    bool full;

    if (!is_valid_object(self) || !result || !type)
        return igloo_ERROR_FAULT;

    if (!is_object_of_type(self, type))
        return igloo_ERROR_TYPEMM;

    full = is_object_full(self);
    igloo_ro_rlock(self, full);

    if (!is_object_of_type_strong(self, type)) {
        igloo_ro_unlock(self, full);
        return igloo_ERROR_GONE;
    }

    do {
        if (type->type_get_errorcb) {
            error = type->type_get_errorcb(self, result);
            break;
        }
    } while (type != type->type_parent && (type = type->type_parent));

    igloo_ro_unlock(self, full);

    return error;
}

igloo_error_t   igloo_ro_stringify_raw(igloo_ro_t self, char **result, igloo_ro_sy_t flags, const igloo_ro_type_t *type)
{
#define STRINGIFY_FORMAT_STRONG     "{%p@%s, strong, %s, %zu static bytes, group={%p@%s}}", \
    stub, igloo_RO_GET_TYPENAME(self), (full ? "full" : "tiny"), igloo_RO_GET_TYPE(self)->type_length, stub->group, igloo_RO_GET_TYPENAME(stub->group)
#define STRINGIFY_FORMAT_WEAK       "{%p@%s, weak, %s, %zu static bytes}", \
    stub, igloo_RO_GET_TYPENAME(self), (full ? "full" : "tiny"), igloo_RO_GET_TYPE(self)->type_length
#define STRINGIFY_FORMAT_INVALID    "{%p, invalid}", \
    igloo_RO__GETSTUB(self)
#define STRINGIFY_TEXT_NULL     "{igloo_RO_NULL}"
#define STRINGIFY_PROC(format) \
    do { \
        int ret = snprintf(NULL, 0, format); \
        if (ret > 1) { \
            *result = calloc(1, ret + 2); \
            if (!*result) { \
                error = igloo_ERROR_NOMEM; \
            } else { \
                int res = snprintf(*result, ret + 1, format); \
                if (res > 0 && res < (ret + 2)) { \
                    error = igloo_ERROR_NONE; \
                } else { \
                    free(*result); \
                    *result = NULL; \
                    error = igloo_ERROR_FAULT; \
                } \
            } \
        } \
    } while (0)

    igloo_ro_stub_t *stub = igloo_ro_to_type(self, igloo_ro_stub_t);
    igloo_error_t error = igloo_ERROR_NOSYS;
    igloo_ro_stringify_t impl = NULL;
    bool full;

    if (!result)
        return igloo_ERROR_FAULT;

    if (!flags)
        return igloo_ERROR_INVAL;

    if (flags & igloo_RO_SY_DEFAULT)
        flags |= igloo_RO_SY_OBJECT|igloo_RO_SY_DEBUG;

    if (igloo_ro_is_null(self)) {
        if (flags & igloo_RO_SY_OBJECT) {
            *result = strdup(STRINGIFY_TEXT_NULL);
            if (*result)
                return igloo_ERROR_NONE;
            return igloo_ERROR_NOMEM;
        } else {
            return igloo_ERROR_FAULT;
        }
    }

    // check for invalid pointers: when igloo_ro_is_null(self) is not true but stub == NULL we must have an invalid pointer.
    if (!stub) {
        STRINGIFY_PROC(STRINGIFY_FORMAT_INVALID);
        return error;
    }

    if (!type)
        return igloo_ERROR_FAULT;

    if (!is_object_of_type(self, type))
        return igloo_ERROR_TYPEMM;

    full = is_object_full(self);

    if (!stub->refc) {
        STRINGIFY_PROC(STRINGIFY_FORMAT_WEAK);
        return error;
    }

    igloo_ro_rwlock(self, full);

    if (!is_object_of_type_strong(self, type)) {
        igloo_ro_unlock(self, full);
        return igloo_ERROR_GONE;
    }

    if (flags & (igloo_RO_SY_CONTENT|igloo_RO_SY_DEBUG)) {
        do {
            if (type->type_stringifycb) {
                impl = type->type_stringifycb;
                break;
            }
        } while (type != type->type_parent && (type = type->type_parent));
    }

    if ((flags & (igloo_RO_SY_CONTENT|igloo_RO_SY_DEBUG)) && impl) {
        error = impl(self, result, flags);
    }

    if (error != igloo_ERROR_NONE && (flags & igloo_RO_SY_OBJECT)) {
        STRINGIFY_PROC(STRINGIFY_FORMAT_STRONG);
    }

    igloo_ro_unlock(self, full);

    return error;
}

bool            igloo_ro_can_raw(igloo_ro_t self, igloo_feature_t *feature, const igloo_ro_type_t *type)
{
    const igloo_ro_type_t *cur;
    bool full;
    bool supported = false;

    if (!is_object_of_type(self, type) || !feature || !type)
        return false;

    full = is_object_full(self);
    igloo_ro_rlock(self, full);

    if (!is_object_of_type_strong(self, type)) {
        igloo_ro_unlock(self, full);
        return false;
    }

    cur = type;
    do {
        if (cur->type_features && cur->type_features_length) {
            size_t i;
            for (i = 0; !supported && i < cur->type_features_length; i++) {
                igloo_feature_t **t = cur->type_features[i];
                if (!t || !*t)
                    break;
                supported = igloo_feature_equal(feature, *t);
            }
        }
    } while (!supported && cur != cur->type_parent && (cur = cur->type_parent));

    if (!supported) {
        cur = type;
        do {
            if (cur->type_cancb) {
                igloo_error_t error = cur->type_cancb(self, &supported, feature);
                if (error != igloo_ERROR_NONE)
                    supported = false;
            }
        } while (!supported && cur != cur->type_parent && (cur = cur->type_parent));
    }

    igloo_ro_unlock(self, full);

    return supported;
}

/* ---[ Special functions from private.h ]--- */
static igloo_error_t igloo_ro_bootstrap_init_instance(igloo_ro_t instance, const igloo_ro_type_t *type, ...)
{
    igloo_error_t error;
    va_list ap;

    va_start(ap, type);
    error = type->type_newcb(instance, type, ap);
    va_end(ap);

    return error;
}
igloo_error_t igloo_ro_bootstrap(igloo_ro_t *instance, const igloo_ro_type_t *type)
{
    igloo_ro_object_group_t *group;
    igloo_error_t error;

    if (!instance)
        return igloo_ERROR_FAULT;

    error = check_type(type);
    if (error != igloo_ERROR_NONE)
        return error;

    error = igloo_ro_new__raw_checked((igloo_ro_t*)&group, igloo_RO_GET_TYPE_BY_SYMBOL(igloo_ro_object_group_t), igloo_RO_NULL);
    if (error != igloo_ERROR_NONE)
        return error;

    igloo_RO__GETSTUB(group)->group = group;
    group->is_default_group = true;

    error = igloo_ro_new__raw_checked(instance, type, group);
    if (error != igloo_ERROR_NONE) {
        igloo_ro_unref(&group);
        return error;
    }

    error = igloo_ro_weak_ref_transparent(*instance, &(group->instance));
    if (error != igloo_ERROR_NONE) {
        igloo_ro_unref(&group);
        igloo_ro_unref(instance);
        return error;
    }

    igloo_ro_unref(&group);

    error = igloo_ro_bootstrap_init_instance(*instance, type);

    if (error != igloo_ERROR_NONE)
        igloo_ro_unref(instance);

    return error;
}

igloo_ro_t igloo_ro_get_instance_unsafe(igloo_ro_t self, const igloo_ro_type_t *type)
{
    igloo_ro_t ret;

    if (!is_valid_object(self))
        return igloo_RO_NULL;

    ret = igloo_RO_TO_TYPE_raw(self, type);
    if (!igloo_ro_is_null(ret))
        return ret;

    if (!igloo_RO__GETSTUB(self)->group)
        return igloo_RO_NULL;

    return igloo_RO_TO_TYPE_raw(igloo_RO__GETSTUB(self)->group->instance, type);
}
