/* -*-	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.
*/

#ifndef ns_ll_nodeb_h
#define ns_ll_nodeb_h

#include "marshall.h"
#include <connector.h>
#include <random.h>
#include <mac.h>

#include "packet.h"
#include "delay.h"
#include "queue.h"
#include "rlc-umts-nodeb.h"
#include "phy-umts-nodeb.h"
#include "linked-list.h"
#include "ll-flows.h"
#include "tree.h"
#include "bsfc-queue.h"
#include <ll.h>
#include <arp.h>
#include <classifier.h>
#include <lanRouter.h>
#include <varp.h>


/*************************** Timers *****************************/
class LLNodeb;
class AppsNodebTimer : public Handler {
public:
	AppsNodebTimer(LLNodeb* m, double s = 0) : ll(m), Next(0) {
		busy_ = paused_ = 0;stime = rtime = 0.0; src_ = -1; dest_ = -1; size_ = 0; flow_ = -1;}

	virtual void handle(Event *e);
	virtual void start(double time);
	virtual void stop(void);
	virtual void pause(void) { assert(0); }
	virtual void resume(void) { assert(0); }

	LLNodeb	*ll;
  	int 		flow_;
	nsaddr_t	src_;
	nsaddr_t	dest_;
	int 		size_;
	int		busy_;
	int		paused_;
	Event		intr;
	double	stime;	// start time
	double	rtime;	// remaining time
	AppsNodebTimer* 	Next;
};

typedef AppsNodebTimer* TimerNodebPtr;

class TNodebList
{ public:
  TNodebList(LLNodeb* m) : ll(m) {
   	Head = new AppsNodebTimer(ll); Tail=Head; CurrentPtr = Head;
  }
  ~TNodebList();
  TimerNodebPtr Previous(TimerNodebPtr);
  void AddANode(nsaddr_t src, nsaddr_t dest, int size, int flow);
  void DeleteANode(TimerNodebPtr);
  void DeleteANode(nsaddr_t src, int flow);
  TimerNodebPtr GetNode(nsaddr_t src, int flow);

  void start(nsaddr_t src, int flow, double time);
  void stop(nsaddr_t src, int flow);

  LLNodeb *ll;
  TimerNodebPtr Head,Tail,CurrentPtr;
};

class ReleaseUETimer : public Handler {
public:
	ReleaseUETimer(LLNodeb* m, double s = 0) : ll(m), Next(0) {
		busy_ = paused_ = 0; stime = rtime = 0.0; addr_ = -1;
	}

	void handle(Event *e);
	void start(double time);
	void stop(void);
	virtual void pause(void) { assert(0); }
	virtual void resume(void) { assert(0); }
	inline int busy(void) { return busy_; }
	inline int paused(void) { return paused_; }

	LLNodeb	*ll;
	int	busy_;
	int	paused_;
	Event intr;
	double stime;	// start time
	double rtime;	// remaining time
  	int addr_;
	ReleaseUETimer* 	Next;
};

typedef ReleaseUETimer* RTimerNodebPtr;

class RTNodebList
{ public:
  RTNodebList(LLNodeb* m) : ll(m) {
   	Head = new ReleaseUETimer(ll); Tail=Head; CurrentPtr = Head;
  }
  ~RTNodebList();
  RTimerNodebPtr Previous(RTimerNodebPtr);
  void AddANode(int addr);
  void DeleteANode(RTimerNodebPtr);
  void DeleteANode(int addr);
  RTimerNodebPtr GetNode(int addr);

  void start(int addr, double time);
  void stop(int addr);

  LLNodeb *ll;
  RTimerNodebPtr Head,Tail,CurrentPtr;
};

///////////////////////////////////////////////////////////////////////////////
/*                             Class LLNodeb                                 */
///////////////////////////////////////////////////////////////////////////////



struct location {		// for the static matrix
	nsaddr_t ue_;	// UE IP address
	nsaddr_t nodeb_;	// Node B IP address at which is attached the UE
};

