/* Copyright (C) 2018       Marvin Scholz <epirat07@gmail.com>
 * 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.
 */

#ifndef _LIBIGLOO__TYPEDEF_H_
#define _LIBIGLOO__TYPEDEF_H_

/* This header includes only macros needed to define types.
 * This header must be included before "types.h" and "ro.h" if used.
 */

#ifdef __cplusplus
extern "C" {
#endif

#define igloo_CONTROL_VERSION_BUILD(ver)    (((ver) << 1) | 1)
#define igloo_CONTROL_TYPE_BUILD(type)      (((type) << 2) | 3)
#define igloo_CONTROL_INIT3(version, size, type)    {.control_version = igloo_CONTROL_VERSION_BUILD(0), .control_length = sizeof(igloo_control_t), \
                                                     .type_version = (version), .type_length = (size), .type_type = (type)}
#define igloo_CONTROL_CHECK3(ctl, version, size, type)  ((ctl).control_version == igloo_CONTROL_VERSION_BUILD(0) && (ctl).control_length == sizeof(igloo_control_t) && \
                                                     (ctl).type_version == (version) && (ctl).type_length == (size) && (ctl).type_type == (type))

/* ---[ PRIVATE ]--- */
/*
 * Those declarations are defined here as they must be known to the compiler.
 * Nobody should ever try to access them directly.
 */
#define igloo_CONTROL_TYPE_BUILD_INTERNAL(type) (((type) << 2) | 1)

#define igloo_CONTROL_TYPE__RO_TYPE             igloo_CONTROL_TYPE_BUILD_INTERNAL(1)
#define igloo_CONTROL_TYPE__FEATURE             igloo_CONTROL_TYPE_BUILD_INTERNAL(2)
#define igloo_CONTROL_TYPE__ERROR_DESC          igloo_CONTROL_TYPE_BUILD_INTERNAL(3)

typedef struct igloo_ro_type_tag igloo_ro_type_t;
#define igloo_RO_TYPE(type)                     type * subtype__ ## type;
#define igloo_RO_GET_TYPE_BY_SYMBOL(type)       (igloo_ro__type__ ## type)
#define igloo_RO_GET_OWN_TYPE_BY_SYMBOL(type)   (&(igloo_ro__typedef__ ## type))

#define igloo_RO__CONTROL_VERSION   igloo_CONTROL_VERSION_BUILD(1)
#define igloo_RO__DEFINE_TYPE(type, suffix, preattr, parent, ...) \
preattr const igloo_ro_type_t igloo_ro__typedef__ ## type = \
{ \
    .control = igloo_CONTROL_INIT3(igloo_RO__CONTROL_VERSION, sizeof(igloo_ro_type_t), igloo_CONTROL_TYPE__RO_TYPE), \
    .type_length = sizeof(type), \
    .type_name = # type suffix, \
    .type_parent = &igloo_ro__typedef__ ## parent \
    , ## __VA_ARGS__ \
}
/* ---[ END PRIVATE ]--- */

#define igloo_RO_TYPEFLAG_IMMUTABLE                 0x10

#define igloo_RO_FORWARD_TYPE(type)                 extern const igloo_ro_type_t *igloo_ro__type__ ## type
#define igloo_RO_FORWARD_OPEN_TYPE(type)            extern const igloo_ro_type_t igloo_ro__typedef__ ## type; igloo_RO_FORWARD_TYPE(type)
#define igloo_RO_OPEN_TYPE(type, parent, ...)       igloo_RO__DEFINE_TYPE(type, "", , parent, ## __VA_ARGS__); const igloo_ro_type_t * igloo_ro__type__ ## type = igloo_RO_GET_OWN_TYPE_BY_SYMBOL(type)
#define igloo_RO_PUBLIC_TYPE(type, parent, ...)     igloo_RO__DEFINE_TYPE(type, "", static, parent, ## __VA_ARGS__); const igloo_ro_type_t * igloo_ro__type__ ## type = igloo_RO_GET_OWN_TYPE_BY_SYMBOL(type)
#define igloo_RO_PRIVATE_TYPE(type, parent, ...)    igloo_RO__DEFINE_TYPE(type, " (private)", static, parent, ## __VA_ARGS__); static const igloo_ro_type_t * igloo_ro__type__ ## type = igloo_RO_GET_OWN_TYPE_BY_SYMBOL(type)
#define igloo_RO_ALIAS_TYPE(type, target)           const igloo_ro_type_t * igloo_ro__type__ ## type = igloo_RO_GET_OWN_TYPE_BY_SYMBOL(target)
#define igloo_RO_TYPEDECL_FREE(cb)                  .type_freecb = (cb)
#define igloo_RO_TYPEDECL_NEW(cb)                   .type_newcb = (cb)
#define igloo_RO_TYPEDECL_NEW_NOOP()                .type_newcb = igloo_ro_new__return_zero
#define igloo_RO_TYPEDECL_GET_ERROR(cb)             .type_get_errorcb = (cb)
#define igloo_RO_TYPEDECL_STRINGIFY(cb)             .type_stringifycb = (cb)
#define igloo_RO_TYPEDECL_FEATURES(list)            .type_features = (list), .type_features_length = (sizeof((list))/sizeof(*(list)))
#define igloo_RO_TYPEDECL_FLAGS(flags)              .type_flags = ((const void*)(flags) + 0x01)

#ifdef __cplusplus
}
#endif

#endif /* ! _LIBIGLOO__TYPEDEF_H_ */
