/*                                                                              *
 *   This file is part of the ESO VISIR package                                 *
 *   Copyright (C) 2004,2005 European Southern Observatory                      *
 *                                                                              *
 *   This library is free software; you can redistribute it and/or modify       *
 *   it under the terms of the GNU General Public License as published by       *
 *   the Free Software Foundation; either version 2 of the License, or          *
 *   (at your option) any later version.                                        *
 *                                                                              *
 *   This program 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 General Public License for more details.                               *
 *                                                                              *
 *   You should have received a copy of the GNU General Public License          *
 *   along with this program; if not, write to the Free Software                *
 *   Foundation, 51 Franklin St, Fifth Floor, Boston, MA  02111-1307  USA       *
 *                                                                              */

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

/*-----------------------------------------------------------------------------
                                Includes
 -----------------------------------------------------------------------------*/

#include "../visir/visir_queue.c"
#include <cpl.h>
#include <stdint.h>

/*----------------------------------------------------------------------------*/
/**
 * @defgroup visir_utils_test Testing of the VISIR utilities
 */
/*----------------------------------------------------------------------------*/

#define CONCAT(a,b) a ## _ ## b
#define CONCAT2X(a,b) CONCAT(a,b)

/*----------------------------------------------------------------------------*/
/**
   @brief   Unit tests of utils module
**/
/*----------------------------------------------------------------------------*/

int main(void)
{

    cpl_test_init(PACKAGE_BUGREPORT, CPL_MSG_WARNING);
    FILE          * stream;

    stream = cpl_msg_get_level() > CPL_MSG_INFO
        ? fopen("/dev/null", "a") : stdout;

#if defined _OPENMP && _OPENMP >= 200805
    {
        visir_queue * q = visir_queue_init(-1);
        cpl_test_eq(q->max_size, INT_MAX);
        cpl_test_eq(q->size, 0);
        visir_queue_delete(q);
    }

    {
        const int N = 1000;
        visir_queue * q = visir_queue_init(-1);
        for (int i = 0; i < N; i++)
            visir_queue_put(q, (void*)(intptr_t)i);
        cpl_test_eq(q->size, N);
        for (int i = 0; i < N; i++)
            cpl_test_eq((intptr_t)visir_queue_pop(q), (intptr_t)i);
        cpl_test_eq(q->size, 0);
        visir_queue_delete(q);
    }
    {
        const int N = 100000;
        visir_queue * q = visir_queue_init(-1);
        omp_set_num_threads(10);
#pragma omp parallel for
        for (int i = 0; i < N; i++)
            visir_queue_put(q, (void*)(intptr_t)i);
        cpl_test_eq(q->size, N);
#pragma omp parallel for
        for (int i = 0; i < N; i++)
            visir_queue_pop(q);
        cpl_test_eq(q->size, 0);
        visir_queue_delete(q);
    }
    {
        const int N = 1000;
        visir_queue * q = visir_queue_init(5);
        omp_set_num_threads(10);
#pragma omp parallel
#pragma omp sections
        {
#pragma omp section
            {
                for (int i = 0; i < N; i++) {
                    visir_queue_put(q, (void*)55);
                    cpl_test_leq(q->size, 5);
                }

            }
#pragma omp section
            {
                for (int i = 0; i < N; i++) {
                    cpl_test_eq((intptr_t)visir_queue_pop(q), 55);
                    cpl_test_leq(0, q->size);
                }
            }
        }

        cpl_test_eq(q->size, 0);
        visir_queue_delete(q);
    }

    /* test that queue does not block when an error was set */
    {
        const int N = 1000;
        visir_queue * q = visir_queue_init(5);
        omp_set_num_threads(10);
#pragma omp parallel
#pragma omp sections
        {
#pragma omp section
            {
                for (int i = 0; i < N; i++) {
                    visir_queue_put(q, (void*)55);
                    cpl_test_leq(q->size, 5);
                }

            }
#pragma omp section
            {
                for (int i = 0; i < N / 10; i++) {
                    cpl_test_eq((intptr_t)visir_queue_pop(q), 55);
                    cpl_test_leq(0, q->size);
                }
                visir_queue_set_error(q, CPL_ERROR_ILLEGAL_INPUT);
            }
        }

        visir_queue_delete(q);
    }
#endif

    if (stream != stdout) cpl_test_zero( fclose(stream) );
    return cpl_test_end(0);
}
