/*
 * 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/queue.cc,v 1.11 1998/11/02 18:27:40 heideman Exp $ (LBL)
 */

#ifdef WIN32
#include <windows.h>
#endif
#include "netview.h"
#include "psview.h"
#include "queue.h"
#include "drop.h"
#include "monitor.h"
#include "sincos.h"

QueueItem::QueueItem(const PacketAttr& p, double tim, long offset)
	: Animation(tim, offset), qnext_(0)
{
	pkt_ = p;
}

QueueItem::~QueueItem()
{
  if (monitor_!=NULL) {
    monitor_->delete_monitor_object(this);
  }
}
 

void 
QueueItem::draw(View* nv, double ) const
{
	nv->polygon(px_, py_, 4, paint_);
	if ((pkt_.attr & 0x100) == 0)
		nv->fill(px_, py_, 4, paint_);
	if (monitor_!=NULL)
	  monitor_->draw(nv, px_[0],py_[0]);
}

void 
QueueItem::draw(PSView* nv, double ) const
{
	if ((pkt_.attr & 0x100) == 0)
	  nv->fill(px_, py_, 4, paint_);
	else 
	  nv->polygon(px_, py_, 4, paint_);

}

float QueueItem::distance(float x, float y) const 
{
	float cx, cy;
	cx = (bb_.xmin + bb_.xmax) / 2;
	cy = (bb_.ymin + bb_.ymax) / 2;
	return ((cx-x) * (cx-x) + (cy-y)*(cy-y));
}

void QueueItem::position(float& x, float& y)
{
	/* XXX this is stupid */
	x = px_[0];
	y = py_[0];
}

void QueueItem::update_bb()
{
	bb_.xmin = bb_.xmax = px_[0];
	bb_.ymin = bb_.ymax = py_[0];
	for (int i = 0; i < 4; i++) {
		if (px_[i] < bb_.xmin)
			bb_.xmin = px_[i];
		if (px_[i] > bb_.xmax)
			bb_.xmax = px_[i];
		if (py_[i] < bb_.ymin)
			bb_.ymin = py_[i];
		if (py_[i] < bb_.ymax)
			bb_.ymax = py_[i];
	}
}

void QueueItem::locate(float x, float y, float dx, float dy)
{
	px_[0] = x;	py_[0] = y;
	x += dy;	y -= dx;
	px_[1] = x;	py_[1] = y;
	x += dx;	y += dy;
	px_[2] = x;	py_[2] = y;
	x -= dy;	y += dx;
	px_[3] = x;	py_[3] = y;
	update_bb();
}

// use Animation::inside()
//int QueueItem::inside(double /*now*/, float px, float py) const
//{
// 	float minx, maxx, miny, maxy;
// 	int npts = 4;
// 	maxx = minx = px_[0]; maxy = miny = py_[0];
// 	while (--npts > 0) {
// 		if (px_[npts] < minx)
// 			minx = px_[npts];
// 		if (px_[npts] > maxx)
// 			maxx = px_[npts];
// 		if (py_[npts] < miny)
// 			miny = py_[npts];
// 		if (py_[npts] > maxy)
// 			maxy = py_[npts];
// 	}
// 	return (px >= minx && px <= maxx && py >= miny && py <= maxy);
//}

const char* QueueItem::info() const
{
	static char text[128];
	sprintf(text, "%s %d: %s\n   %d bytes\n",
		pkt_.type, pkt_.id, pkt_.convid, pkt_.size);
	return (text);
}

const char* QueueItem::getname() const
{
	static char text[128];
	sprintf(text, "p");
	return (text);
}

void QueueItem::monitor(Monitor *m, double /*now*/, char *result, int /*len*/)
{
  monitor_=m;
  sprintf(result, "%s %d: %s\n   %d bytes\n",
                pkt_.type, pkt_.id, pkt_.convid, pkt_.size);
}

MonState *QueueItem::monitor_state()
{
  /*return any state we wish the monitor to remember after we're gone*/
  MonState *ms=new MonState;
  ms->type=MON_PACKET;
  ms->pkt.id=pkt_.id;
  return ms;
}

Queue::Queue(float angle)
	: cnt_(0), psize_(0.), nb_(0), angle_(angle)
{
	head_ = 0;
	tail_ = &head_;
}

void Queue::place(double psize, double x, double y)
{
	psize_ = psize;
	double qc, qs;
	SINCOSPI(angle_, &qs, &qc);
	dx_ = qc * psize_;
	dy_ = qs * psize_;

	px_ = 3 * dx_ / 4;
	py_ = 3 * dy_ / 4;

	qx_ = x + 2 * dx_;
	qy_ = y + 2 * dy_;
}

void Queue::relocate()
{
	float x = qx_, y = qy_;

	for (QueueItem *q = head_; q != 0; q = q->qnext_) {
		q->locate(x, y, px_, py_);
		x += dx_;
		y += dy_;
	}
}

QueueItem *Queue::remove(const PacketAttr& p, int& pos)
{
	QueueItem* q;
	pos = 0;
	for (QueueItem **pp = &head_; (q = *pp) != 0; pp = &q->qnext_) {
		++pos;
		if (q->pkt_.id == p.id
		    && q->pkt_.attr == p.attr
		    && q->pkt_.size == p.size
		    && strcmp(q->pkt_.convid, p.convid) == 0
		    && strcmp(q->pkt_.type, p.type)== 0) {
			--cnt_;
			nb_ -= q->pkt_.size;
			*pp = q->qnext_;
			if (*pp == 0)
				tail_ = pp;
			relocate();
			return (q);
		}
	}
	return (0);
}

void Queue::enque(QueueItem *q, int mode)
{
  /*add to the tail if time is running forwards, else add to the head*/
  if (mode==QUEUE_TAIL)
    {
      *tail_ = q;
      tail_ = &q->qnext_;
      q->qnext_ = 0;
      ++cnt_;
      nb_ += q->pkt_.size;
      relocate();
    }
  else if (mode==QUEUE_HEAD)
    {
      q->qnext_=head_;
      head_=q;
      ++cnt_;
      nb_ += q->pkt_.size;
      relocate();
    }
}

void Queue::reset(double /*now*/)
{
	while (head_ != 0) {
		QueueItem *n = head_->qnext_;
		delete head_;
		head_ = n;
	}
	head_ = 0;
	tail_ = &head_;
	cnt_ = 0;
	nb_ = 0;
}
