/* -*-	Mode:C++; c-basic-offset:8; tab-width:8; indent-tabs-mode:t -*- */
/*
 * Copyright (c) 1997 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.
 *
 * Ported from CMU/Monarch's code, appropriate copyright applies.
 * nov'98 -Padma.
 *
 * $Header: /nfs/jade/vint/CVSROOT/ns-2/trace/cmu-trace.cc,v 1.88 2005/10/08 19:16:16 tomh Exp $
 */

#include <packet.h>
#include <ip.h>
#include <tcp.h>
#include <sctp.h>
#include <rtp.h>
#include <arp.h>
#include <dsr/hdr_sr.h>	// DSR
#include <mac.h>
#include <mac-802_11.h>
#include <smac.h>
#include <address.h>
#include <tora/tora_packet.h> //TORA
#include <imep/imep_spec.h>         // IMEP
#include <aodv/aodv_packet.h> //AODV
#include <cmu-trace.h>
#include <mobilenode.h>
#include <simulator.h>
//<zheng: add for 802.15.4>
#include "wpan/p802_15_4pkt.h"
#include "wpan/p802_15_4trace.h"
#include "wpan/p802_15_4nam.h"
//</zheng: add for 802.15.4>

#include "diffusion/diff_header.h" // DIFFUSION -- Chalermek


//#define LOG_POSITION

//extern char* pt_names[];

static class CMUTraceClass : public TclClass {
public:
	CMUTraceClass() : TclClass("CMUTrace") { }
	TclObject* create(int, const char*const* argv) {
		return (new CMUTrace(argv[4], *argv[5]));
	}
} cmutrace_class;


//<zheng: ns 2.27 removed the following part, but we need it to control the broadcast radius>
double CMUTrace::bradius = 0.0;
double CMUTrace::radius_scaling_factor_ = 0.0;
double CMUTrace::duration_scaling_factor_ = 0.0;
//</zheng>

CMUTrace::CMUTrace(const char *s, char t) : Trace(t)
{
	bzero(tracename, sizeof(tracename));
	strncpy(tracename, s, MAX_ID_LEN);

        if(strcmp(tracename, "RTR") == 0) {
                tracetype = TR_ROUTER;
        }
	else if(strcmp(tracename, "TRP") == 0) {
                tracetype = TR_ROUTER;
        }
        else if(strcmp(tracename, "MAC") == 0) {
                tracetype = TR_MAC;
        }
        else if(strcmp(tracename, "IFQ") == 0) {
                tracetype = TR_IFQ;
        }
        else if(strcmp(tracename, "AGT") == 0) {
                tracetype = TR_AGENT;
        }
        else {
                fprintf(stderr, "CMU Trace Initialized with invalid type\n");
                exit(1);
        }
// change wrt Mike's code
//	assert(type_ == DROP || type_ == SEND || type_ == RECV);
	assert(type_ == DROP || type_ == SEND || type_ == RECV
               || ((type_ == EOT) && (tracetype == TR_MAC)));



	newtrace_ = 0;
	for (int i=0 ; i < MAX_NODE ; i++) 
		nodeColor[i] = 3 ;
        node_ = 0;
}

