/*
 * Copyright (c) 1991,1993 Regents of the University of California.
 * 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 must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in the
 *    documentation and/or other materials provided with the distribution.
 * 3. All advertising materials mentioning features or use of this software
 *    must display the following acknowledgement:
 *	This product includes software developed by the Computer Systems
 *	Engineering Group at Lawrence Berkeley Laboratory.
 * 4. Neither the name of the University nor of the Laboratory may be used
 *    to endorse or promote products derived from this software without
 *    specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
 * SUCH DAMAGE.
 *
 * @(#) $Header: /cvsroot/nsnam/nam-1/node.cc,v 1.63 2011/11/02 16:08:03 tom_henderson Exp $ (LBL)
 */

#include <stdlib.h>
#ifdef WIN32
#include <windows.h>
#endif
#include <tcl.h>

#include "view.h"
#include "netview.h"
#include "psview.h"
#include "editview.h"
#include "node.h"
#include "queue.h"
#include "feature.h"
#include "agent.h"
#include "edge.h"
#include "route.h"
#include "monitor.h"
#include "lan.h"
#include "paint.h"
#include "trace.h"


//----------------------------------------------------------------------
//----------------------------------------------------------------------
MovementElement::MovementElement(double time, double x, double y) {
	next_ = NULL;
	time_ = time;
	x_ = x;
	y_ = y;
	x_velocity_ = 0.0;
	y_velocity_ = 0.0;
	duration_ = 0.0;
}

//----------------------------------------------------------------------
//----------------------------------------------------------------------
MovementElement::MovementElement(double time, double x,
                                 double y, double x_velocity,
                                 double y_velocity, double duration) {
	next_ = NULL;
	time_ = time;
	x_ = x;
	y_ = y;
	x_velocity_ = x_velocity;
	y_velocity_ = y_velocity;
	duration_ = duration;
}

//----------------------------------------------------------------------
//----------------------------------------------------------------------
MovementElement::~MovementElement() {
	next_ = NULL;
}

//----------------------------------------------------------------------
// bool
// MovementElement::contains(double time, double duration);
//   - returns true if time >= time_ 
//     and < time_ + duration_
//----------------------------------------------------------------------
bool
MovementElement::contains(double time, double duration) {
	return (time >= time_ && time < (time_ + duration_));
}


//----------------------------------------------------------------------
//----------------------------------------------------------------------
MovementList::MovementList() {
	head_ = NULL;
}

//----------------------------------------------------------------------
// MovementList::~MovementList()
//   - deletes the remaining part of the list
//----------------------------------------------------------------------
MovementList::~MovementList() {
	clear();
}

//----------------------------------------------------------------------
// void
// MovementList::clear() {
//   - deletes all items on the list
//----------------------------------------------------------------------
void
MovementList::clear() {
	MovementElement * run;

	if (head_) {
		for (run = head_->next_; run; run = run->next_) {
			delete head_;
			head_ = run;
		}

		if (head_) {
			delete head_;
		}
		head_ = NULL;
	}
}

//----------------------------------------------------------------------
// MovementList *
// MovementList::add(double x, double y, double time)
//    - adds a new time value to the list which is 
//      sorted in ascending order
//----------------------------------------------------------------------
MovementElement *
MovementList::add(double x, double y, double time) {
	MovementElement * new_movement, * run, * previous;
	
	if (!head_) {
		// First element on list
		head_ = new MovementElement(time, x, y);

	} else {
		// Have to place down on list
		previous = NULL;
		run = head_;
		for (run = head_; run; run = run->next_) {
			// slight rounding errors occur with strtod so we need to 
			// have an acceptable error range
			if ((run->time_ - time <= 0.000001) &&
			    (run->time_ - time >= -0.000001)) {
				// Update time with new x,y
				run->x_ = x;
				run->y_ = y;
				break;
			} else if (time < run->time_) {
				if (previous) {
					// Place in middle of list
					new_movement = new MovementElement(time, x, y);
					new_movement->next_ = run;
					previous->next_ = new_movement;
				} else {
					// Add to front of list
					new_movement = new MovementElement(time, x, y);
					new_movement->next_ = head_;
					head_ = new_movement;
				}
				break;
			}
			previous = run;
		}

		if (!run) {
			// We ran to the end of the list and couldn't place
			// the new time so we have to place it at the end 
			previous->next_ = new MovementElement(time, x, y);
			run = previous->next_;
		}
	}
	return run;
}

