/* -*-	Mode:C++; c-basic-offset:8; tab-width:8; indent-tabs-mode:t -*- */
//
// Copyright (c) 1997 by the University of Southern California
// All rights reserved.
//
// Permission to use, copy, modify, and distribute this software and its
// documentation in source and binary forms for non-commercial purposes
// and without fee is hereby granted, provided that the above copyright
// notice appear in all copies and that both the copyright notice and
// this permission notice appear in supporting documentation. and that
// any documentation, advertising materials, and other materials related
// to such distribution and use acknowledge that the software was
// developed by the University of Southern California, Information
// Sciences Institute.  The name of the University may not be used to
// endorse or promote products derived from this software without
// specific prior written permission.
//
// THE UNIVERSITY OF SOUTHERN CALIFORNIA makes no representations about
// the suitability of this software for any purpose.  THIS SOFTWARE IS
// PROVIDED "AS IS" AND WITHOUT ANY EXPRESS OR IMPLIED WARRANTIES,
// INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF
// MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
//
// Other copyrights might apply to parts of this software and are so
// noted when applicable.
//
// Media applications: wrappers of transport agents for multimedia objects
// 
// Their tasks are to receive 'data' from underlying transport agents, 
// and to notify "above" applications that such data has arrived. 
//
// When required (by RapAgent), it also provides callbacks to the 
// transport agent, and contact the above application on behalf of the 
// transport agent.
//
// $Header: /nfs/jade/vint/CVSROOT/ns-2/rap/media-app.h,v 1.11 1999/11/18 23:14:32 haoboy Exp $

#ifndef ns_media_app_h
#define ns_media_app_h

#include <stdlib.h>
#include <tcl.h>
#include "config.h"
#include "agent.h"
#include "app.h"
#include "webcache/http-aux.h"
#include "rap/rap.h"
#include "rap/utilities.h"


class HttpMediaData;

// Fixed length segment in a multimedia stream
class MediaSegment : public DoubleListElem {
public: 
	MediaSegment() : start_(0), end_(0), flags_(0) {}
	MediaSegment(int start, int end) : start_(start),end_(end),flags_(0) {}
	MediaSegment(const HttpMediaData& d);
	MediaSegment(const MediaSegment& s) {
		// XXX Don't copy pointers (prev_,next_)?
		start_ = s.start_;
		end_ = s.end_;
		flags_ = s.flags_;
	}

	int start() const { return start_; }
	int end() const { return end_; }
	int datasize() const { return end_ - start_; }
	MediaSegment* next() const { 
		return (MediaSegment *)DoubleListElem::next(); 
	}
	MediaSegment* prev() const { 
		return (MediaSegment *)DoubleListElem::prev(); 
	}

	void set_start(int d) { start_ = d; }
	void set_end(int d) { end_ = d; }
	void set_datasize(int d) { end_ = start_ + d; }
	// Advance the segment by size
	void advance(int size) { start_ += size, end_ = start_ + size; }

	int in(const MediaSegment& s) const {
		return ((start_ >= s.start_) && (end_ <= s.end_));
	}
	int before(const MediaSegment& s) const {
		return (end_ <= s.start_);
	}
	int overlap(const MediaSegment& s) const {
		assert(s.start_ <= s.end_);
		return ((s.end_ >= start_) && (s.start_ <= end_));
	}
	// Return the overlapping size between the two
	int merge(const MediaSegment& s) {
		if ((s.end_ < start_) || (s.start_ > end_))
			// No overlap
			return 0;
		int size = datasize() + s.datasize();
		if (s.start_ < start_) start_ = s.start_;
		if (s.end_ > end_) end_ = s.end_;
		assert(size >= datasize());
		return (size - datasize());
	}
	// Return the amount of data evicted
	int evict_tail(int sz) {
		// Adjust both the size and playout time
#ifdef MCACHE_DEBUG
		fprintf(stderr, "evicted (%d, %d) ", end_-sz, end_);
#endif
		if (datasize() >= sz) 
			end_ -= sz;
		else {
			sz = datasize();
			end_ = start_;
		}
		return sz;
	}
	// Return the amount of data evicted
	int evict_head(int sz) {
		// Adjust both the size and playout time
		if (datasize() >= sz) 
			start_ += sz;
		else {
			sz = datasize();
			end_ = start_;
		}
		return sz;
	}
	int is_empty() const { return end_ == start_; }

	// Whether this is the last segment of the available data
	enum { MS_LAST = 1, MS_PREF = 2 };
	int is_last() const { return (flags_ & MS_LAST); }
	void set_last() { flags_ |= MS_LAST; }
	int is_pref() const { return (flags_ & MS_PREF); }
	void set_pref() { flags_ |= MS_PREF; }

private: 
	int start_;
	int end_;
	int flags_;
};