void
CMUTrace::format_mac_common(Packet *p, const char *why, int offset)
{
	struct hdr_cmn *ch = HDR_CMN(p);
	struct hdr_ip *ih = HDR_IP(p);
	struct hdr_mac802_11 *mh;
	struct hdr_smac *sh;
	char mactype[SMALL_LEN];

	strcpy(mactype, Simulator::instance().macType());
	if (strcmp (mactype, "Mac/SMAC") == 0)
		sh = HDR_SMAC(p);
	else
		mh = HDR_MAC802_11(p);
	
	double x = 0.0, y = 0.0, z = 0.0;
       
	char op = (char) type_;
	Node* thisnode = Node::get_node_by_address(src_);
	double energy = -1;
	if (thisnode) {
	    if (thisnode->energy_model()) {
		    energy = thisnode->energy_model()->energy();
	    }
	}

	// hack the IP address to convert pkt format to hostid format
	// for now until port ids are removed from IP address. -Padma.

	int src = Address::instance().get_nodeaddr(ih->saddr());

	if(tracetype == TR_ROUTER && type_ == SEND) {
		if(src_ != src)
			op = FWRD;
	}

	// use tagged format if appropriate
	if (pt_->tagged()) {
		int next_hop = -1 ;
		Node* nextnode = Node::get_node_by_address(ch->next_hop_);
        	if (nextnode) next_hop = nextnode->nodeid(); 

		node_->getLoc(&x, &y, &z);

		if (op == DROP) op = 'd';
		if (op == SEND) op = '+';
		if (op == FWRD) op = 'h';

		sprintf(pt_->buffer() + offset,
			"%c "TIME_FORMAT" -s %d -d %d -p %s -k %3s -i %d "
			"-N:loc {%.2f %.2f %.2f} -N:en %f ",
			
			op,				// event type
			Scheduler::instance().clock(),	// time
			src_,				// this node
			next_hop,			// next hop
			packet_info.name(ch->ptype()),	// packet type
			tracename,			// trace level
			ch->uid(),			// event id
			x, y, z,			// location
			energy);				// energy

		offset = strlen(pt_->buffer());
		if (strcmp (mactype, "Mac/SMAC") == 0) {
			format_smac(p, offset);
		} else {
			format_mac(p, offset);
		}
		return;
	}


	// Use new ns trace format to replace the old cmu trace format)
	if (newtrace_) {
	    
	    node_->getLoc(&x, &y, &z);
	    // consistence
	    if ( op == DROP ) { op = 'd';}

	        // basic trace infomation + basic exenstion

	    sprintf(pt_->buffer() + offset,
		   "%c -t %.9f -Hs %d -Hd %d -Ni %d -Nx %.2f -Ny %.2f -Nz %.2f -Ne %f -Nl %3s -Nw %s ",
		    op,                       // event type
		    Scheduler::instance().clock(),  // time
		    src_,                           // this node
                    ch->next_hop_,                  // next hop
		    src_,                           // this node
		    x,                              // x coordinate
		    y,                              // y coordinate
		    z,                              // z coordinate
		    energy,                         // energy, -1 = not existing
		    tracename,                      // trace level
                    why);                            // reason

	    // mac layer extension

	    offset = strlen(pt_->buffer());
	    if (strcmp(mactype, "Mac/SMAC") == 0) {
		    format_smac(p, offset);
	    } else {
		    format_mac(p, offset);
	    }
	    return;
	}


#ifdef LOG_POSITION
        x = 0.0, y = 0.0, z = 0.0;
        node_->getLoc(&x, &y, &z);
#endif

	sprintf(pt_->buffer() + offset,
#ifdef LOG_POSITION
		"%c %.9f %d (%6.2f %6.2f) %3s %4s %d %s %d ",
#else
		"%c %.9f _%d_ %3s %4s %d %s %d",
#endif
		op,
		Scheduler::instance().clock(),
                src_,                           // this node
#ifdef LOG_POSITION
                x,
                y,
#endif
		tracename,
		why,
		
                ch->uid(),                      // identifier for this event
		
		((ch->ptype() == PT_MAC) ? (
		  (mh->dh_fc.fc_subtype == MAC_Subtype_RTS) ? "RTS"  :
		  (mh->dh_fc.fc_subtype == MAC_Subtype_CTS) ? "CTS"  :
		  (mh->dh_fc.fc_subtype == MAC_Subtype_ACK) ? "ACK"  :
		  //<zheng: add for 802.15.4>
		  (mh->dh_fc.fc_subtype == MAC_Subtype_Beacon) ? "BCN"  :		//Beacon
		  (mh->dh_fc.fc_subtype == MAC_Subtype_Command_AssoReq) ? "CM1"  :	//CMD: Association request
		  (mh->dh_fc.fc_subtype == MAC_Subtype_Command_AssoRsp) ? "CM2"  :	//CMD: Association response
		  (mh->dh_fc.fc_subtype == MAC_Subtype_Command_DAssNtf) ? "CM3"  :	//CMD: Disassociation notification
		  (mh->dh_fc.fc_subtype == MAC_Subtype_Command_DataReq) ? "CM4"  :	//CMD: Data request
		  (mh->dh_fc.fc_subtype == MAC_Subtype_Command_PIDCNtf) ? "CM5"  :	//CMD: PAN ID conflict notification
		  (mh->dh_fc.fc_subtype == MAC_Subtype_Command_OrphNtf) ? "CM6"  :	//CMD: Orphan notification
		  (mh->dh_fc.fc_subtype == MAC_Subtype_Command_BconReq) ? "CM7"  :	//CMD: Beacon request
		  (mh->dh_fc.fc_subtype == MAC_Subtype_Command_CoorRea) ? "CM8"  :	//CMD: Coordinator realignment
		  (mh->dh_fc.fc_subtype == MAC_Subtype_Command_GTSReq) ? "CM9"  :	//CMD: GTS request
		  //</zheng: add for 802.15.4>
		  "UNKN") :
		 (ch->ptype() == PT_SMAC) ? (
		  (sh->type == RTS_PKT) ? "RTS" :
		  (sh->type == CTS_PKT) ? "CTS" :
		  (sh->type == ACK_PKT) ? "ACK" :
		  (sh->type == SYNC_PKT) ? "SYNC" :
		  "UNKN") : 
		 packet_info.name(ch->ptype())),
		ch->size());
	
	offset = strlen(pt_->buffer());

	if (strncmp (mactype, "Mac/SMAC", 8) == 0) {
		format_smac(p, offset);
	} else {
		format_mac(p, offset);
        }
	
	offset = strlen(pt_->buffer());

	if (thisnode) {
		if (thisnode->energy_model()) {
			sprintf(pt_->buffer() + offset,
				"[energy %f] ",
				thisnode->energy_model()->energy());
		}
        }
}

void
CMUTrace::format_mac(Packet *p, int offset)
{
	struct hdr_mac802_11 *mh = HDR_MAC802_11(p);
	
	if (pt_->tagged()) {
		sprintf(pt_->buffer() + offset,
			"-M:dur %x -M:s %x -M:d %x -M:t %x ",
			mh->dh_duration,		// MAC: duration
			
			// change wrt Mike's code
			//ETHER_ADDR(mh->dh_da),		// MAC: source
			//ETHER_ADDR(mh->dh_sa),		// MAC: destination
			ETHER_ADDR(mh->dh_ra),          // MAC: source
                       ETHER_ADDR(mh->dh_ta),          // MAC: destination


			GET_ETHER_TYPE(mh->dh_body));	// MAC: type
	} else if (newtrace_) {
		sprintf(pt_->buffer() + offset, 
			"-Ma %x -Md %x -Ms %x -Mt %x ",
			mh->dh_duration,
			
			// change wrt Mike's code
			//ETHER_ADDR(mh->dh_da),
			//ETHER_ADDR(mh->dh_sa),

	   		ETHER_ADDR(mh->dh_ra),
	                   ETHER_ADDR(mh->dh_ta),

			GET_ETHER_TYPE(mh->dh_body));
	} else {
		sprintf(pt_->buffer() + offset,
			" [%x %x %x %x] ",
			//*((u_int16_t*) &mh->dh_fc),
			mh->dh_duration,
			
			// change wrt Mike's code
			//ETHER_ADDR(mh->dh_da),
			//ETHER_ADDR(mh->dh_sa),
			ETHER_ADDR(mh->dh_ra),
                        ETHER_ADDR(mh->dh_ta),


			GET_ETHER_TYPE(mh->dh_body));
	}
}

void
CMUTrace::format_smac(Packet *p, int offset)
{
	struct hdr_smac *sh = HDR_SMAC(p);
	sprintf(pt_->buffer() + offset,
		" [%.2f %d %d] ",
		sh->duration,
		sh->dstAddr,
		sh->srcAddr);
}
	