//----------------------------------------------------------------------
// MovementList * 
// MovementList::remove(double time)
//    - removes time from the list 
//----------------------------------------------------------------------
void
MovementList::remove(double time) {
	MovementElement * run, * previous;
	previous = NULL;

	for (run = head_; run; run = run->next_) {
		// slight rounding errors occur with strtod so we need to 
		// have an acceptable error range
		if ((run->time_ - time <= 0.000001) &&
		    (run->time_ - time >= -0.000001)) {
			if (previous) {
				previous->next_ = run->next_;
				delete run;

			} else {
				// Element is at the front of the list
				head_ = run->next_;
			}
			size_--;
			break;
		}
		previous = run; 
	}
}

//----------------------------------------------------------------------
//----------------------------------------------------------------------
void
MovementList::setList(char * list_string) {
	char * run = 0;
	char * next = 0;
	//double time;
	// Erase all items on the list 
	clear();

	// Run down the list string and add values to the list
	run = list_string;
	while (run) {
		//time = strtod(run, &next);
		if (run != next) {
			//add(time);
		} else {
			break;
		}
		run = next;
	}
}

//----------------------------------------------------------------------
// void 
// MovementList::getListString(char * buffer, int buffer_size)
//   - fills buffer with a space delimited list of all values on the
//     time list sorted in ascending order
//
//   - the returned list should be freed when it is not needed anymore
//----------------------------------------------------------------------
int
MovementList::getListString(char * buffer, int buffer_size) {
	int total_written, just_written;
	MovementElement * run;

	total_written = 1; // Need space for the /0 character
	for (run = head_; run; run = run->next_) {
		just_written = sprintf(buffer, "%f ", run->time_);

		if (just_written == -1) {
			fprintf(stderr, "Ran out of buffer space when creating time list string\n");
			total_written = -1;
			break;
		}
		//Advance buffer pointer past written characters
		buffer += just_written;
		total_written += just_written;
	}
	return total_written;
}

//----------------------------------------------------------------------
//
//----------------------------------------------------------------------
double
MovementList::lastStopMovement() {
	MovementElement * element;
	double stop_time = 0.0;
	bool start = true;

	for (element = head(); element; element = element->next_) {
		if (start) {
			start = false;
		} else {
			stop_time = element->time_;
			start = true;
		}
	}
	return stop_time;
}



//----------------------------------------------------------------------
// double
// MovementList::getXPositionAt(double time)
//   - Calculates the x position of a node based on where it should be
//     on it's movement list
//   - If the list is empty then it doesn't modify x and y
//
//----------------------------------------------------------------------
void
MovementList::getPositionAt(double time, double & x, double & y) {
	MovementElement * before, * after;

	if (!head()) {
		return;
	}

	before = NULL;
	for (after = head(); after; after = after->next_) {
		if (time < after->time_) {
			break;
		}
		before = after;
	}

	if (before) {
		if (after) {
			// We need to calculate the movement between 2 positions
			x = before->x_ + (after->x_ - before->x_) *
			                  (before->time_ - time) /
			                  (before->time_ - after->time_);

			y = before->y_ + (after->y_ - before->y_) *
			                  (before->time_ - time) /
			                  (before->time_ - after->time_);
		} else {
			// We are at the end of the list so set the position to the 
			// before movement x,y
			x = before->x_;
			y = before->y_;
		}
	} else {
		// If there is no before then give the first item in the list
		before = head();
		if (before) {
			x = before->x_;
			y = before->y_;
		}
	}
}


//----------------------------------------------------------------------
//  Wrapper for tcl creation of Nodes
//----------------------------------------------------------------------
static class NodeClass : public TclClass {
public:
  NodeClass() : TclClass("Node") {} 
  TclObject * create(int argc, const char*const* argv) {
    Node * node;
    double size = strtod(argv[6], NULL);

    // set <node> [new Node node_id type size]
    if (!strcmp(argv[5], "circle")) {
      node = new CircleNode(argv[4], size);
    } else if (!strcmp(argv[5], "box")) {
      node = new BoxNode(argv[4], size);
    } else {
      node = new HexagonNode(argv[4], size);
    }
 
    return node;
  }
} class_node;




//----------------------------------------------------------------------
//----------------------------------------------------------------------
Node::Node(const char* name, double size) :
  Animation(0, 0),
  queue_(0),
  size_(size),
  nsize_((float) size),
  x_(0.),
  y_(0.),
  x_vel_(0.),
  y_vel_(0.),
  starttime_(0.),
  endtime_(0.),
  links_(0),
  routes_(0),
  agents_(0),
  anchor_(0),
  mark_(0),
  state_(UP),
  nm_(NULL),
  nMark_(0),
  dlabel_(0),
