/* sp-ul_db module
 *
 * Copyright (C) 2007 1&1 Internet AG
 *
 * This file is part of Kamailio, a free SIP server.
 *
 * Kamailio 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
 *
 * Kamailio 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, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
 */

#include <inttypes.h>
#include <stdint.h>

#include "../../core/mem/shm_mem.h"
#include "ul_check.h"
#include "time.h"

static struct check_list_head *head = NULL;

static struct check_list_element *initialise_element(void);

static void destroy_element(struct check_list_element *element);

int init_list(void)
{
	if(!head) {
		if((head = (struct check_list_head *)shm_malloc(
					sizeof(struct check_list_head)))
				== NULL) {
			LM_ERR("couldn't allocate shared memory.\n");
			return -1;
		}
	}
	memset(head, 0, sizeof(struct check_list_head));

	if(lock_init(&head->list_lock) == 0) {
		LM_ERR("cannot initialise lock.\n");
		shm_free(head);
		return -1;
	}
	return 0;
}

struct check_data *get_new_element(void)
{
	struct check_list_element *ret;
	if(!head) {
		LM_ERR("list not initialised.\n");
		return NULL;
	}
	LM_DBG("start.\n");
	lock_get(&head->list_lock);

	if((ret = initialise_element()) == NULL) {
		lock_release(&head->list_lock);
		return NULL;
	}
	head->element_count++;
	if(head->first == NULL) {
		LM_DBG("new element is the first.\n");
		LM_DBG("element_count: %i\n", head->element_count);
		head->first = ret;
		lock_release(&head->list_lock);
		return ret->data;
	}
	LM_DBG("new element.\n");
	LM_DBG("element_count: %i\n", head->element_count);
	ret->next = head->first;
	head->first = ret;
	lock_release(&head->list_lock);
	return ret->data;
}

int must_refresh(struct check_data *element)
{
	int ret;
	lock_get(&element->flag_lock);
	ret = element->refresh_flag;
	LM_DBG("refresh_flag is at %i.\n", ret);
	element->refresh_flag = 0;
	lock_release(&element->flag_lock);
	return ret;
}

int must_reconnect(struct check_data *element)
{
	int ret;
	lock_get(&element->flag_lock);
	ret = element->reconnect_flag;
	LM_DBG("reconnect_flag is at %i.\n", ret);
	element->reconnect_flag = 0;
	lock_release(&element->flag_lock);
	return ret;
}

int set_must_refresh(void)
{
	struct check_list_element *tmp;
	int i = 0;
	lock_get(&head->list_lock);
	tmp = head->first;
	while(tmp) {
		lock_get(&tmp->data->flag_lock);
		tmp->data->refresh_flag = 1;
		lock_release(&tmp->data->flag_lock);
		tmp = tmp->next;
		i++;
		LM_DBG("element no %i.\n", i);
	}
	lock_release(&head->list_lock);
	return i;
}

int set_must_reconnect(void)
{
	struct check_list_element *tmp;
	int i = 0;
	lock_get(&head->list_lock);
	tmp = head->first;
	while(tmp) {
		lock_get(&tmp->data->flag_lock);
		tmp->data->reconnect_flag = 1;
		lock_release(&tmp->data->flag_lock);
		tmp = tmp->next;
		i++;
		LM_DBG("element no %i.\n", i);
	}
	lock_release(&head->list_lock);
	return i;
}


int must_retry(time_t *timer, time_t interval)
{
	if(!timer) {
		return -1;
	}
	LM_DBG("must_retry: time is at %" PRIu64 ", retry at %" PRIu64 ".\n",
			(uint64_t)time(NULL), (uint64_t)(*timer));
	if(*timer <= time(NULL)) {
		*timer = time(NULL) + interval;
		return 1;
	}
	return 0;
}

void destroy_list(void)
{
	struct check_list_element *tmp;
	struct check_list_element *del;
	if(head) {
		tmp = head->first;
		while(tmp) {
			del = tmp;
			tmp = tmp->next;
			destroy_element(del);
		}
		lock_destroy(&head->list_lock);
		shm_free(head);
	}
	return;
}

static struct check_list_element *initialise_element(void)
{
	struct check_list_element *ret;
	if((ret = (struct check_list_element *)shm_malloc(
				sizeof(struct check_list_element)))
			== NULL) {
		LM_ERR("couldn't allocate shared memory.\n");
		return NULL;
	}
	memset(ret, 0, sizeof(struct check_list_element));

	if((ret->data = (struct check_data *)shm_malloc(sizeof(struct check_data)))
			== NULL) {
		LM_ERR("couldn't allocate shared memory.\n");
		shm_free(ret);
		return NULL;
	}
	memset(ret->data, 0, sizeof(struct check_data));

	if(lock_init(&ret->data->flag_lock) == 0) {
		LM_ERR("cannot initialise flag lock.\n");
		shm_free(ret->data);
		shm_free(ret);
		return NULL;
	}
	return ret;
}

static void destroy_element(struct check_list_element *element)
{
	if(element) {
		if(element->data) {
			/*		if(element->data->flag_lock){ */
			lock_destroy(&element->data->flag_lock);
			/*			}*/
			shm_free(element->data);
		}
		shm_free(element);
	}
	return;
}