void
CMUTrace::format_ip(Packet *p, int offset)
{
        struct hdr_cmn *ch = HDR_CMN(p);
	struct hdr_ip *ih = HDR_IP(p);
	
	// hack the IP address to convert pkt format to hostid format
	// for now until port ids are removed from IP address. -Padma.
	int src = Address::instance().get_nodeaddr(ih->saddr());
	int dst = Address::instance().get_nodeaddr(ih->daddr());

	if (pt_->tagged()) {
		sprintf(pt_->buffer() + offset,
			"-IP:s %d -IP:sp %d -IP:d %d -IP:dp %d -p %s -e %d "
			"-c %d -i %d -IP:ttl %d ",
			src,                           // packet src
			ih->sport(),                   // src port
			dst,                           // packet dest
			ih->dport(),                   // dst port
			packet_info.name(ch->ptype()), // packet type
			ch->size(),                    // packet size
			ih->flowid(),                  // flow id
			ch->uid(),                     // unique id
			ih->ttl_                       // ttl
			);
	} else if (newtrace_) {
	    sprintf(pt_->buffer() + offset,
		    "-Is %d.%d -Id %d.%d -It %s -Il %d -If %d -Ii %d -Iv %d ",
		    src,                           // packet src
		    ih->sport(),                   // src port
		    dst,                           // packet dest
		    ih->dport(),                   // dst port
		    packet_info.name(ch->ptype()),  // packet type
		    ch->size(),                     // packet size
		    ih->flowid(),                   // flow id
		    ch->uid(),                      // unique id
		    ih->ttl_);                      // ttl
	} else {
	    sprintf(pt_->buffer() + offset, "------- [%d:%d %d:%d %d %d] ",
		src, ih->sport(),
		dst, ih->dport(),
		ih->ttl_, (ch->next_hop_ < 0) ? 0 : ch->next_hop_);
	}
}

// Note:  HDLC format (format_hdlc()) has moved to satellite tracing

void
CMUTrace::format_arp(Packet *p, int offset)
{
	struct hdr_arp *ah = HDR_ARP(p);

	if (pt_->tagged()) {
	    sprintf(pt_->buffer() + offset,
		    "-arp:op %s -arp:ms %d -arp:s %d -arp:md %d -arp:d %d ",
		    ah->arp_op == ARPOP_REQUEST ?  "REQUEST" : "REPLY",
		    ah->arp_sha,
		    ah->arp_spa,
		    ah->arp_tha,
		    ah->arp_tpa);
	} else if (newtrace_) {
	    sprintf(pt_->buffer() + offset,
		    "-P arp -Po %s -Pms %d -Ps %d -Pmd %d -Pd %d ",
		    ah->arp_op == ARPOP_REQUEST ?  "REQUEST" : "REPLY",
		    ah->arp_sha,
		    ah->arp_spa,
		    ah->arp_tha,
		    ah->arp_tpa);
	} else {

	    sprintf(pt_->buffer() + offset,
		"------- [%s %d/%d %d/%d]",
		ah->arp_op == ARPOP_REQUEST ?  "REQUEST" : "REPLY",
		ah->arp_sha,
		ah->arp_spa,
		ah->arp_tha,
		ah->arp_tpa);
	}
}

void
CMUTrace::format_dsr(Packet *p, int offset)
{
	hdr_sr *srh = hdr_sr::access(p);

	if (pt_->tagged()) {
	    sprintf(pt_->buffer() + offset,
		    "-dsr:h %d -dsr:q %d -dsr:s %d -dsr:p %d -dsr:n %d "
		    "-dsr:l %d -dsr:e {%d %d} -dsr:w %d -dsr:m %d -dsr:c %d "
		    "-dsr:b {%d %d} ",
		    srh->num_addrs(),
		    srh->route_request(),
		    srh->rtreq_seq(),
		    srh->route_reply(),
		    srh->rtreq_seq(),
		    srh->route_reply_len(),
		    srh->reply_addrs()[0].addr,
		    srh->reply_addrs()[srh->route_reply_len()-1].addr,
		    srh->route_error(),
		    srh->num_route_errors(),
		    srh->down_links()[srh->num_route_errors() - 1].tell_addr,
		    srh->down_links()[srh->num_route_errors() - 1].from_addr,
		    srh->down_links()[srh->num_route_errors() - 1].to_addr);
	    return;
	} else if (newtrace_) {
	    sprintf(pt_->buffer() + offset, 
		"-P dsr -Ph %d -Pq %d -Ps %d -Pp %d -Pn %d -Pl %d -Pe %d->%d -Pw %d -Pm %d -Pc %d -Pb %d->%d ",
		    srh->num_addrs(),                   // how many nodes travered

		srh->route_request(),
		srh->rtreq_seq(),

		srh->route_reply(),
		srh->rtreq_seq(),
		srh->route_reply_len(),
		// the dest of the src route
		srh->reply_addrs()[0].addr,
		srh->reply_addrs()[srh->route_reply_len()-1].addr,

		srh->route_error(),
		srh->num_route_errors(),
		srh->down_links()[srh->num_route_errors() - 1].tell_addr,
		srh->down_links()[srh->num_route_errors() - 1].from_addr,
		srh->down_links()[srh->num_route_errors() - 1].to_addr);

	   return;
	}
	sprintf(pt_->buffer() + offset, 
		"%d [%d %d] [%d %d %d %d->%d] [%d %d %d %d->%d]",
		srh->num_addrs(),

		srh->route_request(),
		srh->rtreq_seq(),

		srh->route_reply(),
		srh->rtreq_seq(),
		srh->route_reply_len(),
		// the dest of the src route
		srh->reply_addrs()[0].addr,
		srh->reply_addrs()[srh->route_reply_len()-1].addr,

		srh->route_error(),
		srh->num_route_errors(),
		srh->down_links()[srh->num_route_errors() - 1].tell_addr,
		srh->down_links()[srh->num_route_errors() - 1].from_addr,
		srh->down_links()[srh->num_route_errors() - 1].to_addr);
}