//  lcolor_(0),
//  dcolor_(0),  // This is some type of hack to not use dcolor
//  ncolor_(0),
  direction_(0) {

  next_ = NULL;

  label_ = new char[strlen(name) + 1];
  strcpy(label_, name);
  addr_ = nn_ = atoi(name); /*XXX*/
        
  lcolor_ = NULL;
  dcolor_ = NULL; 
  ncolor_ = NULL;
  init_color("black");
  dx_ = 0.0;
  dy_ = 0.0;

  tcl_script_label_ = NULL;
  tcl_script_ = NULL;

  wireless_ = false;
  paint_ = Paint::instance()->thick();
}

//----------------------------------------------------------------------
//----------------------------------------------------------------------
Node::~Node() {
	if (nm_ != NULL) {
		NodeMark *p = nm_;
		nm_ = nm_->next;
		delete p;
	}
	delete label_;
	if (queue_ != NULL)
		delete queue_;

	if (tcl_script_) {
	delete [] tcl_script_;
	}

	if (tcl_script_label_) {
		delete [] tcl_script_label_;
	}
}

//----------------------------------------------------------------------
// void 
// Node::init_color(const char *clr) {
//----------------------------------------------------------------------
void 
Node::init_color(const char *clr) {
  color(clr);
  lcolor(clr);
  ncolor(clr);
  dcolor(clr);
  oldPaint_ = paint_;
}

void Node::set_down(char *color)
{
	// If current color is down, don't change it again.
	// Assuming only one down color. User can change this behavior
	// by adding tcl code for link-up and link-down events.
	if (state_ == UP) {
		int pno = Paint::instance()->lookup(color, 3);
		oldPaint_ = paint_;
		paint_ = pno;
		state_ = DOWN;
	}
}

void Node::set_up()
{
	if (state_ == DOWN) {
		state_ = UP;
		toggle_color();
	}
}

float Node::distance(float x, float y) const
{
	return ((x_-x) * (x_-x) + (y_-y) * (y_-y));
}

void Node::size(double s)
{
	size_ = s;
	update_bb();
}

// We don't need one update_bb() for every derived class because their
// sizes are all based on 0.5*size_. Otherwise we'll have to do update_bb
// individually
void Node::update_bb()
{
	double off = 0.5 * size_ + NodeMarkScale * size_; // thick circles
	if (nMark_ > 0) 
		off += nMark_ * NodeMarkScale * size_;
	/*XXX*/
	bb_.xmin = x_ - off;
	bb_.ymin = y_ - off;
	bb_.xmax = x_ + off;
	bb_.ymax = y_ + off;
}


const char* Node::info() const
{
	static char text[128];
	sprintf(text, "Node: %s (%g, %g)", label_, x(), y());
	return (text);
}

//----------------------------------------------------------------------
// const char * 
// Node::property()
//   - returns a string of property types and values for the node 
//     object
//----------------------------------------------------------------------
const char * 
Node::property() {
	static char text[256];
	char *p;
	rgb * color;	 

	color = Paint::instance()->paint_to_rgb(paint_);

	
	// object type and id
	p = text;
	sprintf(text, "{\"Node: %d\" nn_ title \"Node %d\"} ", nn_, nn_);
	
	// node size
	p = &text[strlen(text)];
	sprintf(p, "{Size size_ text %.1f} ", size_);
	
	// node color 
	p = &text[strlen(text)];
	sprintf(p, "{Color color color %s} ", color->colorname);

	// label if it exists
	p = &text[strlen(text)];
	if (dlabel_) {
		sprintf(p, "{Label dlabel_ text %s} ", dlabel_);
	} else {
		sprintf(p, "{Label dlabel_ text  } ");
	}

	if (movement_list.head()) {

	}

	return(text);
}

const char* Node::getname() const
{
	static char text[128];
	sprintf(text, "n %s", label_);
	return (text);
}

void Node::monitor(Monitor *m, double /*now*/, char *result, int /*len*/)
{
  monitor_=m;
  sprintf(result, "Node: %s", label_);
  return;
}

void Node::add_link(Edge* e)
{
	movement_list.clear();
	e->next_ = links_;
	links_ = e;
}