// Maintains received segments of every layer
class MediaSegmentList : public DoubleList {
public:
	MediaSegmentList() : DoubleList(), length_(0) {}

	int length() const { return length_; }
	void add(const MediaSegment& s);
	int in(const MediaSegment& s);
	MediaSegment get_nextseg(const MediaSegment& s);
	int evict_tail(int size);
	int evict_head(int size);
	int evict_head_offset(int offset);
	int overlap_size(const MediaSegment& s) const;
	MediaSegmentList check_holes(const MediaSegment& s);

	char* dump2buf();
	virtual void destroy() {
		DoubleList::destroy();
		length_ = 0;
	}

	// Debug only
	void print(void);
	int getsize();
	void check_integrity();

protected:
	void merge_seg(MediaSegment* seg);

private:
	int length_;
};


// Represents a multimedia segment transmitted through the network
class HttpMediaData : public HttpData {
private:
	char page_[HTTP_MAXURLLEN];	// Page ID
	char sender_[HTTP_MAXURLLEN];	// Sender name
	int layer_;			// Layer id. 0 if no layered encoding
	int st_;			// Segment start time
	int et_; 			// Segment end time
	int flags_; 			// flags: end of all data, etc.
	Application* conid_;		// Connection ID. Used for statistics
public:
	struct hdr {
		char sender_[HTTP_MAXURLLEN];
		char page_[HTTP_MAXURLLEN];
		int layer_;
		int st_, et_;
		int flags_;
	};

public:
	HttpMediaData(const char* sender, const char* name, int layer, 
		      int st, int et);
	HttpMediaData(HttpMediaData& d) : HttpData(d) {
		layer_ = d.layer_;
		st_ = d.st_;
		et_ = d.et_;
		flags_ = d.flags_;
		strcpy(page_, d.page_);
		strcpy(sender_, d.sender_);
	}

	virtual int size() const { return HttpData::size() + sizeof(hdr); }
	virtual AppData* copy() { return (new HttpMediaData(*this)); }

	int st() const { return st_; }
	int et() const { return et_; }
	int datasize() const { return et_ - st_; }
	int layer() const { return layer_; }
	const char* page() const { return page_; }
	const char* sender() const { return sender_; }

	Application* conid() { return conid_; }
	void set_conid(Application* c) { conid_ = c; }

	// flags
	// MD_LAST: last segment of this layre
	// MD_FINISH: completed the entire stream
	enum {
		MD_LAST = 1, 
		MD_FINISH = 2,
		MD_PREFETCH = 4
	}; 

	// Whether this is the last data segment of the layer
	int is_last() const { return (flags_ & MD_LAST); }
	void set_last() { flags_ |= MD_LAST; }
	int is_finished() const { return (flags_ & MD_FINISH); }
	void set_finish() { flags_ |= MD_FINISH; }
	int is_pref() const { return (flags_ & MD_PREFETCH); }
	void set_pref() { flags_ |= MD_PREFETCH; }
};


const int MEDIAREQ_GETSEG   	= 1;
const int MEDIAREQ_CHECKSEG 	= 2;

const int MEDIAREQ_SEGAVAIL 	= 3;
const int MEDIAREQ_SEGUNAVAIL 	= 4;

// It provides a MediaApp two types of requests: check data availability and 
// request data. It won't be sent in a packet so we don't need copy(). 
class MediaRequest : public AppData {
private:
	int request_; 			// Request code
	char name_[HTTP_MAXURLLEN];	// Page URL
	int layer_;			// Layer ID
	u_int st_;			// Start playout time
	u_int et_;			// End playout time
	Application* app_;		// Calling application
public:
	MediaRequest(int rc) : AppData(MEDIA_REQUEST), request_(rc) {}
	MediaRequest(const MediaRequest& r) : AppData(MEDIA_REQUEST) {
		request_ = r.request();
		st_ = r.st();
		et_ = r.et();
		layer_ = r.layer();
		strcpy(name_, r.name());
	}
	// We don't need it, but have to declare.
	virtual AppData* copy() { abort(); return NULL; }

	int request() const { return request_; }
	int st() const { return st_; }
	int et() const { return et_; }
	int datasize() const { return et_ - st_; }
	int layer() const { return layer_; }
	const char* name() const { return name_; }
	Application* app() const { return app_; }