struct loc {	// for internal purposes
	nsaddr_t ipaddr_;		// UE IP address
	int phyaddr_;		// UE physical address
	int dltempsf_;		// spreading factor temporal storage
	int dlsf_;			// downlink spreading factor assigned to that UE
	int dlfreq_;		// downlink frequence assigned to that UE
	double dltotalr_;		// downlink total physical rate to that UE
};

class LLNodeb : public LL {
	friend class ReleaseUETimer;
	friend class AppsNodebTimer;
	friend class TNodebList;
	friend class RTNodebList;
public:
	LLNodeb();
	virtual void recv(Packet* p, Handler* h);
	int register_ue(Packet* p);	// registers the new UE in its internal structure
	int alloc_res(Packet* p, int sf); // allocate resources in the OSVF trees
	int getcode(nsaddr_t src, int flow);// returns the code for applications originated in fixed nodes
	int cal_sf(Packet* p);		// calculates the spreading factor for a given packet
	int cal_sf(double rate);	// calculates the spreading factor for a given physical rate
	double get_bytes(int pos);    // gets bytes per tti for a specific user in IFQ
	int remove_res(int pos);	// remove resources in the OSVF trees
	void update_db(Packet* p);	// update the static structure registry_
	void send_setup_reply(Packet* p);   // send SETUP REPLY message for setup procedure and handover
	void send_release_reply(Packet* p);	// send release reply message
	int getip(nsaddr_t addr);	// get the IP address
	void pag_proc(Packet* p, int pos); // send paging message to the destination of the incoming flow
	void send_to_ra(Packet* p);	// send the packet to the routing agent
	void send_res_reply(Packet* p, int state);	// send the resource reply message or a failure
	void send_msg(Packet* p, int loc);	// send data packets downwards
	int free_res(Packet* p);// allocate downlink resources in this layer and OSVF trees if there are
					// enough available (returns 1) and returns 0 if not.
	double phy_rate(double rate, int code, int psize); // returns the physical rate corresponding for the user rate,
									   // channel coding and packet size passed
	int look_for(nsaddr_t ad);	// returns the position in ue_info_[] for that UE
	int ue_node(nsaddr_t source);
	void update_nodeb(Packet* p, nsaddr_t addr); // send a HANDOVER message to the former Nodeb for the incoming UE
	virtual void update_flow(int acked, int fraged, int flowid, nsaddr_t src, int handover); // give resources
												// in RLC layer for the flow
	virtual void configure_flow(int flow, const char*const& type , double rate) {} //
	void remove_flow_ul(Packet* p, int pos);	// remove the uplink resources allocated for that flow
	void remove_flow_dl(Packet* p);	// remove downlink resources allocated for that flow
	void filltype(Packet* p);	// fill rlc header fields: frag_ and ack_ depending on the flow
	void update_phy_bytes_slot(int pos);	// calculate and update bytes in physical layer
	void RemoveUEHandler(int addr); // handler for ReleaseUETimer, release resources in physical layer and mac layer
	void RemoveFlowHandler(nsaddr_t src, nsaddr_t dest, int size, int flow); // handler for AppsNodebTimer
								// (contained in TNodebList), send a Connection release message

      inline PhyUmtsNodeb *phy() { return phy_; }


protected:
	int command(int argc, const char*const* argv);

	PhyUmtsNodeb* phy_;			// physical layer
	RlcUmtsNodeb* rlc_;			// rlc layer
	BsFCQueue* ifq_;				// ifq
	static location registry_[MAX_NUM_NODES];	// matrix with all UE location in every moment.
	loc ue_info_[MAX_NUM_UE];	// information about the UEs in our cell

	tree *dl_sftree[MAX_NUM_FREQ];	// OVSF trees

	nsaddr_t ip_nodeb_;		// our IP address
	FlowList *flows_; 		// information about stored flows
	PacketQueue *fl_;			// temporary storage of the fixed flows waiting for paging response
	linked_list *refused;		// list with the refused flows

	TNodebList	*removeflow_;	// list of timers to detect when an application has finished
	RTNodebList	*remove_;	// list of timers to release physical resources for a UE

	static int verbose_;
	Event intr;
};

#endif