void Node::delete_link(Edge* e) {
	Edge *h, *k;
	h = links_;
	for (k = links_; k != 0; k = k->next_) {
		if (k->src() == e->src() && k->dst() == e->dst()) {
			if (k == links_) {
				links_ = k->next_;
				break;
			} else {
				h->next_ = k->next_;
				break;
			}
		}

		h = k;
	}	
}


void Node::add_agent(Agent* a)
{
  a->next_ = agents_;
  agents_ = a;
}

void Node::delete_agent(Agent* r)
{
  /*given a agent, remove it from a node's agent list*/
  Agent *ta1, *ta2;
  ta1=agents_;
  ta2=agents_;
  while ((ta1!=r)&&(ta1!=NULL))
    {
      ta2=ta1;
      ta1=ta1->next();
    }
  if (ta1==r)
    {
      ta2->next(ta1->next());
      if (ta1==agents_)
	agents_=ta1->next();
    }
}

Agent *Node::find_agent(char *name) const
{
  Agent *ta=NULL;
  ta=agents_;
  while (ta!=NULL)
    {
      if (strcmp(ta->name(), name)==0)
	return ta;
      ta=ta->next();
    }
  return NULL;
}

void Node::add_route(Route* r)
{
  r->next_ = routes_;
  routes_ = r;
}

void Node::delete_route(Route* r)
{
  /*given a route, remove it from a node's route list*/
  Route *tr1, *tr2;
  tr1=routes_;
  tr2=routes_;
  while ((tr1!=r)&&(tr1!=NULL))
    {
      tr2=tr1;
      tr1=tr1->next();
    }
  if (tr1==r)
    {
      tr2->next(tr1->next());
      if (tr1==routes_)
	routes_=tr1->next();
    }
  if (routes_!=NULL)
    {
      /*need to reposition routes on this edge*/
      Edge *e=tr1->edge();
      int ctr=0;
      for (tr2=routes_;tr2!=NULL;tr2=tr2->next())
	if (tr2->edge()==e)
	  tr2->place(e->x0(), e->y0(), ctr++);
    }
}

Route *Node::find_route(Edge *e, int group, int pktsrc, int oif) const
{
  Route *tr=NULL;
  tr=routes_;
  while (tr!=NULL)
    {
      if (tr->matching_route(e, group, pktsrc, oif)==1)
	return tr;
      tr=tr->next();
    }
  return NULL;
}

void Node::place_route(Route *r)
{
	if (r->node() != this)
		return;
	if (r->marked() == 0) {
		r->place(r->edge()->x0(), r->edge()->y0());
		r->mark(1);
	}
}

// Used when topology is relayout
void Node::place_all_routes()
{
	Route *tr = routes_;
	while (tr != NULL) {
		place_route(tr);
		tr = tr->next();
	}
}

// Clear mark on routes so we can replace them
void Node::clear_routes()
{
	Route *tr = routes_;
	while (tr != NULL) {
		tr->mark(0);
		tr = tr->next();
	}
}

int Node::no_of_routes(Edge *e) const
{
  Route *tr=routes_;
  int no_of_routes=0;
  while (tr!=NULL)
    {
      if (tr->edge()==e)
	no_of_routes++;
      tr=tr->next();
    }
  return no_of_routes;
 }

void
Node::label(const char* name, int anchor) {
	delete []label_;
	label_ = new char[strlen(name) + 1];
	strcpy(label_, name);
	anchor_ = anchor;
}

void Node::lcolor(const char* name)
{
	if (name[0] == 0) {
		if (lcolor_) {
			delete []lcolor_;
			lcolor_ = 0;
		}
		return;
	}

	if (lcolor_)
		delete []lcolor_;
	lcolor_ = new char[strlen(name) + 1];
	strcpy(lcolor_, name);
}

void Node::dlabel(const char* name)
{
	if (name[0] == 0) {
		if (dlabel_) {
			delete []dlabel_;
			dlabel_ = 0;
		}
		return;
	}

	if (dlabel_)
		delete []dlabel_;
	dlabel_ = new char[strlen(name) + 1];
	strcpy(dlabel_, name);
}

//----------------------------------------------------------------------
// void
// Node::dcolor(const char* name)
//----------------------------------------------------------------------
void Node::dcolor(const char* name) {
  if (name[0] == 0) {
    if (dcolor_) {
      delete []dcolor_;
      dcolor_ = 0;
    }
    return;
  }

  if (dcolor_)
    delete []dcolor_;
  dcolor_ = new char[strlen(name) + 1];
  strcpy(dcolor_, name);
}