	// These functions allow the caller to fill in only the 
	// necessary fields
	void set_st(int d) { st_ = d; }
	void set_et(int d) { et_ = d; }
	void set_datasize(int d) { et_ = st_ + d; }
	void set_name(const char *s) { strcpy(name_, s); }
	void set_layer(int d) { layer_ = d; }
	void set_app(Application* a) { app_ = a; }
};



// Maximum number of layers for all streams in the simulation
const int MAX_LAYER = 10;

// XXX A media app is only responsible for transmission of a single 
// media stream!! Once the transmission is done, the media app will be 
// deleted.
class MediaApp : public Application {
public:
	MediaApp(const char* page);

	virtual AppData* get_data(int& size, AppData* req_data = 0);
	virtual void process_data(int size, AppData* data) {
		send_data(size, data);
	}
	void set_page(const char* pg) { strcpy(page_, pg); }

	void log(const char* fmt, ...);
	int command(int argc, const char*const* argv);

protected:
	virtual void start();
	virtual void stop();

	// Helper function
	RapAgent* rap() { return (RapAgent*)agent_; }

	int seg_size_; 			// data segment size
	char page_[HTTP_MAXURLLEN];
	MediaSegment data_[MAX_LAYER];	// Pointer to next data to be sent
	Tcl_Channel log_; 		// log file

	// XXX assuming all streams in the simulation have the same
	// number of layers.
	int num_layer_;
private:
	int last_layer_;
};



class QA; 

class QATimer : public TimerHandler {
public:
	QATimer(QA *a) : TimerHandler() { a_ = a; }
protected:
	virtual void expire(Event *e);
	QA *a_;
};

const double QA_EPSILON = 1e-6;

class QA : public MediaApp { 
public:
	QA(const char *page);
	virtual ~QA();

	virtual AppData* get_data(int& size, AppData* req_data = 0);
	void UpdateState();
	double UpdateInterval() { return rap()->srtt(); } 

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

	// Helper functions
	void check_availability(int layer, const MediaSegment& s);
	RapAgent* rap() { return (RapAgent*)agent_; }

	// Misc helpers
	inline double MWM(double srtt) {
		return 2 * LAYERBW_ * srtt;
	}
	inline double rate() { 
		return (double)seg_size_ / rap()->ipg();
	}

	// Calculate area of a triangle for a given side and slope
	inline double BufNeed(double side, double slope) {
		return (side <= 0) ? 0.0 : ((side*side) / (2.0*slope));
	}
	int AllZero(double *arr, int len);
	double TotalBuf(int n, double *buffer);
	AppData* output(int& size, int layer);
	void DumpInfo(double t, double last_t, double rate, 
		      double avgrate, double srtt);
	double bufOptScen1(int layer, int layers, double currrate, 
			   double slope, int backoffs);
	double bufOptScen2(int layer, int layers, double currrate, 
			   double slope, int backoffs);
	void drain_buf(double* DrainArr, double bufToDrain, 
		       double* FinalDrainArray, double* bufAvail, 
		       int layers, double rate, double srtt);
	void DrainPacket(double bufToDrain, double* FinalDrainArray, 
			 int layers, double rate, double srtt, 
			 double* FinalBuffer);

	// Ack feedback information
	void DrainBuffers();

	// Debugging output
	void debug(const char* fmt, ...);
	void panic(const char* fmt, ...);
	void check_layers(int layer, MediaSegment& tmp);

	// Data members
	int layer_;
	double    playTime_; // playout time of the receiver
	double    startTime_; // Absoloute time when playout started

	// Internal state info for QA
	double   buffer_[MAX_LAYER];
	double   drained_[MAX_LAYER]; 
	double   bw_[MAX_LAYER];
	int	 playing_[MAX_LAYER]; 
	int	 sending_[MAX_LAYER];
	QATimer* updTimer_;

	// Average transmission rate and its moving average weight
	// Measured whenever a segment is sent out (XXX why??)
	double   avgrate_; 
	double   rate_weight_;

	// Variables related to playout buffer management
	int      poffset_;  /* The playout offset: estimation of client 
			       playout time */
	// Linked list keeping track of holes between poffset_ and current
	// transmission pointer
	MediaSegmentList outlist_[MAX_LAYER];
	// The end offset of the prefetch requests. Used to avoid redundant
	// prefetching requests
	int pref_[MAX_LAYER];

	// OTcl-bound variables
	int debug_;  		// Turn on/off debug output
	double pref_srtt_;	// Prefetching SRTT
	int LAYERBW_;
	int MAXACTIVELAYERS_;
	double SRTTWEIGHT_;
	int SMOOTHFACTOR_;
	int MAXBKOFF_;
}; 

#endif // ns_media_app_h