void
CMUTrace::format_msg(Packet *, int)
{
}

void
CMUTrace::format_tcp(Packet *p, int offset)
{
	struct hdr_cmn *ch = HDR_CMN(p);
	struct hdr_tcp *th = HDR_TCP(p);
	
	if (pt_->tagged()) {
	    sprintf(pt_->buffer() + offset,
		    "-tcp:s %d -tcp:a %d -tcp:f %d -tcp:o %d ",
		    th->seqno_,
		    th->ackno_,
		    ch->num_forwards(),
		    ch->opt_num_forwards());
	} else if (newtrace_) {
	    sprintf(pt_->buffer() + offset,
		"-Pn tcp -Ps %d -Pa %d -Pf %d -Po %d ",
		th->seqno_,
		th->ackno_,
		ch->num_forwards(),
		ch->opt_num_forwards());

	} else {
	    sprintf(pt_->buffer() + offset,
		"[%d %d] %d %d",
		th->seqno_,
		th->ackno_,
		ch->num_forwards(),
		ch->opt_num_forwards());
	}
}

/* Armando L. Caro Jr. <acaro@@cis,udel,edu> 6/5/2002
 * (with help from Florina Almenárez <florina@@it,uc3m,es>)
 */
void
CMUTrace::format_sctp(Packet* p,int offset)
{
	struct hdr_cmn *ch = HDR_CMN(p);
	struct hdr_sctp *sh = HDR_SCTP(p);
	//struct hdr_ip *ih = HDR_IP(p);
	char cChunkType;
  
	for(u_int i = 0; i < sh->NumChunks(); i++) {
		switch(sh->SctpTrace()[i].eType) {
		case SCTP_CHUNK_INIT:
		case SCTP_CHUNK_INIT_ACK:
		case SCTP_CHUNK_COOKIE_ECHO:
		case SCTP_CHUNK_COOKIE_ACK:
			cChunkType = 'I';       // connection initialization
			break;
      
		case SCTP_CHUNK_DATA:
			cChunkType = 'D';
			break;
      
		case SCTP_CHUNK_SACK:
			cChunkType = 'S';
			break;
      
		case SCTP_CHUNK_FORWARD_TSN:
			cChunkType = 'R';
			break;

		case SCTP_CHUNK_HB:
			cChunkType = 'H';
			break;
			
		case SCTP_CHUNK_HB_ACK:
			cChunkType = 'B';
			break;
		default:
			// quiet compiler
			cChunkType = ' ';
			assert (false);
			break;
		}
    
		if( newtrace_ ) {
			sprintf(pt_->buffer() + offset,
				"-Pn sctp -Pnc %d -Pct %c "
				"-Ptsn %d -Psid %d -Pssn %d "
				"-Pf %d -Po %d ",
				sh->NumChunks(),
				cChunkType,
				sh->SctpTrace()[i].uiTsn,
				sh->SctpTrace()[i].usStreamId,
				sh->SctpTrace()[i].usStreamSeqNum,
				ch->num_forwards(),
				ch->opt_num_forwards());
		}
		else {
			sprintf(pt_->buffer() + offset,
				"[%d %c %d %d %d] %d %d",
				sh->NumChunks(),
				cChunkType,
				sh->SctpTrace()[i].uiTsn,
				sh->SctpTrace()[i].usStreamId,
				sh->SctpTrace()[i].usStreamSeqNum,
				ch->num_forwards(),
				ch->opt_num_forwards());
		}
	}
}

void
CMUTrace::format_rtp(Packet *p, int offset)
{
	struct hdr_cmn *ch = HDR_CMN(p);
	struct hdr_rtp *rh = HDR_RTP(p);
	struct hdr_ip *ih = HDR_IP(p);
        Node* thisnode = Node::get_node_by_address(src_);

	//hacking, needs to change later, 
        int dst = Address::instance().get_nodeaddr(ih->daddr());
	
	if (dst == src_){
		// I just received a cbr data packet
		if (thisnode->energy_model() && 
		    thisnode->energy_model()->powersavingflag()) {
			thisnode->energy_model()->set_node_state(EnergyModel::INROUTE);
		}
        }

	if (pt_->tagged()) {
		sprintf(pt_->buffer() + offset,
			"-cbr:s %d -cbr:f %d -cbr:o %d ",
			rh->seqno_,
			ch->num_forwards(),
			ch->opt_num_forwards());
	} else if (newtrace_) {
		sprintf(pt_->buffer() + offset,
			"-Pn cbr -Pi %d -Pf %d -Po %d ",
			rh->seqno_,
			ch->num_forwards(),
			ch->opt_num_forwards());
	} else {
		sprintf(pt_->buffer() + offset,
			"[%d] %d %d",
			rh->seqno_,
			ch->num_forwards(),
			ch->opt_num_forwards());
	}
}

void
CMUTrace::format_imep(Packet *p, int offset)
{
        struct hdr_imep *im = HDR_IMEP(p);

#define U_INT16_T(x)    *((u_int16_t*) &(x))

	if (pt_->tagged()) {
	    sprintf(pt_->buffer() + offset,
		    "-imep:a %c -imep:h %c -imep:o %c -imep:l %04x ",
		    (im->imep_block_flags & BLOCK_FLAG_ACK) ? 'A' : '-',
                    (im->imep_block_flags & BLOCK_FLAG_HELLO) ? 'H' : '-',
                    (im->imep_block_flags & BLOCK_FLAG_OBJECT) ? 'O' : '-',
                    U_INT16_T(im->imep_length));
	} else if (newtrace_) {
	    sprintf(pt_->buffer() + offset,
                "-P imep -Pa %c -Ph %c -Po %c -Pl 0x%04x ] ",
                (im->imep_block_flags & BLOCK_FLAG_ACK) ? 'A' : '-',
                (im->imep_block_flags & BLOCK_FLAG_HELLO) ? 'H' : '-',
                (im->imep_block_flags & BLOCK_FLAG_OBJECT) ? 'O' : '-',
                U_INT16_T(im->imep_length));
	} else {
            sprintf(pt_->buffer() + offset,
                "[%c %c %c 0x%04x] ",
                (im->imep_block_flags & BLOCK_FLAG_ACK) ? 'A' : '-',
                (im->imep_block_flags & BLOCK_FLAG_HELLO) ? 'H' : '-',
                (im->imep_block_flags & BLOCK_FLAG_OBJECT) ? 'O' : '-',
                U_INT16_T(im->imep_length));
	}
#undef U_INT16_T
}


