/*
 * 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: /usr/src/mash/repository/vint/nam-1/edge.cc,v 1.23 1998/12/11 23:55:19 yaxu Exp $ (LBL)
 */


#include "config.h"
#include "sincos.h"

#include "edge.h"
#include "node.h"
#include "packet.h"
#include "view.h"
#include "netview.h"
#include "psview.h"
#include "transform.h"
#include "paint.h"
#include "monitor.h"

Edge::Edge(Node* src, Node* dst, double ps,
	   double bw, double delay, double length, double angle) :
	Animation(0, 0),
	src_(src->num()), dst_(dst->num()),
	neighbor_(dst),
        start_(src),
	x0_(0), y0_(0),
	//x1_(0), y1_(0),
	psize_(ps),
	angle_(angle),
	bandwidth_(bw),
	delay_(delay),
	length_(length),
	state_(UP),
	packets_(NULL),
	no_of_packets_(0),
	last_packet_(NULL),
	marked_(0)
{
	paint_ = Paint::instance()->thick();
	if (length_==0) length_=delay_;

	// Set initial (x0,y0), (x1,y1) so that they reflect the current 
	// angle. This enables netmodel to share the same placeEdge() and
	// placeAgent() with AutoNetModel.
	SINCOSPI(angle, &x1_, &y1_);
}

Edge::~Edge()
{
}

float Edge::distance(float x, float y) const 
{
	// TODO: Look it up when back home...
	matrix_.imap(x, y);
	return y;
}

void Edge::init_color(char *clr)
{
	color(clr);
	oldPaint_ = paint_;
}

void Edge::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 Edge::set_up()
{
	if (state_ == DOWN) {
		state_ = UP;
		toggle_color();
	}
}

void Edge::place(double x0, double y0, double x1, double y1)
{
	x0_ = x0;
	y0_ = y0;
	x1_ = x1;
	y1_ = y1;

	double dx = x1 - x0;
	double dy = y1 - y0;
	/*XXX*/
	// delay should not be equal to edge's position
	//	delay_ = sqrt(dx * dx + dy * dy);
	matrix_.clear();
	matrix_.rotate((180 / M_PI) * atan2(dy, dx));
	matrix_.translate(x0, y0);

 	if (x1>x0) {
 	  bb_.xmin = x0; bb_.xmax = x1;
	} else {
 	  bb_.xmin = x1; bb_.xmax = x0;
 	}
 	if (y1>y0) {
 	  bb_.ymin = y0; bb_.ymax = y1;
 	} else {
 	  bb_.ymin = y1; bb_.ymax = y0;
 	}

//	eb_.xmin = -0.005, eb_.xmax = sqrt(dx*dx+dy*dy) + 0.005;
//	eb_.ymin = -0.005, eb_.ymax = 0.005;
	eb_.xmin = -0.1 * start_->size();
	eb_.xmax = sqrt(dx*dx+dy*dy) + 0.1*neighbor_->size(); 
	eb_.ymin = -0.1 * start_->size();
	eb_.ymax = 0.1 * start_->size();

}

void Edge::AddPacket(Packet *p)
{
  p->next(packets_);
  if (packets_!=NULL)
    packets_->prev(p);
  p->prev(NULL);
  packets_=p;
  if (last_packet_==NULL)
    last_packet_=p;
  no_of_packets_++;
#define PARANOID
#ifdef PARANOID
  int ctr=0;
  for(Packet *tp=packets_;tp!=NULL;tp=tp->next())
    {
      ctr++;
      if (ctr>no_of_packets_) abort();
    }
  if (last_packet_->next()!=NULL) abort();
#ifdef DEBUG
  printf("AddPacket: %d->%d OK\n", src_, dst_);
#endif
#endif
  
}

void Edge::arrive_packet(Packet *p, double atime)
{
  /*Trigger any arrival event at the node*/
  neighbor_->arrive_packet(p, this, atime);
}

void Edge::DeletePacket(Packet *p)
{
  /*Trigger any deletion event at the node*/
  neighbor_->delete_packet(p);

  no_of_packets_--;
  if (last_packet_==p)
    {
      /*Normal case - it's the last packet*/
      last_packet_ = p->prev();
      if (packets_!=p)
	p->prev()->next(NULL);
      else
	packets_=NULL;
    }
  else
    {
      /*Abnormal case - need to search the list*/
      Packet *tp;
      tp=packets_;
      while((tp!=p)&&(tp!=NULL))
	{
	  tp=tp->next();
	}
      if (tp==p)
	{
	  if (tp==packets_)
	    /*it was the first packet*/
	    packets_=tp->next();
	  else
	    tp->prev()->next(tp->next());
	  if (tp==last_packet_)
	    {
	      /*this shouldn't ever happen*/
	      fprintf(stderr, "**it happened\n");
	      last_packet_=tp->prev();
	    }
	  else
	    tp->next()->prev(tp->prev());
	}
    }
#define PARANOID
#ifdef PARANOID
  int ctr=0;
  for(Packet *tp=packets_;tp!=NULL;tp=tp->next())
    {
      ctr++;
      if (ctr>no_of_packets_) abort();
    }
  if ((last_packet_!=NULL)&&(last_packet_->next()!=NULL)) abort();
#ifdef DEBUG
  printf("DeletePacket: %d->%d OK\n", src_, dst_);
#endif
#endif
}