//----------------------------------------------------------------------
//----------------------------------------------------------------------
void
Node::ncolor(const char* name) {
  if (name[0] == 0) {
    if (ncolor_) {
      delete []ncolor_;
      ncolor_ = 0;
    }
    return;
  }

  if (ncolor_)
    delete []ncolor_;

  ncolor_ = new char[strlen(name) + 1];
  strcpy(ncolor_, name);
}

void Node::direction(const char* name)
{
        if (name[0] == 0) {
                if (direction_) {
                        direction_ = 0;
                }
                return;
        }
        if (!strcmp(name, "EAST"))
               direction_ = 1;
        else if (!strcmp(name, "SOUTH"))
               direction_ = 2;
        else if (!strcmp(name, "WEST"))
               direction_ = 3;
        else if (!strcmp(name, "NORTH"))
               direction_ = 4;
        else
               direction_ = 0;
}


void Node::add_sess_queue(unsigned int, Queue *q)
{
	if (queue_ != NULL)
		// Currently we only allow one queue per node
		return;
	queue_ = q;
	queue_->place(size_*0.5, x_+size_*.05, y_+size_*0.5);
}


//----------------------------------------------------------------------
//----------------------------------------------------------------------
char * Node::getTclScript()  {
  return tcl_script_;
}

//----------------------------------------------------------------------
//----------------------------------------------------------------------
char * Node::getTclScriptLabel()  {
  return tcl_script_label_;
}

//----------------------------------------------------------------------
//----------------------------------------------------------------------
void Node::setTclScript(const char * label, const char * script) {
  if (tcl_script_label_) {
    delete [] tcl_script_label_;
  }
  tcl_script_label_ = new char[strlen(label)];
  strcpy(tcl_script_label_, label);

  if (tcl_script_) {
    delete [] tcl_script_;
  }
  tcl_script_ = new char[strlen(script)];
  strcpy(tcl_script_, script);
}

//----------------------------------------------------------------------
// int
// Node::command(int argc, const char * const * argv)
//  - Interface between tcl and c
//----------------------------------------------------------------------
int
Node::command(int argc, const char * const * argv) {
	double start_x, start_y, destination_x, destination_y;
	double speed, start_time, destination_time;

	if (!strcmp(argv[1], "addMovement")) {
		// $node addMovement time x y speed
		// - time is time of start of movement
		// - x,y is destination location
		// - speed is how fast the node is travelling
		start_time = strtod(argv[2],NULL);
		destination_x = strtod(argv[3],NULL);
		destination_y = strtod(argv[4],NULL);
		speed = strtod(argv[5],NULL);

	// We need to calculate at what time we reach
		// destination x,y
		start_x = x();  // intial x,y values should have been set for 
		start_y = y();  // node before add the Movement positions

		if (movement_list.head()) {
			movement_list.getPositionAt(start_time, start_x, start_y);
		} else {
			addMovementDestination(start_x, start_y, start_time);
		}

		// Now we need to find the arrival time at the
		// destination coordinates

		if (speed != 0.0) {
			destination_time = sqrt((start_x - destination_x) *
			                        (start_x - destination_x) +
			                        (start_y - destination_y) *
			                        (start_y - destination_y)) / speed + 
			                   start_time;

			addMovementDestination(destination_x,
			                       destination_y,
			                       destination_time);
		}
		return TCL_OK;
	}
	return TCL_ERROR;
}

//----------------------------------------------------------------------
//----------------------------------------------------------------------
NodeMark* Node::find_mark(char *name) {
	NodeMark *p = nm_;
	while (p != NULL) {
		if (strcmp(p->name, name) == 0)
			break;
		p = p->next;
	}
	return p;
}

int Node::add_mark(char *name, char *color, char *shape)
{
	if (find_mark(name) != NULL)
		return 1;

	NodeMark *cm = new NodeMark(name);
	cm->color = Paint::instance()->lookup(color, 2);
	if (strcmp(shape, "circle") == 0) 
		cm->shape = NodeMarkCircle;
	else if (strcmp(shape, "square") == 0)
		cm->shape = NodeMarkSquare;
	else if (strcmp(shape, "hexagon") == 0)
		cm->shape = NodeMarkHexagon;

	if (nm_ != NULL)
		cm->next = nm_;
	nm_ = cm;
	nMark_++;
	update_bb();
	return 0;
}