void
CMUTrace::format_tora(Packet *p, int offset)
{
        struct hdr_tora *th = HDR_TORA(p);
        struct hdr_tora_qry *qh = HDR_TORA_QRY(p);
        struct hdr_tora_upd *uh = HDR_TORA_UPD(p);
        struct hdr_tora_clr *ch = HDR_TORA_CLR(p);

        switch(th->th_type) {

        case TORATYPE_QRY:

		if (pt_->tagged()) {
		    sprintf(pt_->buffer() + offset,
			    "-tora:t %x -tora:d %d -tora:c QUERY",
			    qh->tq_type, qh->tq_dst);
		} else if (newtrace_) {
		    sprintf(pt_->buffer() + offset,
			"-P tora -Pt 0x%x -Pd %d -Pc QUERY ",
                        qh->tq_type, qh->tq_dst);
			
                } else {

                    sprintf(pt_->buffer() + offset, "[0x%x %d] (QUERY)",
                        qh->tq_type, qh->tq_dst);
		}
                break;

        case TORATYPE_UPD:

		if (pt_->tagged()) {
		    sprintf(pt_->buffer() + offset,
			    "-tora:t %x -tora:d %d -tora:a %f -tora:o %d "
			    "-tora:r %d -tora:e %d -tora:i %d -tora:c UPDATE",
			    uh->tu_type,
                            uh->tu_dst,
                            uh->tu_tau,
                            uh->tu_oid,
                            uh->tu_r,
                            uh->tu_delta,
                            uh->tu_id);
		} else if (newtrace_) {
		    sprintf(pt_->buffer() + offset,
                        "-P tora -Pt 0x%x -Pd %d (%f %d %d %d %d) -Pc UPDATE ",
                        uh->tu_type,
                        uh->tu_dst,
                        uh->tu_tau,
                        uh->tu_oid,
                        uh->tu_r,
                        uh->tu_delta,
                        uh->tu_id);
		} else {
                    sprintf(pt_->buffer() + offset,
                        "-Pt 0x%x -Pd %d -Pa %f -Po %d -Pr %d -Pe %d -Pi %d -Pc UPDATE ",
                        uh->tu_type,
                        uh->tu_dst,
                        uh->tu_tau,
                        uh->tu_oid,
                        uh->tu_r,
                        uh->tu_delta,
                        uh->tu_id);
		}
                break;

        case TORATYPE_CLR:
		if (pt_->tagged()) {
		    sprintf(pt_->buffer() + offset,
			    "-tora:t %x -tora:d %d -tora:a %f -tora:o %d "
			    "-tora:c CLEAR ",
			    ch->tc_type,
                            ch->tc_dst,
                            ch->tc_tau,
                            ch->tc_oid);
		} else if (newtrace_) {
		    sprintf(pt_->buffer() + offset, 
			"-P tora -Pt 0x%x -Pd %d -Pa %f -Po %d -Pc CLEAR ",
                        ch->tc_type,
                        ch->tc_dst,
                        ch->tc_tau,
                        ch->tc_oid);
		} else {
                    sprintf(pt_->buffer() + offset, "[0x%x %d %f %d] (CLEAR)",
                        ch->tc_type,
                        ch->tc_dst,
                        ch->tc_tau,
                        ch->tc_oid);
		}
                break;
        }
}

void
CMUTrace::format_aodv(Packet *p, int offset)
{
        struct hdr_aodv *ah = HDR_AODV(p);
        struct hdr_aodv_request *rq = HDR_AODV_REQUEST(p);
        struct hdr_aodv_reply *rp = HDR_AODV_REPLY(p);


        switch(ah->ah_type) {
        case AODVTYPE_RREQ:

		if (pt_->tagged()) {
		    sprintf(pt_->buffer() + offset,
			    "-aodv:t %x -aodv:h %d -aodv:b %d -aodv:d %d "
			    "-aodv:ds %d -aodv:s %d -aodv:ss %d "
			    "-aodv:c REQUEST ",
			    rq->rq_type,
                            rq->rq_hop_count,
                            rq->rq_bcast_id,
                            rq->rq_dst,
                            rq->rq_dst_seqno,
                            rq->rq_src,
                            rq->rq_src_seqno);
		} else if (newtrace_) {

		    sprintf(pt_->buffer() + offset,
			"-P aodv -Pt 0x%x -Ph %d -Pb %d -Pd %d -Pds %d -Ps %d -Pss %d -Pc REQUEST ",
			rq->rq_type,
                        rq->rq_hop_count,
                        rq->rq_bcast_id,
                        rq->rq_dst,
                        rq->rq_dst_seqno,
                        rq->rq_src,
                        rq->rq_src_seqno);


		} else {

		    sprintf(pt_->buffer() + offset,
			"[0x%x %d %d [%d %d] [%d %d]] (REQUEST)",
			rq->rq_type,
                        rq->rq_hop_count,
                        rq->rq_bcast_id,
                        rq->rq_dst,
                        rq->rq_dst_seqno,
                        rq->rq_src,
                        rq->rq_src_seqno);
		}
                break;

        case AODVTYPE_RREP:
        case AODVTYPE_HELLO:
	case AODVTYPE_RERR:
		
		if (pt_->tagged()) {
		    sprintf(pt_->buffer() + offset,
			    "-aodv:t %x -aodv:h %d -aodv:d %d -adov:ds %d "
			    "-aodv:l %f -aodv:c %s ",
			    rp->rp_type,
			    rp->rp_hop_count,
			    rp->rp_dst,
			    rp->rp_dst_seqno,
			    rp->rp_lifetime,
			    rp->rp_type == AODVTYPE_RREP ? "REPLY" :
			    (rp->rp_type == AODVTYPE_RERR ? "ERROR" :
			     "HELLO"));
		} else if (newtrace_) {
			
			sprintf(pt_->buffer() + offset,
			    "-P aodv -Pt 0x%x -Ph %d -Pd %d -Pds %d -Pl %f -Pc %s ",
				rp->rp_type,
				rp->rp_hop_count,
				rp->rp_dst,
				rp->rp_dst_seqno,
				rp->rp_lifetime,
				rp->rp_type == AODVTYPE_RREP ? "REPLY" :
				(rp->rp_type == AODVTYPE_RERR ? "ERROR" :
				 "HELLO"));
	        } else {
			
			sprintf(pt_->buffer() + offset,
				"[0x%x %d [%d %d] %f] (%s)",
				rp->rp_type,
				rp->rp_hop_count,
				rp->rp_dst,
				rp->rp_dst_seqno,
				rp->rp_lifetime,
				rp->rp_type == AODVTYPE_RREP ? "REPLY" :
				(rp->rp_type == AODVTYPE_RERR ? "ERROR" :
				 "HELLO"));
		}
                break;
		
        default:
#ifdef WIN32
                fprintf(stderr,
		        "CMUTrace::format_aodv: invalid AODV packet type\n");
#else
		fprintf(stderr,
		        "%s: invalid AODV packet type\n", __FUNCTION__);
#endif
                abort();
        }
}

