/* Copyright (C) 2018-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

#ifdef HAVE_ERRNO_H
#include <errno.h>
#endif

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

#include <igloo/tap.h>
#include <igloo/error.h>

#define _VECTOR(error,good) {(error), #error , (good)}
struct vector {
    igloo_error_t error;
    const char *string;
    bool good;
};

static void check_description(const igloo_error_desc_t * desc, igloo_error_t error)
{
    if (desc == NULL)
        return;
    igloo_tap_test_error("desc->error == error", error, desc->error);
    igloo_tap_test("desc->name != NULL", desc->name != NULL);
    igloo_tap_test("desc->uuid != NULL", desc->uuid != NULL);
    igloo_tap_test("desc->message != NULL", desc->message != NULL);

    if (desc->name) {
        const igloo_error_desc_t * desc_match = igloo_error_getbyname(desc->name);
        igloo_tap_test("igloo_error_getbyname(desc->name) != NULL", desc_match != NULL);
        igloo_tap_test_error("inverse error == forward error", error, desc_match->error);
    }
}

static void test_get_description(void)
{
    static const struct vector vector[] = {
        _VECTOR(igloo_ERROR_NONE, true),
        _VECTOR(igloo_ERROR_GENERIC, true),
        _VECTOR(igloo_ERROR_NOENT, true),
        _VECTOR(igloo_ERRORTESTVECTOR_NX, false)
    };
    size_t i;

    for (i = 0; i < (sizeof(vector)/sizeof(*vector)); i++) {
        const igloo_error_desc_t * desc = igloo_error_get_description(vector[i].error);
        char buffer[128];

        snprintf(buffer, sizeof(buffer), "igloo_error_get_description(%s) %c= NULL", vector[i].string, vector[i].good ? '!' : '=');

        if (vector[i].good) {
            igloo_tap_test(buffer, desc != NULL);
            check_description(desc, vector[i].error);
        } else {
            igloo_tap_test(buffer, desc == NULL);
        }
    }
}

static void test_getbyname(void)
{
    static const struct vector vector[] = {
        {igloo_ERROR_NONE, "NONE", true},
        {igloo_ERROR_NONE, "ERROR_NONE", false},
        {igloo_ERROR_NONE, "igloo_ERROR_NONE", false},
        {igloo_ERRORTESTVECTOR_NX, "NONEX", false}
    };
    size_t i;

    for (i = 0; i < (sizeof(vector)/sizeof(*vector)); i++) {
        const igloo_error_desc_t * desc = igloo_error_getbyname(vector[i].string);
        char buffer[128];

        snprintf(buffer, sizeof(buffer), "igloo_error_getbyname(\"%s\") %c= NULL", vector[i].string, vector[i].good ? '!' : '=');

        if (vector[i].good) {
            igloo_tap_test(buffer, desc != NULL);
            check_description(desc, vector[i].error);
        } else {
            igloo_tap_test(buffer, desc == NULL);
        }
    }
}


#ifdef HAVE_ERRNO_H
static void test_errno(void)
{
    igloo_error_t error;

    igloo_tap_test_error("igloo_error_by_domain value=ENOENT", igloo_ERROR_NOENT, igloo_error_by_domain(igloo_ERROR_DOMAIN_ERRNO, ENOENT, &error));
    igloo_tap_test_success("igloo_error_by_domain error", error);

    errno = ENOENT;
    igloo_tap_test_error("igloo_error_from_system errno=ENOENT", igloo_ERROR_NOENT, igloo_error_from_system(&error));
    igloo_tap_test_success("igloo_error_from_system error", error);

    igloo_tap_test_success("igloo_error_clear_system", igloo_error_clear_system());
    igloo_tap_test_success("igloo_error_from_system", igloo_error_from_system(&error));
    igloo_tap_test_success("igloo_error_from_system error", error);

    errno = ENOENT;
    igloo_tap_test_error("igloo_error_from_system errno=ENOENT (retry after clear)", igloo_ERROR_NOENT, igloo_error_from_system(&error));
    igloo_tap_test_success("igloo_error_from_system error", error);
}
#endif

static void test_ra_errors(void)
{
    igloo_error_t error;

    igloo_tap_test_error("igloo_error_by_domain value=-1", igloo_ERROR_GENERIC, igloo_error_by_domain(igloo_ERROR_DOMAIN_RA_ERROR, -1, &error));
    igloo_tap_test_success("igloo_error_by_domain error", error);

    igloo_tap_test_error("igloo_error_by_domain value=0", igloo_ERROR_NONE, igloo_error_by_domain(igloo_ERROR_DOMAIN_RA_ERROR, 0, &error));
    igloo_tap_test_success("igloo_error_by_domain error", error);

    igloo_tap_test_error("igloo_error_by_domain value=-2", igloo_ERROR_GENERIC, igloo_error_by_domain(igloo_ERROR_DOMAIN_RA_ERROR, -2, &error));
    igloo_tap_test_error("igloo_error_by_domain error", igloo_ERROR_RANGE, error);

    igloo_tap_test_error("igloo_error_by_domain value=65536", igloo_ERROR_GENERIC, igloo_error_by_domain(igloo_ERROR_DOMAIN_RA_ERROR, 65536, &error));
    igloo_tap_test_error("igloo_error_by_domain error", igloo_ERROR_RANGE, error);

    igloo_tap_test_error("igloo_error_by_domain value=256", igloo_ERROR_GENERIC, igloo_error_by_domain(igloo_ERROR_DOMAIN_RA_ERROR, 256, &error));
    igloo_tap_test_error("igloo_error_by_domain error", igloo_ERROR_INVAL, error);
    igloo_tap_test_error("igloo_error_by_domain value=65535", igloo_ERROR_GENERIC, igloo_error_by_domain(igloo_ERROR_DOMAIN_RA_ERROR, 65535, &error));
    igloo_tap_test_error("igloo_error_by_domain error", igloo_ERROR_INVAL, error);
    igloo_tap_test_error("igloo_error_by_domain value=16777215", igloo_ERROR_GENERIC, igloo_error_by_domain(igloo_ERROR_DOMAIN_RA_ERROR, 16777215, &error));
    igloo_tap_test_error("igloo_error_by_domain error", igloo_ERROR_INVAL, error);
    igloo_tap_test_error("igloo_error_by_domain value=4294967295", igloo_ERROR_GENERIC, igloo_error_by_domain(igloo_ERROR_DOMAIN_RA_ERROR, 4294967295, &error));
    igloo_tap_test_error("igloo_error_by_domain error", igloo_ERROR_INVAL, error);
}

int main (void) {
    igloo_tap_init();
    igloo_tap_exit_on(igloo_TAP_EXIT_ON_FIN, NULL);
    igloo_tap_group_run("get description", test_get_description);
    igloo_tap_group_run("getbyname", test_getbyname);
#ifdef HAVE_ERRNO_H
    igloo_tap_group_run("errno", test_errno);
#endif
    igloo_tap_group_run("RoarAudio errors", test_ra_errors);
    igloo_tap_fin();

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