void Node::delete_mark(char *name)
{
	NodeMark **p;
	p = &nm_;
	while (*p != NULL) {
		if (strcmp((*p)->name, name) == 0) {
			NodeMark *q = *p;
			*p = (*p)->next;
			delete q;
			nMark_--;
			update_bb();
			return;
		}
		p = &((*p)->next);
	}
}

void Node::update(double now) {
	if (now >= endtime_ || now <= starttime_ ) {
		return;
	}

	double xpos = xorig_ + (now - starttime_)* x_vel_;
	double ypos = yorig_ + (now - starttime_)* y_vel_;
	place(xpos,ypos);
}


void Node::draw_mark(View* nv) const
{
	NodeMark *cm;
	double s = size_ * 0.7;
	for (cm = nm_; cm != NULL; cm = cm->next, s += size_ * NodeMarkScale) {
		switch (cm->shape) {
		case NodeMarkCircle:
			nv->circle(x_, y_, s, cm->color);
			break;
		case NodeMarkSquare: {
			double x[2], y[2];
			x[0] = x_ - s, x[1] = x_ + s;
			y[0] = y_ - s, y[1] = y_ + s;
			nv->rect(x[0], y[0], x[1], y[1], cm->color);
			break;
		}
		case NodeMarkHexagon: {
			float x[6], y[6];
			double qd = 0.5 * s;
			x[0] = x_ - s;
			y[0] = y_;
			
			x[1] = x_ - qd;
			y[1] = y_ + s;
			
			x[2] = x_ + qd;
			y[2] = y_ + s;
			
			x[3] = x_ + s;
			y[3] = y_;
			
			x[4] = x_ + qd;
			y[4] = y_ - s;
			
			x[5] = x_ - qd;
			y[5] = y_ - s;
			
			nv->polygon((float *)x, (float *)y, 6, cm->color);
			break;
		}
		}
	}
}

void Node::drawlabel(View* nv) const
{
	/*XXX node number */
	if (label_ != 0) 
              nv->string(x_, y_, size_, label_, anchor_, lcolor_);
	if (monitor_!=NULL)
		monitor_->draw(nv, x_, y_-size_/2);
	if (dlabel_ != 0) {
                switch (direction_) {
                case 0:
		    nv->string(x_, y_+size_, size_*0.7, dlabel_, 0, dcolor_);
                    break;
                case 1:
		    nv->string(x_-size_*1.5, y_, size_*0.7, dlabel_, 0, 
			       dcolor_);
                    break;
                case 2:
		    nv->string(x_, y_-size_, size_*0.7, dlabel_, 0, dcolor_);
                    break;
                case 3:
		    nv->string(x_+size_*1.5, y_, size_*0.7, dlabel_, 0, 
			       dcolor_);
                    break;
                case 4:
		    nv->string(x_,           y_+size_,   size_*0.7, dlabel_, 0,
			       dcolor_);
                    break;
                default:
		    nv->string(x_,           y_+size_,   size_*0.7, dlabel_, 0,
			       dcolor_);
                    break;
                }
        }
}

void Node::reset(double)
{
	//XXX why should reset to black??? 
	//	paint_ = Paint::instance()->thick();
}


//----------------------------------------------------------------------
// MovementElement * 
// Node::addMovementDestination(double x, double y, double time)
//   - add a movement destination to node if it doesn't have any links
//   - only node without links can be moved
//----------------------------------------------------------------------
MovementElement * 
Node::addMovementDestination(double world_x, double world_y, double time) {
	if (!links()) {
		if (!movement_list.head() && time != 0.0) {
			addMovementDestination(x(), y(), 0.0);
		}

		return movement_list.add(world_x,world_y,time);
	} else {
		place(world_x,world_y);
		return NULL;
	}
}

//----------------------------------------------------------------------
//----------------------------------------------------------------------
void 
Node::removeMovementDestination(double time) {
	movement_list.remove(time);
}

//----------------------------------------------------------------------
//----------------------------------------------------------------------
int 
Node::getMovementTimeList(char * buffer, int buffer_size) {
	return movement_list.getListString(buffer, buffer_size);
}

//----------------------------------------------------------------------
// double 
// Node::getMaximumX()
//   - Get the maximum x value that a node can be at during it's
//     movement sequence
//----------------------------------------------------------------------
double 
Node::getMaximumX() {
	MovementElement * movement;
	double X;
	movement = movement_list.head();

	if (movement) {
		X = movement->x_;
		while(movement) {
			if (movement->x_ > X) {
				X = movement->x_;
			}
			movement = movement->next_;
		}
	} else {
		X = x();
	}
	return X;
}

