// Copyright (C) FIT VUT
// Petr Lampa <lampa@fit.vutbr.cz>
// $Id$
// vi:set ts=8 sts=8 sw=8:
//
// Linux header problem
//


#ifndef __NDWATCH_PACKET_H__
#define __NDWATCH_PACKET_H__

#include <string.h>

class Buffer
{
private:
	Octet _buf[2048];
	Word _cur;		// current pointer
	Word _scur;		// decode start pointer
	Word _length;		// decode packet length

public:
	Buffer(): _cur(0),_scur(0),_length(0) { }
	inline void clear() { _cur = _scur = _length = 0; }
	inline void reset() { _cur = _scur; }
	inline void set() { _scur = _cur; }
	inline void setup(int len) { _cur = _scur = 0; _length = len; }
	inline int left() const { return _length-_cur; }
	inline unsigned length() const { return (_length)?_length:_cur; }
	inline bool add_octet(Octet b) { 
		if (_cur >= sizeof(_buf)) return false;
		_buf[_cur++] = b;
		return true;
	}
	inline bool add_hshort(Short val)
	{
		if (!add_octet(val >> 8 & 0xff)) return false;
		return add_octet(val & 0xff);
	}
	inline bool add_hlong(Word val)
	{
		if (!add_octet(val >> 24 & 0xff)) return false;
		if (!add_octet(val >> 16 & 0xff)) return false;
		if (!add_octet(val >> 8 & 0xff)) return false;
		return add_octet(val & 0xff);
	}
	inline bool get_octet(Octet &b) {
		if (_cur >= _length) return false;
		b = _buf[_cur++];
		return true;
	}
	inline bool get_hshort(Short &val) {
		Octet b1,b2;
		if (!get_octet(b1)) return false;
		if (!get_octet(b2)) return false;
		val = (b1<<8)+b2;
		return true;
	}
	inline bool get_hlong(Word &val) {
		Octet b1,b2,b3,b4;
		if (!get_octet(b1)) return false;
		if (!get_octet(b2)) return false;
		if (!get_octet(b3)) return false;
		if (!get_octet(b4)) return false;
		val = (b1<<24)+(b2<<16)+(b3<<8)+b4;
		return true;
	}

	inline const void *buf() const { return _buf; }
	inline void buf(const Octet *b, unsigned len) {
		if (len <= sizeof(_buf)) {
			memcpy(_buf, b, len);
			setup(len);
		}
	}

	string to_string(int level=0) const;
	~Buffer() { }
};

inline ostream &operator<<(ostream &out, const Buffer &buf)
{
	return out << buf.to_string();
}
inline ostream &operator<<(ostream &out, const Buffer *buf)
{
	return out << buf->to_string();
}

// base class 
class Packet
{
private:
	Packet(const Packet&);
	Packet& operator=(const Packet&);
	Packet *_upper;
	Packet *_lower;
	bool _own;	// release upper layer payload 
protected:
	Word _length;	// valid after build(0)
public:
	Packet(): _upper(0), _lower(0), _own(false), _length(0) { }

	// attach upper layer payload, optionally taking ownership
	void attach(Packet *upper, bool own = false) { 
		_upper = upper; upper->_lower = this; _own = own; 
		upper->fixup();
	}
	// fixup upper packet in attach
	virtual void fixup() = 0;
	// recursively remove all payload, optionally delete objects
	void release();
	Packet *payload() const { return _upper; }
	Packet *underlayer() const { return _lower; }
	virtual unsigned pseudo_checksum(Octet nh, Word plen) const { return 0; }
	virtual string to_string(int level=0) const = 0;
	virtual string name() const = 0;
	virtual bool build(Buffer &pkt);
	virtual bool decode(Buffer &pkt) = 0;
	virtual bool do_build(Buffer &pkt, int phase, Word &pos) = 0;
	virtual ~Packet() { release(); };
};

inline ostream &operator<<(ostream &out, const Packet *p)
{
	return out << p->to_string();
}

inline string Buffer::to_string(int level) const
{
	char buf[2048*4];	// sufficient for max packet size
	char *p = buf;
	extern const char *xdigits;

	cout << "_cur = " << _cur << "_scur = " << _scur << "_length = " << _length << endl;
	for (unsigned i = 0; i < (_length?_length:_cur); i++) {
		*p++ = xdigits[_buf[i]>>4]; 
		*p++ = xdigits[_buf[i] & 0xf]; 
		if ((i % 16) == 15) *p++ = '\n';
		else *p++ = ' ';
	}
	*p++ = '\n';
	*p = 0;
	return string(buf);
}

inline bool Packet::build(Buffer &pkt)
{
	Word pos = 0;
	// fixup packet, compute checksums
	if (!do_build(pkt, 0, pos)) return false;
	pos = 0;
	// do real build
	if (!do_build(pkt, 1, pos)) return false;
	return true;
}

inline void Packet::release()
{
	Packet *p = this;
	Packet *next = _upper;
	while (next) {	// upper layer
		p->_upper = NULL;
		if (next->_lower != p) {
			cerr << "Packet::release() underlayer doesn't match payload pointer" << endl;
		}
		if (p->_own) {	// owned
			p->_own = false;
			// cerr << "deleting " << next->name() << " " << next << endl;
			delete next;	// this will call release for the next
			return;		// all remaining upper layers done
		}
		p = next;
		next = p->_upper;
	}
}
#endif
