/* -*-	Mode:C++; c-basic-offset:8; tab-width:8; indent-tabs-mode:t -*- */

/* By Pablo Martin and Paula Ballester,
 * Strathclyde University, Glasgow.
 * June, 2003.
*/

/* Copyright (c) 2003 Strathclyde University of Glasgow, Scotland.
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 *
 * 1. Redistributions of source code and binary code must contain
 * the above copyright notice, this list of conditions and the following
 * disclaimer.
 *
 * 2. All advertising materials mentioning features or use of this software
 * must display the following acknowledgement:
 * This product includes software developed at Strathclyde University of
 * Glasgow, Scotland.
 *
 * 3. The name of the University may not be used to endorse or promote
 * products derived from this software without specific prior written
 * permission.
 * STRATHCLYDE UNIVERSITY OF GLASGOW, MAKES NO REPRESENTATIONS
 * CONCERNING EITHER THE MERCHANTABILITY OF THIS SOFTWARE OR THE
 * SUITABILITY OF THIS SOFTWARE FOR ANY PARTICULAR PURPOSE.  The software
 * is provided "as is" without express or implied warranty of any kind.
*/

#include "classifier-umts.h"
#include <address.h>
#include <mac.h>

int UmtsClassifier::classify(Packet *p) {
	hdr_ip* iph = hdr_ip::access(p);
	return mshift(iph->daddr());
};

static class UmtsClassifierClass : public TclClass {
public:
	UmtsClassifierClass() : TclClass("Classifier/Umts") {}
	TclObject* create(int, const char*const*) {
		return (new UmtsClassifier());
	}
} class_umts_classifier;


UmtsClassifier::UmtsClassifier() : Classifier()
{
	index_ = NULL;
	bind("id_", &id_);
	bind("type_", &type_);
}

int UmtsClassifier::command(int argc, const char*const* argv)
{
	Tcl& tcl = Tcl::instance();
	if(argc == 2) {
                if (strcmp(argv[1], "get-index") == 0) {
                        if (index_ != 0)
                                tcl.result(index_->name());
                        return (TCL_OK);
                }
        } else if (argc == 3) {
		/*
		 * $classifier alloc-port nullagent
		 */
		if (strcmp(argv[1],"index") == 0) {
			index_ = (NsObject*) TclObject::lookup(argv[2]);
			if(index_ == 0)
				return TCL_ERROR;
			return TCL_OK;
		}
	}
	return (Classifier::command(argc, argv));
}

NsObject* UmtsClassifier::find(Packet* p)
{
	NsObject* node = NULL;
	int cl = classify(p);

	if ((type_ == 1) && (cl != id_)) {
		if (default_target_)
			return (default_target_);
	}

	// check if it is a new ue in the cell
	struct hdr_ll *lh = HDR_LL(p);
	if (lh->lltype() == LL_SETUP_REPLY) {
		struct hdr_ip *ih = HDR_IP(p);
		if (default_target_){

			Tcl& tcl = Tcl::instance();
			tcl.evalf("%s set update_ 1", index_->name());
			tcl.evalf("%s update-changed %d", index_->name(), Address::instance().get_nodeaddr(ih->daddr()));
			tcl.evalf("%s set update_ 0", index_->name());
			tcl.evalf("%s add-route %d %s", index_->name(), Address::instance().get_nodeaddr(ih->daddr()), default_target_->name());
		}
		node = NULL;
		return (node);
	}

	if ((lh->lltype() == LL_HANDOVER) && (cl == id_)){
		if (default_target_) {
			return (default_target_);
		}
	}

	if (cl < 0 || cl >= nslot_ || (node = slot_[cl]) == 0) {
		if (default_target_) {
			return default_target_;
		}
		/*
		 * Sigh.  Can't pass the pkt out to tcl because it's
		 * not an object.
		 */
		Tcl::instance().evalf("%s no-slot %ld", name(), cl);
		if (cl == TWICE) {
			/*
			 * Try again.  Maybe callback patched up the table.
			 */
			cl = classify(p);
			if (cl < 0 || cl >= nslot_ || (node = slot_[cl]) == 0) {
				return (NULL);
			}
		}
	}

	return (node);
}

/*
 * objects only ever see "packet" events, which come either
 * from an incoming link or a local agent (i.e., packet source).
 */
void UmtsClassifier::recv(Packet* p, Handler*h)
{
	NsObject* node = find(p);

	if (node == NULL) {
		/*
		 * XXX this should be "dropped" somehow.  Right now,
		 * these events aren't traced.
		 */
		Packet::free(p);
		return;
	}

	node->recv(p,h);
	return;
}