void
CMUTrace::nam_format(Packet *p, int offset)
{
	Node* srcnode = 0 ;
	Node* dstnode = 0 ;
	Node* nextnode = 0 ;
        struct hdr_cmn *ch = HDR_CMN(p);
	struct hdr_ip *ih = HDR_IP(p);
	char op = (char) type_;
	char colors[32];
	int next_hop = -1 ;

// change wrt Mike's code
	assert(type_ != EOT);



	//<zheng: add for 802.15.4>

	//Actually we only need to handle MAC layer for nam (but should display dropping for other layers)
	//if (strcmp(tracename,"MAC") != 0)
	//if ((op != 'D')&&(op != 'd'))
	//	return;

	struct hdr_mac802_11 *mh = HDR_MAC802_11(p);
	char ptype[11];
	strcpy(ptype,
	((ch->ptype() == PT_MAC) ? (
	  (mh->dh_fc.fc_subtype == MAC_Subtype_RTS) ? "RTS"  :
	  (mh->dh_fc.fc_subtype == MAC_Subtype_CTS) ? "CTS"  :
	  (mh->dh_fc.fc_subtype == MAC_Subtype_ACK) ? "ACK"  :
	  (mh->dh_fc.fc_subtype == MAC_Subtype_Beacon) ? "BCN"  :		//Beacon
	  (mh->dh_fc.fc_subtype == MAC_Subtype_Command_AssoReq) ? "CM1"  :	//CMD: Association request
	  (mh->dh_fc.fc_subtype == MAC_Subtype_Command_AssoRsp) ? "CM2"  :	//CMD: Association response
	  (mh->dh_fc.fc_subtype == MAC_Subtype_Command_DAssNtf) ? "CM3"  :	//CMD: Disassociation notification
	  (mh->dh_fc.fc_subtype == MAC_Subtype_Command_DataReq) ? "CM4"  :	//CMD: Data request
	  (mh->dh_fc.fc_subtype == MAC_Subtype_Command_PIDCNtf) ? "CM5"  :	//CMD: PAN ID conflict notification
	  (mh->dh_fc.fc_subtype == MAC_Subtype_Command_OrphNtf) ? "CM6"  :	//CMD: Orphan notification
	  (mh->dh_fc.fc_subtype == MAC_Subtype_Command_BconReq) ? "CM7"  :	//CMD: Beacon request
	  (mh->dh_fc.fc_subtype == MAC_Subtype_Command_CoorRea) ? "CM8"  :	//CMD: Coordinator realignment
	  (mh->dh_fc.fc_subtype == MAC_Subtype_Command_GTSReq) ? "CM9"  :	//CMD: GTS request
	  "UNKN"
	  ) : packet_info.name(ch->ptype())));
	//</zheng: add for 802.15.4>
        int dst = Address::instance().get_nodeaddr(ih->daddr());

	nextnode = Node::get_node_by_address(ch->next_hop_);
        if (nextnode) next_hop = nextnode->nodeid(); 

	srcnode = Node::get_node_by_address(src_);
	dstnode = Node::get_node_by_address(ch->next_hop_);

	double distance = 0;

        if ((srcnode) && (dstnode)) {
	   MobileNode* tmnode = (MobileNode*)srcnode;
	   MobileNode* rmnode = (MobileNode*)dstnode;

	   distance = tmnode->propdelay(rmnode) * 300000000 ;
	}

	double energy = -1;
	double initenergy = -1;

	//default value for changing node color with respect to energy depletion
	double l1 = 0.5; 
	double l2 = 0.2;
	
	if (srcnode) {
	    if (srcnode->energy_model()) {
		    energy = srcnode->energy_model()->energy();
		    initenergy = srcnode->energy_model()->initialenergy();
		    l1 = srcnode->energy_model()->level1();
		    l2 = srcnode->energy_model()->level2();
	    }
	}

        int energyLevel = 0 ;
        double energyLeft = (double)(energy/initenergy) ;

        if ((energyLeft <= 1 ) && (energyLeft >= l1 )) energyLevel = 3;	
        if ((energyLeft >= l2 ) && (energyLeft < l1 )) energyLevel = 2;	
        if ((energyLeft > 0 ) && (energyLeft < l2 )) energyLevel = 1;	

	if (energyLevel == 0) 
		strcpy(colors,"-c black -o red");
        else if (energyLevel == 1) 
		strcpy(colors,"-c red -o yellow");
        else if (energyLevel == 2) 
		strcpy(colors,"-c yellow -o green");
        else if (energyLevel == 3) 
		strcpy(colors,"-c green -o black");

	// A simple hack for scadds demo (fernandez's visit) -- Chalermek
	int pkt_color = 0;
	if (ch->ptype()==PT_DIFF) {
		hdr_cdiff *dfh= HDR_CDIFF(p);
		if (dfh->mess_type != DATA) {
			pkt_color = 1;
		}
	}

	//<zheng: add for 802.15.4>
	if (Nam802_15_4::Nam_Status)
	{
		ATTRIBUTELINK *attr;
		int t_src,t_dst;
		if (ch->ptype() == PT_MAC)
		{
			t_src = p802_15_4macSA(p);
			t_dst = p802_15_4macDA(p);;
		}
		else
		{
			t_src = HDR_IP(p)->saddr();
			t_dst = HDR_IP(p)->daddr();
		}
		attr = findAttrLink(HDR_CMN(p)->ptype(),t_src,t_dst);
		if (attr == NULL)
			attr = findAttrLink(HDR_CMN(p)->ptype());
		if (attr != NULL)
			HDR_LRWPAN(p)->attribute = attr->attribute;
		else
			HDR_LRWPAN(p)->attribute = 0;
		if (HDR_LRWPAN(p)->attribute >= 32)
			pkt_color = HDR_LRWPAN(p)->attribute;
	}
	//</zheng: add for 802.15.4>

	// convert to nam format 
	if (op == 's') op = 'h' ;
	if (op == 'D') op = 'd' ;
	if (op == 'h') {
		sprintf(pt_->nbuffer(),
			"+ -t %.9f -s %d -d %d -p %s -e %d -c 2 -a %d -i %d -k %3s ",
			Scheduler::instance().clock(),
			src_,                           // this node
			next_hop,
			ptype,			//<zheng: modify for 802.15.4>packet_info.name(ch->ptype()),
			ch->size(),
			pkt_color,   
			ch->uid(),
			tracename);

		offset = strlen(pt_->nbuffer());
		pt_->namdump();
		sprintf(pt_->nbuffer() ,
			"- -t %.9f -s %d -d %d -p %s -e %d -c 2 -a %d -i %d -k %3s",
			Scheduler::instance().clock(),
			src_,                           // this node
			next_hop,
			ptype,			//<zheng: modify for 802.15.4>packet_info.name(ch->ptype()),
			ch->size(),
			pkt_color,
			ch->uid(),
			tracename);
		
		offset = strlen(pt_->nbuffer());
		pt_->namdump();
	}

        // if nodes are too far from each other
	// nam won't dump SEND event 'cuz it's
	// gonna be dropped later anyway
	// this value 250 is pre-calculated by using 
	// two-ray ground refelction model with fixed
	// transmission power 3.652e-10
//	if ((type_ == SEND)  && (distance > 250 )) return ;

	if(tracetype == TR_ROUTER && type_ == RECV && dst != -1 ) return ;
	if(type_ == RECV && dst == -1 )dst = src_ ; //broadcasting event

        if (energy != -1) { //energy model being turned on
	   if (src_ >= MAX_NODE) {
		   fprintf (stderr, "node id must be < %d\n",
			    MAX_NODE);
	       exit(0);
	   }
	   if (nodeColor[src_] != energyLevel ) { //only dump it when node  
	       sprintf(pt_->nbuffer() ,                    //color change
	          "n -t %.9f -s %d -S COLOR %s",
	           Scheduler::instance().clock(),
	           src_,                           // this node
	           colors);
               offset = strlen(pt_->nbuffer());
               pt_->namdump();
	       nodeColor[src_] = energyLevel ;
	    }   
        }

	sprintf(pt_->nbuffer() ,
		"%c -t %.9f -s %d -d %d -p %s -e %d -c 2 -a %d -i %d -k %3s",
		op,
		Scheduler::instance().clock(),
		src_,                           // this node
		next_hop,
		ptype,			//<zheng: modify for 802.15.4>packet_info.name(ch->ptype()),
		ch->size(),
		pkt_color,
		ch->uid(),
		tracename);

//<zheng: ns 2.27 removed the following part, but we need it to control the broadcast radius>
if (Nam802_15_4::Nam_Status)
{
	if ((strcmp(tracename, "AGT") != 0) || ((u_int32_t)(ih->daddr()) == IP_BROADCAST))		//<zheng: add: next_hop info not available at agent level>
											//(doesn't really matter -- seems agent level has no effect on nam)
	if (next_hop == -1 && op == 'h') {
		// print extra fields for broadcast packets

		// bradius is calculated assuming 2-ray ground reflectlon
		// model using default settings of Phy/WirelessPhy and
		// Antenna/OmniAntenna
		if (bradius == 0.0) calculate_broadcast_parameters();

		double radius = bradius*radius_scaling_factor_; 

		// duration is calculated based on the radius and
		// the speed of light (299792458 m/s)
		double duration = (bradius/299792458.0)*duration_scaling_factor_;
		//<zheng: add -- the duration in 802.15.4 could be very small and rounded to 0.0>
		if (Nam802_15_4::Nam_Status)
		if (duration < 0.000000001)
			duration = 0.000000001;
		//</zheng: add>
		sprintf(pt_->nbuffer() + strlen(pt_->nbuffer()),
			" -R %.2f -D %.2f",
			radius,
			duration);
	}
}
//</zheng>

	offset = strlen(pt_->nbuffer());
	pt_->namdump();
}