void Edge::draw(View* view, double /*now*/) const
{
	view->line(x0_, y0_, x1_, y1_, paint_);
	if (monitor_!=NULL)
	  monitor_->draw(view, (x0_+x1_)/2, (y0_+y1_)/2);
}

void Edge::draw(PSView* view, double /*now*/) const
{
	view->line(x0_, y0_, x1_, y1_, paint_);
}

void Edge::reset(double)
{
	//	paint_ = Paint::instance()->thick();
	no_of_packets_=0;
	packets_=NULL;
	last_packet_=NULL;
}

int Edge::inside(double, float px, float py) const
{
	matrix_.imap(px, py);
	return eb_.inside(px, py);
}

int Edge::inside(float px, float py) const
{
	matrix_.imap(px, py);
	return eb_.inside(px, py);
}

const char* Edge::info() const
{
	static char text[128];
	sprintf(text, "link %d-%d:\n  bw: %g bits/sec\n  delay: %g sec\n",
		src_, dst_, bandwidth_, delay_);
	return (text);
}

const char* Edge::property() const
{
       rgb *color;
       color=Paint::instance()->paint_to_rgb(paint_);
 
        static char text[256];
        char *p;
 
        p = text;
        sprintf(text, "{LINK %d.%d} ", src_, dst_);       // obj type and id
        p = &text[strlen(text)];
	sprintf(p, "{COLOR %s} ", color->colorname); // color & value
        p = &text[strlen(text)];
        sprintf(p, "{BANDWIDTH %f} ", bandwidth_); // size & value
        p = &text[strlen(text)];
        sprintf(p, "{DELAY %f} ", delay_); // color & value

        return(text);

}

const char* Edge::getname() const
{
	static char text[128];
	sprintf(text, "l %d %d",
		src_, dst_);
	return (text);
}

void Edge::monitor(Monitor *m, double /*now*/, char *result, int /*len*/)
{
  monitor_=m;
  sprintf(result, "link %d-%d:\n bw: %g bits/sec\n delay: %g sec",
	  src_, dst_, bandwidth_, delay_);
  return;
}

int Edge::save(FILE *file)
{
   rgb *color;
     color=Paint::instance()->paint_to_rgb(paint_);
  char state[10];
  double angle,length;
  float dx,dy;
  //Edges in the tracefile are bidirectional, so don't need to save
  //both unidirectional edges
  if (src_>dst_) return 0;

  if (state_==UP)
    strcpy(state, " -S UP");
  else if (state_==DOWN)
    strcpy(state, " -S DOWN");
  else
    state[0]='\0';
  if (angle_<0)
    angle=180*(angle_+2*M_PI)/M_PI;
  else
    angle=180*(angle_/M_PI);
  dx=x0_-x1_;
  dy=y0_-y1_;
  length=sqrt(dx*dx+dy*dy);
  printf("%d->%d, angle=%f (%fdeg)\n", src_, dst_, angle_, angle);
  fprintf(file, "l -t * -s %d -d %d -r %f -D %f -c %s -o %.1fdeg -l %f%s\n", 
	  src_, dst_, bandwidth_, delay_, color->colorname,angle, length, state);
  return 0;
}

int Edge::saveAsNs(FILE *file)
{
   rgb *color;
     color=Paint::instance()->paint_to_rgb(paint_); 
  char state[10];
  double angle,length;
  float dx,dy;
  //Edges in the tracefile are bidirectional, so don't need to save
  //both unidirectional edges
  if (src_>dst_) return 0;

  if (angle_<0)  
    angle=180*(angle_+2*M_PI)/M_PI;
  else
    angle=180*(angle_/M_PI);
  dx=x0_-x1_;
  dy=y0_-y1_;
  length=sqrt(dx*dx+dy*dy);
  fprintf(file, "$ns duplex-link $n(%d) $n(%d) %fMb %fms DropTail\n",
          src_, dst_, bandwidth_/1000000.0, delay_*1000);
  fprintf(file, "$ns duplex-link-op $n(%d) $n(%d) orient %f\n",
          src_, dst_, angle);
  return 0;
}

