/*
 * lease_table.cpp
 *
 *  Created on: 7.8.2012
 *      Author: Petr Kramolis
 */
// This program is free software; you can redistribute it and/or
// modify it under the terms of the BUT OPEN SOURCE LICENCE
// Version 1 as published by the Brno University of Technology, Antonínská 548/1,
// 601 90, Czech Republic.
// 
// 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
// BUT OPEN SOURCE LICENCE (BUTOSL) for more details.
// 
// You should have received a copy of the BUT OPEN SOURCE LICENCE (BUTOSL)
// along with this program; if not, write to the Brno University of Technology,
// Antonínská 548/1, 601 90, Czech Republic.



#include <list>
#include "lease_table.h"
#include "dhcpv6.h"
#include <stdio.h>
#include "ndwatch/ipv6.h"
#include <errno.h>
#include "iricore_socket.h"

sem_t sem;
sem_t sem_list;


void *timer_run(void *args_void){
	struct timer_args *args = (struct timer_args *)args_void;
	struct timespec ts;
	int s;
	uint64_t seconds;
	ts.tv_sec = time(NULL) + 240;
	ts.tv_nsec = 0;
	while(1){
		sleep(2);
		s = sem_timedwait(&sem, &ts);
		if(s == 0 || (s == -1 && errno == ETIMEDOUT)){
			seconds = args->table->check_lease(args->usocket);
			if(seconds != 0){
				ts.tv_sec = seconds + 1;
				continue;
			}
		}
		ts.tv_sec = time(NULL) + 240;
	}
	return NULL;
}

bool Lease_table_item::operator<(const Lease_table_item& right) const
{
	return (_timestamp < right._timestamp); //reverse!
}

bool Lease_table_item::operator==(const Lease_table_item& right) const
{
	return (_ident_line == right._ident_line); //reverse!
}

void Lease_table_item::append(std::string type, std::string value){
	_ident_line += "('" + type +"','"+ value +"'),";

	if(_ident_line.find("IPv4") != string::npos){
		_IP = true;
	}else if (_ident_line.find("IPv6") != string::npos ){
		_IP = true;
	}
}


void Lease_table_item::append(std::string ident_line){
	_ident_line += ident_line;
	if(_ident_line.find("IPv4") != string::npos){
		_IP = true;
	}else if (_ident_line.find("IPv6") != string::npos ){
		_IP = true;
	}
}

void Lease_Table::remove( Lease_table_item &item, unix_socket * usocket)
{
	unsigned int list_size;
	list_size = _list.size();
	_list.remove(item);
	if( list_size == _list.size()){
		//there is no record for this item... report BEGIN
		usocket->send_report(item , time(NULL), "BEGIN", "detected IP address assignment but time of assignment is unknown");
	}
	usocket->send_report(item , time(NULL), "END", "IP address unassigned");
}


void Lease_Table::update( Lease_table_item &item, unix_socket * usocket)
{
	unsigned int size;
	size = _list.size();

	_list.remove((const Lease_table_item) item);

	if(size != _list.size()){ // mac -> ip pair was in list
		usocket->send_report(item , time(NULL), "CONTINUE", "client renewed his IP address assignment");
	} else {
		usocket->send_report(item , time(NULL), "BEGIN", "new IP address assigned to client");
	}

	sem_wait(&sem_list);
	_list.push_back(item);
	_list.sort();
	sem_post(&sem_list);

	//let timer check list
	sem_post(&sem);
}


uint64_t Lease_Table::check_lease(unix_socket * usocket){
	int64_t t = 0;
	sem_wait(&sem_list);
	while(1){
		if(_list.size() == 0){
			sem_post(&sem_list);
			return 0;
		}
		t = _list.front().timestamp();
		if(time(NULL) >= t){
			usocket->send_report(_list.front(),t,"END", "IP address assignment time-outed");
			_list.pop_front();
		} else {
			sem_post(&sem_list);
			return t;
		}
	}
	sem_post(&sem_list);
	return t;
}