//----------------------------------------------------------------------
// double
// Node::getMaximumY()
//   - Get the maximum y value that a node can be at during it's
//     movement sequence
//----------------------------------------------------------------------
double
Node::getMaximumY() {
	MovementElement * movement;
	double Y;
	movement = movement_list.head();

	if (movement) {
		Y = movement->y_;
		while(movement) {
			if (movement->y_ > Y) {
				Y = movement->y_;
			}
			movement = movement->next_;
		}
	} else {
		Y = y();
	}
	return Y;
}

void Node::place(double _x, double _y) { 
	x_ = _x;
	y_ = _y;
	mark_ = 1;
	update_bb();

	if (!links() && !movement_list.head()) {
		addMovementDestination(x(), y(), 0.0);
	}

	// Should we place queues here too? 
	if (queue_ != NULL) { 
		queue_->place(0.5 * size_, x_+0.5*size_, y_+0.5*size_);
	}
}

//----------------------------------------------------------------------
// void
// Node::move(EditView *v, float wdx, float wdy)
//   - Move by a displacement in *window* coordinates
//----------------------------------------------------------------------
void Node::move(EditView * edit_view, float wdx, float wdy) {
	float cx, cy;

	// First get our position in window coordinates
	cx = x_, cy = y_;
	edit_view->map(cx, cy);
	cx += wdx;
	cy += wdy;

	// Now switch back to world coordinates
	edit_view->imap(cx, cy);

	// Place the new location
	place(cx, cy);

	// Because all placements are centrally organized by AutoNetModel, 
	// we'll have to call back. :(
	edit_view->moveNode(this);
}

//----------------------------------------------------------------------
//
//
//----------------------------------------------------------------------
void
Node::updatePositionAt(double current_time) {
	double world_x, world_y;

	world_x = x();
	world_y = y();
	movement_list.getPositionAt(current_time, world_x, world_y);

	place(world_x, world_y);
}


Edge *Node::find_edge(int dst) const 
{
	for (Edge *e = links_; e != 0; e = e->next_) 
		if (e->dst() == dst)
			return e;
	return NULL;
}

int Node::save(FILE *file)
{
	char buffer[TRACE_LINE_MAXLEN], *p;
	rgb *color;
	color=Paint::instance()->paint_to_rgb(paint_);

	buffer[0] = 0;
	if (state_ == UP)
		strcpy(buffer, "-S UP");
	else if (state_==DOWN)
		strcpy(buffer, "-S DOWN");
	p = buffer + strlen(buffer);
	if (dlabel_)
		sprintf(p, " -b \"%s\"", dlabel_);
	fprintf(file, "n -t * -s %d -v %s -c %s -z %f %s\n", 
		nn_, style(), color->colorname, size_, buffer);
	return(0);
}

//----------------------------------------------------------------------
// int
// Node::writeNsScript(FILE *file)
//----------------------------------------------------------------------
int
Node::writeNsScript(FILE *file) {
	int chars_written = 0;
	rgb * color;
	color = Paint::instance()->paint_to_rgb(paint_);
	double start_x, start_y;

	// If there are no values on movement_list then start_ and start_y
	// will be unchanged. So we need to intialize start_x and start_y
	// to the current values
	start_x = x();
	start_y = y();
	movement_list.getPositionAt(0.0, start_x, start_y);

	chars_written += fprintf(file, "set node(%d) [$ns node]\n", number());
	chars_written += fprintf(file, "## node(%d) at %f,%f\n", number(), start_x, start_y);
	chars_written += fprintf(file, "$node(%d) set X_ %f\n", number(), start_x);
	chars_written += fprintf(file, "$node(%d) set Y_ %f\n", number(), start_y);
	chars_written += fprintf(file, "$node(%d) set Z_ 0.0\n", number());

//	chars_written += fprintf(file, "$node(%d) size \"%f\"\n", number(), size_);

	chars_written += fprintf(file, "$node(%d) color \"%s\"\n", number(), color->colorname);
	if (dlabel_) {
		chars_written += fprintf(file, "$ns at 0.0 \"$node(%d) label %s\"\n", number(), dlabel_);
	}
	return chars_written;
}


