#pragma once

/*
 * Odyssey.
 *
 * Scalable PostgreSQL connection pooler.
 */

#include <types.h>
#include <list.h>
#include <server.h>
#include <od_ldap.h>

typedef int (*od_server_pool_cb_t)(od_server_t *, void **);

struct od_server_pool {
	od_list_t active;
	od_list_t idle;
	int count_active;
	int count_idle;
};

static inline void od_server_pool_init(od_server_pool_t *pool)
{
	pool->count_active = 0;
	pool->count_idle = 0;
	od_list_init(&pool->idle);
	od_list_init(&pool->active);
}

#define OD_SERVER_POOL_FREE_DECLARE(name, type, server_free_cb)  \
	static inline void od_##name##_server_pool_free(         \
		od_server_pool_t *pool)                          \
	{                                                        \
		type *server;                                    \
		od_list_t *i, *n;                                \
                                                                 \
		od_list_foreach_safe (&pool->idle, i, n) {       \
			server = od_container_of(i, type, link); \
			server_free_cb(server);                  \
		}                                                \
                                                                 \
		od_list_foreach_safe (&pool->active, i, n) {     \
			server = od_container_of(i, type, link); \
			server_free_cb(server);                  \
		}                                                \
	}

OD_SERVER_POOL_FREE_DECLARE(pg, od_server_t, od_server_free)

#ifdef LDAP_FOUND
OD_SERVER_POOL_FREE_DECLARE(ldap, od_ldap_server_t, od_ldap_server_free)
#endif

#define OD_SERVER_POOL_SET_DECLARE(name, type)                                 \
	static inline void od_##name##_server_pool_set(                        \
		od_server_pool_t *pool, type *server, od_server_state_t state) \
	{                                                                      \
		if (server->state == state)                                    \
			return;                                                \
		switch (server->state) {                                       \
		case OD_SERVER_UNDEF:                                          \
			break;                                                 \
		case OD_SERVER_IDLE:                                           \
			pool->count_idle--;                                    \
			break;                                                 \
		case OD_SERVER_ACTIVE:                                         \
			pool->count_active--;                                  \
			break;                                                 \
		}                                                              \
                                                                               \
		od_list_t *target = NULL;                                      \
		switch (state) {                                               \
		case OD_SERVER_UNDEF:                                          \
			break;                                                 \
		case OD_SERVER_IDLE:                                           \
			target = &pool->idle;                                  \
			pool->count_idle++;                                    \
			break;                                                 \
		case OD_SERVER_ACTIVE:                                         \
			target = &pool->active;                                \
			pool->count_active++;                                  \
			break;                                                 \
		}                                                              \
                                                                               \
		od_list_unlink(&server->link);                                 \
		od_list_init(&server->link);                                   \
		if (target) {                                                  \
			od_list_push(target, &server->link);                   \
		}                                                              \
		server->state = state;                                         \
	}

OD_SERVER_POOL_SET_DECLARE(pg, od_server_t)

#ifdef LDAP_FOUND
OD_SERVER_POOL_SET_DECLARE(ldap, od_ldap_server_t)
#endif

#define OD_SERVER_POOL_NEXT_DECLARE(name, type)                     \
	static inline type *od_##name##_server_pool_next(           \
		od_server_pool_t *pool, od_server_state_t state)    \
	{                                                           \
		int target_count = 0;                               \
		od_list_t *target = NULL;                           \
		switch (state) {                                    \
		case OD_SERVER_IDLE:                                \
			target_count = pool->count_idle;            \
			target = &pool->idle;                       \
			break;                                      \
		case OD_SERVER_ACTIVE:                              \
			target_count = pool->count_active;          \
			target = &pool->active;                     \
			break;                                      \
		case OD_SERVER_UNDEF:                               \
			assert(0);                                  \
			break;                                      \
		}                                                   \
		if (target_count == 0)                              \
			return NULL;                                \
		type *server;                                       \
		server = od_container_of(target->next, type, link); \
		return server;                                      \
	}

OD_SERVER_POOL_NEXT_DECLARE(pg, od_server_t)

#ifdef LDAP_FOUND
OD_SERVER_POOL_NEXT_DECLARE(ldap, od_ldap_server_t)
#endif

static inline od_server_t *od_server_pool_foreach(od_server_pool_t *pool,
						  od_server_state_t state,
						  od_server_pool_cb_t callback,
						  void **argv)
{
	od_list_t *target = NULL;
	switch (state) {
	case OD_SERVER_IDLE:
		target = &pool->idle;
		break;
	case OD_SERVER_ACTIVE:
		target = &pool->active;
		break;
	case OD_SERVER_UNDEF:
		assert(0);
		break;
	}
	od_server_t *server;
	od_list_t *i, *n;
	od_list_foreach_safe (target, i, n) {
		server = od_container_of(i, od_server_t, link);
		int rc;
		rc = callback(server, argv);
		if (rc) {
			return server;
		}
	}
	return NULL;
}

static inline int od_server_pool_idle(od_server_pool_t *pool)
{
	return pool->count_idle;
}

static inline int od_server_pool_active(od_server_pool_t *pool)
{
	return pool->count_active;
}

static inline int od_server_pool_total(od_server_pool_t *pool)
{
	return pool->count_active + pool->count_idle;
}
