/*
    This file is part of Kismet

    Kismet 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.

    Kismet 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 Kismet; if not, write to the Free Software
    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
*/

#include "config.h"

#include "alertracker.h"
#include "server_globals.h"
#include "kismet_server.h"

Alertracker::Alertracker() {
    next_alert_id = 0;
    max_backlog = 0;
    server = NULL;
    protoref = -1;
}

Alertracker::~Alertracker() {
    for (map<int, alert_rec *>::iterator x = alert_ref_map.begin();
         x != alert_ref_map.end(); ++x)
        delete x->second;
}

void Alertracker::AddTcpServer(TcpServer *in_server) {
    server = in_server;
}

void Alertracker::AddAlertProtoRef(int in_ref) {
    protoref = in_ref;
}

void Alertracker::SetAlertBacklog(int in_max) {
    max_backlog = in_max;
}

int Alertracker::RegisterAlert(const char *in_header, alert_time_unit in_unit, 
							   int in_rate, alert_time_unit in_burstunit,
                               int in_burst) {

    // Bail if this header is registered
    if (alert_name_map.find(in_header) != alert_name_map.end())
        return -1;

	if (in_burstunit > in_unit)
		return -1;

    alert_rec *arec = new alert_rec;

    arec->ref_index = next_alert_id++;
    arec->header = in_header;
    arec->limit_unit = in_unit;
    arec->limit_rate = in_rate;
	arec->burst_unit = in_burstunit;
    arec->limit_burst = in_burst;
    arec->burst_sent = 0;
	arec->time_last = 0;

    alert_name_map[arec->header] = arec->ref_index;
    alert_ref_map[arec->ref_index] = arec;

    return arec->ref_index;
}

int Alertracker::FetchAlertRef(string in_header) {
    if (alert_name_map.find(in_header) != alert_name_map.end())
        return alert_name_map[in_header];

    return -1;
}

int Alertracker::CheckTimes(alert_rec *arec) {
    // Is this alert rate-limited?  If not, shortcut out and send it
    if (arec->limit_rate == 0) {
        return 1;
    }

    struct timeval now;
    gettimeofday(&now, NULL);

	// If the last time we sent anything was longer than the main rate limit,
	// then we reset back to empty
	if (arec->time_last < (now.tv_sec - alert_time_unit_conv[arec->limit_unit])) {
		arec->total_sent = 0;
		arec->burst_sent = 0;
		return 1;
	}

	// If the last time we sent anything was longer than the burst rate, we can
	// reset the burst to 0
	if (arec->time_last < (now.tv_sec - alert_time_unit_conv[arec->burst_unit])) {
		arec->burst_sent = 0;
	}

	// If we're under the limit on both, we're good to go
	if (arec->burst_sent < arec->limit_burst && arec->total_sent < arec->limit_rate) {
		return 1;
	}

    return 0;
}

int Alertracker::PotentialAlert(int in_ref) {
    map<int, alert_rec *>::iterator aritr = alert_ref_map.find(in_ref);

    if (aritr == alert_ref_map.end())
        return -1;

    return CheckTimes(aritr->second);
}

int Alertracker::RaiseAlert(int in_ref, mac_addr bssid, mac_addr source, 
							mac_addr dest, mac_addr other,
							int in_channel, string in_text) {
    map<int, alert_rec *>::iterator aritr = alert_ref_map.find(in_ref);

    if (aritr == alert_ref_map.end())
        return -1;

    alert_rec *arec = aritr->second;

    if (CheckTimes(arec) != 1)
        return 0;

    ALERT_data *adata = new ALERT_data;

    char tmpstr[128];
    timeval *ts = new timeval;
    gettimeofday(ts, NULL);

    snprintf(tmpstr, 128, "%ld", (long int) ts->tv_sec);
    adata->sec = tmpstr;

    snprintf(tmpstr, 128, "%ld", (long int) ts->tv_usec);
    adata->usec = tmpstr;

    snprintf(tmpstr, 128, "%d", in_channel);
    adata->channel = tmpstr;

    adata->text = in_text;
    adata->header = arec->header;
    adata->bssid = bssid.Mac2String();
    adata->source = source.Mac2String();
    adata->dest  = dest.Mac2String();
    adata->other = other.Mac2String();

	// Increment and set the timers
    arec->burst_sent++;
	arec->total_sent++;
	arec->time_last = time(0);

    alert_backlog.push_back(adata);
    if (alert_backlog.size() > max_backlog) {
        delete alert_backlog[0];
        alert_backlog.erase(alert_backlog.begin());
    }

    server->SendToAll(protoref, (void *) adata);

    // Hook main for sounds and whatnot on the server
    KisLocalAlert(in_text.c_str());

    return 1;
}

void Alertracker::BlitBacklogged(int in_fd) {
    for (unsigned int x = 0; x < alert_backlog.size(); x++)
        server->SendToClient(in_fd, protoref, (void *) alert_backlog[x]);
}
