/*
 * WINGSetSourceRoute.{cc,hh} 
 * John Bicket, Roberto Riggio, Stefano Testi
 *
 * Copyright (c) 1999-2001 Massachusetts Institute of Technology
 * Copyright (c) 2009 CREATE-NET
 *
 * Permission is hereby granted, free of charge, to any person obtaining a
 * copy of this software and associated documentation files (the "Software"),
 * to deal in the Software without restriction, subject to the conditions
 * listed in the Click LICENSE file. These conditions include: you must
 * preserve this copyright notice, and you cannot mention the copyright
 * holders in advertising related to the Software without their permission.
 * The Software is provided WITHOUT ANY WARRANTY, EXPRESS OR IMPLIED. This
 * notice is a summary of the Click LICENSE file; the license in that file is
 * legally binding.
 */

#include <click/config.h>
#include <click/confparse.hh>
#include <click/error.hh>
#include <click/glue.hh>
#include <click/ipaddress.hh>
#include <click/straccum.hh>
#include <clicknet/ether.h>
#include "pathmulti.hh"
#include "arptablemulti.hh"
#include "wingsetsourceroute.hh"
#include "wingforwarder.hh"
CLICK_DECLS

bool
cp_node_airport(String s, NodeAirport *node)
{
	int l = s.find_left(':');
	int r = s.find_right(':');
	IPAddress ip;
	int arr, dep;
	if (!cp_integer(s.substring(0, l), &arr) ||
		!cp_ip_address(s.substring(l + 1, r - 2), &ip) ||
		!cp_integer(s.substring(r + 1, s.length()), &dep)) {
		return false;
	}
	*node = NodeAirport(ip, arr, dep);
	return true;
}

WINGSetSourceRoute::WINGSetSourceRoute() :
	_forwarder(0), _debug(false) {
}

WINGSetSourceRoute::~WINGSetSourceRoute() {
}

int WINGSetSourceRoute::configure(Vector<String> &conf, ErrorHandler *errh) {
	if (cp_va_kparse(conf, this, errh, 
				"IP", cpkM, cpIPAddress, &_ip, 
				"FWD", cpkM, cpElement, &_forwarder, 
				"DEBUG", 0, cpBool, &_debug, 
				cpEnd) < 0)
		return -1;
	if (_forwarder->cast("WINGForwarder") == 0)
		return errh->error("FWD element is not a WINGForwarder");
	return 0;
}

Packet *
WINGSetSourceRoute::simple_action(Packet *p_in) {
	IPAddress dst = p_in->dst_ip_anno();
	if (!dst) {
		click_chatter("%{element} :: %s :: got invalid dst %s", 
				this,
				__func__, 
				dst.unparse().c_str());
		p_in->kill();
		return 0;
	}
	PathMulti *p = _routes.findp(dst);
	if (p) {
		Packet *p_out = _forwarder->encap(p_in, *p);
		return p_out;
	}
	/* no route */
	checked_output_push(1, p_in);
	return 0;
}

String WINGSetSourceRoute::print_routes() {
	StringAccum sa;
	for (RouteTable::iterator iter = _routes.begin(); iter.live(); iter++) {
		sa << route_to_string(iter.value()) << "\n";
	}
	return sa.take_string();
}

String WINGSetSourceRoute::route_to_string(PathMulti p) {
	StringAccum sa;
	int hops = p.size() - 1;
	StringAccum sa2;
	for (int i = 0; i < p.size(); i++) {
		sa2 << " " << p[i].unparse();
	}
	sa << p[p.size() - 1]._ip << " hops " << hops << sa2;
	return sa.take_string();
}

enum {
	H_ROUTES, H_CLEAR, H_ADD, H_DEL
};

String WINGSetSourceRoute::read_handler(Element *e, void *thunk) {
	WINGSetSourceRoute *c = (WINGSetSourceRoute *) e;
	switch ((intptr_t) (thunk)) {
	case H_ROUTES:
		return c->print_routes();
	default:
		return "<error>\n";
	}
}

int WINGSetSourceRoute::write_handler(const String &in_s, Element *e, void *vparam, ErrorHandler *errh) {
	WINGSetSourceRoute *td = (WINGSetSourceRoute *) e;
	String s = cp_uncomment(in_s);
	switch ((intptr_t) vparam) {
	case H_ADD: {
		Vector<String> args;
		PathMulti p;
		cp_spacevec(s, args);
		if (args.size() < 2) {
			return errh->error("must have at least 2 arguments, %u given", args.size());
		}
		for (int x = 0; x < args.size(); x++) {
			NodeAirport node;
			if (!cp_node_airport(args[x], &node)) {
				return errh->error("unable to parse hop %s (arr:ipaddress:dep)", args[x].c_str());
			}
			p.push_back(node);
		}
		if (p[0]._ip != td->_ip) {
			return errh->error("first hop %s doesn't match my ip %s",
						p[0]._ip.unparse().c_str(),
						td->_ip.unparse().c_str());
		}
		td->_routes.insert(p[p.size() - 1]._ip, p);
		break;
	}
	case H_DEL: {
		IPAddress ip;
		if (!cp_ip_address(s, &ip)) {
			return errh->error("parameter %s must be IPAddress", s.c_str());
		}
		if (!td->_routes.remove(ip)) {
			return errh->error("unable to find destination %s", ip.unparse().c_str());
		}
		break;
	}
	case H_CLEAR: {
		td->_routes.clear();
		break;
	}
	}
	return 0;
}

void WINGSetSourceRoute::add_handlers() {
	add_read_handler("routes", read_handler, H_ROUTES);
	add_write_handler("clear", write_handler, H_CLEAR);
	add_write_handler("add", write_handler, H_ADD);
	add_write_handler("del", write_handler, H_DEL);
}

CLICK_ENDDECLS
ELEMENT_REQUIRES(WINGForwarder)
EXPORT_ELEMENT(WINGSetSourceRoute)