void CMUTrace::format(Packet* p, const char *why)
{
	hdr_cmn *ch = HDR_CMN(p);
	int offset = 0;

	/*
	 * Log the MAC Header
	 */
	format_mac_common(p, why, offset);

	if (pt_->namchannel()) 
		nam_format(p, offset);
	offset = strlen(pt_->buffer());
	switch(ch->ptype()) {
	case PT_MAC:
	case PT_SMAC:
		break;
	case PT_ARP:
		format_arp(p, offset);
		break;
	default:
		format_ip(p, offset);
		offset = strlen(pt_->buffer());
		switch(ch->ptype()) {
		case PT_AODV:
			format_aodv(p, offset);
			break;
		case PT_TORA:
                        format_tora(p, offset);
                        break;
                case PT_IMEP:
                        format_imep(p, offset);
                        break;
		case PT_DSR:
			format_dsr(p, offset);
			break;
		case PT_MESSAGE:
		case PT_UDP:
			format_msg(p, offset);
			break;
		case PT_TCP:
		case PT_ACK:
			format_tcp(p, offset);
			break;
		case PT_SCTP:
			/* Armando L. Caro Jr. <acaro@@cis,udel,edu> 6/5/2002
			 */
			format_sctp(p, offset);
			break;
		case PT_CBR:
			format_rtp(p, offset);
			break;
	        case PT_DIFF:
			break;
		case PT_GAF:
		case PT_PING:
			break;
		default:
		/*<zheng: del -- there are many more new packet types added, like PT_EXP (poisson traffic belongs to this type)>
			fprintf(stderr, "%s - invalid packet type (%s).\n",
				__PRETTY_FUNCTION__, packet_info.name(ch->ptype()));
			exit(1);
		</zheng: del>*/
			break;		//zheng: add
		}
	}
}

