/* Copyright (C) 2021       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/tap.h>
#include <igloo/igloo.h>
#include <igloo/error.h>
#include <igloo/ro.h>

static igloo_ro_t g_instance;

static void group_create_object(void)
{
    igloo_ro_stub_t *stub;
    igloo_ro_tiny_t *tiny;
    igloo_ro_full_t *full; 

    igloo_tap_test_success("igloo_ro_new igloo_ro_stub_t", igloo_ro_new(&stub, igloo_ro_stub_t, g_instance));
    igloo_tap_dump_ro_variable(stub);
    igloo_tap_test_success("unref", igloo_ro_unref(&stub));

    igloo_tap_test_success("igloo_ro_new igloo_ro_tiny_t", igloo_ro_new(&tiny, igloo_ro_tiny_t, g_instance));
    igloo_tap_dump_ro_variable(tiny);
    igloo_tap_test_success("unref", igloo_ro_unref(&tiny));

    igloo_tap_test_success("igloo_ro_new igloo_ro_full_t", igloo_ro_new(&full, igloo_ro_full_t, g_instance));
    igloo_tap_dump_ro_variable(full);
    igloo_tap_test_success("unref", igloo_ro_unref(&full));
}

static void group_refcounting(void)
{
    igloo_ro_tiny_t *tiny;
    igloo_ro_tiny_t *extra_ref;

    igloo_tap_test_success("igloo_ro_new igloo_ro_tiny_t", igloo_ro_new(&tiny, igloo_ro_tiny_t, g_instance));
    igloo_tap_dump_ro_variable(tiny);
    igloo_tap_test_success("igloo_ro_ref", igloo_ro_ref(tiny, &extra_ref, igloo_ro_tiny_t));
    igloo_tap_dump_ro_variable(extra_ref);
    igloo_tap_test_success("igloo_ro_unref extra", igloo_ro_unref(&extra_ref));
    igloo_tap_dump_ro_variable(tiny);
    igloo_tap_test_success("igloo_ro_ref", igloo_ro_ref(tiny, &extra_ref, igloo_ro_tiny_t));
    igloo_tap_test_success("unref", igloo_ro_unref(&tiny));
    igloo_tap_dump_ro_variable(extra_ref);
    igloo_tap_test_success("igloo_ro_unref extra", igloo_ro_unref(&extra_ref));
}

static void test_stringify_real(igloo_ro_stub_t *stub, igloo_ro_sy_t flags, const char *flags_str, igloo_error_t expected)
{
    char buf[64];
    char *res = NULL;
    igloo_error_t error = igloo_ro_stringify(stub, &res, flags);

    snprintf(buf, sizeof(buf), "stub=%p, flags=0x%x (%s)", stub, (unsigned int)flags, flags_str);
    igloo_tap_diagnostic(buf);
    igloo_tap_diagnostic(res);

    igloo_tap_test_error("correct error", expected, error);

    if (expected == igloo_ERROR_NONE) {
        igloo_tap_test("result is non-NULL", res != NULL);
    } else {
        igloo_tap_test("result is NULL", res == NULL);
    }

    free(res);
}
#define test_stringify(stub, flags, expected) test_stringify_real((stub), (flags), (# flags), (expected))

static void group_stringify(void)
{
    char buffer[64];
    igloo_ro_stub_t *stub_strong;
    igloo_ro_stub_t *stub_weak;
    igloo_ro_stub_t *invalid = (void*)&(buffer[1]);

    memset(buffer, 0, sizeof(buffer));

    igloo_tap_test_success("igloo_ro_new igloo_ro_stub_t", igloo_ro_new(&stub_strong, igloo_ro_stub_t, g_instance));
    igloo_tap_dump_ro_variable(stub_strong);
    test_stringify(stub_strong, igloo_RO_SY_DEFAULT, igloo_ERROR_NONE);
    test_stringify(stub_strong, igloo_RO_SY_OBJECT, igloo_ERROR_NONE);
    test_stringify(stub_strong, igloo_RO_SY_CONTENT, igloo_ERROR_NOSYS);
    test_stringify(stub_strong, igloo_RO_SY_DEBUG, igloo_ERROR_NOSYS);
    igloo_tap_test_success("weak_ref", igloo_ro_weak_ref(stub_strong, &stub_weak, igloo_ro_stub_t));
    igloo_tap_test_success("unref", igloo_ro_unref(&stub_strong));
    igloo_tap_dump_ro_variable(stub_strong);
    igloo_tap_dump_ro_variable(stub_weak);
    test_stringify(stub_weak, igloo_RO_SY_DEFAULT, igloo_ERROR_NONE);
    test_stringify(stub_weak, igloo_RO_SY_OBJECT, igloo_ERROR_NONE);
    test_stringify(stub_weak, igloo_RO_SY_CONTENT, igloo_ERROR_GONE);
    test_stringify(stub_weak, igloo_RO_SY_DEBUG, igloo_ERROR_GONE);
    igloo_tap_test_success("weak_unref", igloo_ro_weak_unref(&stub_weak));
    igloo_tap_dump_ro_variable(stub_weak);

    test_stringify(invalid, igloo_RO_SY_DEFAULT, igloo_ERROR_NONE);
    test_stringify(invalid, igloo_RO_SY_OBJECT, igloo_ERROR_NONE);
    test_stringify(invalid, igloo_RO_SY_CONTENT, igloo_ERROR_FAULT);
    test_stringify(invalid, igloo_RO_SY_DEBUG, igloo_ERROR_FAULT);

    test_stringify(NULL, igloo_RO_SY_DEFAULT, igloo_ERROR_NONE);
    test_stringify(NULL, igloo_RO_SY_OBJECT, igloo_ERROR_NONE);
    test_stringify(NULL, igloo_RO_SY_CONTENT, igloo_ERROR_FAULT);
    test_stringify(NULL, igloo_RO_SY_DEBUG, igloo_ERROR_FAULT);
}

int main(void)
{

    igloo_tap_init();
    igloo_tap_exit_on(igloo_TAP_EXIT_ON_FIN, NULL);
    igloo_tap_test_success("igloo_initialize", igloo_initialize(&g_instance));
    igloo_tap_group_run("create_object", group_create_object);
    igloo_tap_group_run("refcounting", group_refcounting);
    igloo_tap_group_run("stringify", group_stringify);
    igloo_tap_test_success("unref instance", igloo_ro_unref(&g_instance));
    igloo_tap_fin();

    return EXIT_FAILURE; // return failure as we should never reach this point!
}