//----------------------------------------------------------------------
// int
// Node::writeNsMovement(FILE *file)
//----------------------------------------------------------------------
int
Node::writeNsMovement(FILE *file) {
	int chars_written = 0;
	MovementElement * start, * destination;
	double speed;

	start = movement_list.head();
	if (start) {
		fprintf(file, "$ns initial_node_pos $node(%d) %f\n", number(), size());

		// Skip first command since it is our starting place
		destination = start->next_;
		while (destination) {
			if (start->x_ != destination->x_ ||
			    start->y_ != destination->y_) {
				// find speed from start to destination
				speed = sqrt((start->x_ - destination->x_) *
				             (start->x_ - destination->x_) +
				             (start->y_ - destination->y_) *
				             (start->y_ - destination->y_))/
				        (destination->time_ - start->time_);

				chars_written += fprintf(file, "$ns at %f \"$node(%d) setdest %f %f %f\"\n",
				                                start->time_, number(),
				                                destination->x_, destination->y_, speed);
			}
			start = destination;
			destination = destination->next_;
		}
	}

	return chars_written;
}


BoxNode::BoxNode(const char* name, double size) : Node(name, size)
{
	BoxNode::size(size);
}

void BoxNode::size(double s)
{
	Node::size(s);
	double delta = 0.5 * s;
	x0_ = x_ - delta;
	y0_ = y_ - delta;
	x1_ = x_ + delta;
	y1_ = y_ + delta;
}

void BoxNode::draw(View* nv, double now) {
	nv->rect(x0_, y0_, x1_, y1_, paint_);
	drawlabel(nv);
	draw_mark(nv);
}

void BoxNode::place(double x, double y)
{
	Node::place(x, y);
	double delta = 0.5 * size_;
	x0_ = x_ - delta;
	y0_ = y_ - delta;
	x1_ = x_ + delta;
	y1_ = y_ + delta;
}

CircleNode::CircleNode(const char* name, double size) : Node(name, size)
{
	CircleNode::size(size);
}

void CircleNode::size(double s)
{
	Node::size(s);
	radius_ = 0.5 * s;
}

void 
CircleNode::draw(View* nv, double now) {
	nv->circle(x_, y_, radius_, paint_);
	drawlabel(nv);
	draw_mark(nv);
}

HexagonNode::HexagonNode(const char* name, double size) : Node(name, size)
{
	HexagonNode::size(size);
}

void HexagonNode::size(double s)
{
	Node::size(s);
	double hd = 0.5 * s;
	double qd = 0.5 * hd;

	xpoly_[0] = x_ - hd; ypoly_[0] = y_;
	xpoly_[1] = x_ - qd; ypoly_[1] = y_ + hd;
	xpoly_[2] = x_ + qd; ypoly_[2] = y_ + hd;
	xpoly_[3] = x_ + hd; ypoly_[3] = y_;
	xpoly_[4] = x_ + qd; ypoly_[4] = y_ - hd;
	xpoly_[5] = x_ - qd; ypoly_[5] = y_ - hd;
}

void HexagonNode::draw(View* nv, double now) {
	nv->polygon(xpoly_, ypoly_, 6, paint_);
	drawlabel(nv);
	draw_mark(nv);
}

void HexagonNode::place(double x, double y)
{
	Node::place(x, y);

	double hd = 0.5 * size_;
	double qd = 0.5 * hd;
	xpoly_[0] = x_ - hd; ypoly_[0] = y_;
	xpoly_[1] = x_ - qd; ypoly_[1] = y_ + hd;
	xpoly_[2] = x_ + qd; ypoly_[2] = y_ + hd;
	xpoly_[3] = x_ + hd; ypoly_[3] = y_;
	xpoly_[4] = x_ + qd; ypoly_[4] = y_ - hd;
	xpoly_[5] = x_ - qd; ypoly_[5] = y_ - hd;
}


VirtualNode::VirtualNode(const char* name, Lan *lan) : 
  Node(name, 0), lan_(lan)
{
}

void VirtualNode::size(double s)
{
	Node::size(0);
	lan_->size(s);
}

void VirtualNode::draw(View* nv, double now) {
  printf("drawing vn at %f %f\n", x_, y_);
}

void VirtualNode::place(double x, double y)
{
	Node::place(x, y);
	lan_->place(x, y);
}

void VirtualNode::arrive_packet(Packet *p, Edge *e, double atime)
{
        lan_->arrive_packet(p,e,atime);
}

void VirtualNode::delete_packet(Packet *p)
{
        lan_->delete_packet(p);
}

double VirtualNode::x(Edge *e) const
{
  return lan_->x(e);
}

double VirtualNode::y(Edge *e) const
{
  return lan_->y(e);
}