int
CMUTrace::command(int argc, const char*const* argv)
{
	
        if(argc == 3) {
                if(strcmp(argv[1], "node") == 0) {
                        node_ = (MobileNode*) TclObject::lookup(argv[2]);
                        if(node_ == 0)
                                return TCL_ERROR;
                        return TCL_OK;
                }
		if (strcmp(argv[1], "newtrace") == 0) {
			newtrace_ = atoi(argv[2]);
		        return TCL_OK;
		}
        }
	return Trace::command(argc, argv);
}

/*ARGSUSED*/
void
CMUTrace::recv(Packet *p, Handler *h)
{
	if (!node_energy()) {
		Packet::free(p);
		return;
	}
        assert(initialized());
        /*
         * Agent Trace "stamp" the packet with the optimal route on
         * sending.
         */
        if (tracetype == TR_AGENT && type_ == SEND) {
                God::instance()->stampPacket(p);
        }
#if 0
        /*
         * When the originator of a packet drops the packet, it may or may
         * not have been stamped by GOD.  Stamp it before logging the
         * information.
         */
        if(src_ == src && type_ == DROP) {
                God::instance()->stampPacket(p);
        }
#endif
	format(p, "---");
	pt_->dump();
	//namdump();
	if(target_ == 0)
		Packet::free(p);
	else
		send(p, h);
}

void
CMUTrace::recv(Packet *p, const char* why)
{
        assert(initialized() && type_ == DROP);
	if (!node_energy()) {
		Packet::free(p);
		return;
	}
#if 0
        /*
         * When the originator of a packet drops the packet, it may or may
         * not have been stamped by GOD.  Stamp it before logging the
         * information.
         */
        if(src_ == ih->saddr()) {
                God::instance()->stampPacket(p);
        }
#endif
	format(p, why);
	pt_->dump();
	//namdump();
	Packet::free(p);
}

int CMUTrace::node_energy()
{
	Node* thisnode = Node::get_node_by_address(src_);
	double energy = 1;
	if (thisnode) {
		if (thisnode->energy_model()) {
			energy = thisnode->energy_model()->energy();
		}
	} 
	if (energy > 0) return 1;
	return 0;
}

//<zheng: ns 2.27 removed the following part, but we need it to control the broadcast radius>
void CMUTrace::calculate_broadcast_parameters() {
	// Calculate the maximum distance at which a packet can be received
	// based on the two-ray reflection model using the current default
	// values for Phy/WirelessPhy and Antenna/OmniAntenna.

	double P_t, P_r, G_t, G_r, h, L;
	Tcl& tcl = Tcl::instance();

	tcl.evalc("Phy/WirelessPhy set Pt_");
	P_t = atof(tcl.result());
	tcl.evalc("Phy/WirelessPhy set RXThresh_");
	P_r = atof(tcl.result());
	tcl.evalc("Phy/WirelessPhy set L_");
	L = atof(tcl.result());
	tcl.evalc("Antenna/OmniAntenna set Gt_");
	G_t = atof(tcl.result());
	tcl.evalc("Antenna/OmniAntenna set Gr_");
	G_r = atof(tcl.result());
	tcl.evalc("Antenna/OmniAntenna set Z_");
	h = atof(tcl.result());
	bradius = pow(P_t*G_r*G_t*pow(h,4.0)/(P_r*L), 0.25);
	//<zheng: add for 802.15.4>
	//the above calculation is not accurate for short distance
	double PI,freq,lambda,crossover_dist;
	PI = 3.14159265359;
	tcl.evalc("Phy/WirelessPhy set freq_");
	freq = atof(tcl.result());
	lambda = 3.0e8/freq;
	crossover_dist = (4 * PI * h * h) / lambda;
	if (bradius < crossover_dist)	//need re-calculation
		bradius = pow(P_t * G_r * G_t * pow(lambda, 2.0)/(P_r * L), 0.5)/(4 * PI);
	//</zheng: add for 802.15.4>

	// Also get the scaling factors
	tcl.evalc("CMUTrace set radius_scaling_factor_");
	radius_scaling_factor_ = atof(tcl.result());
	tcl.evalc("CMUTrace set duration_scaling_factor_");
	duration_scaling_factor_ = atof(tcl.result());
}
//</zheng>


syntax highlighted by Code2HTML, v. 0.9.1