/*
 * Author:
 *         Kendy Kutzner <kutzner@ira.uka.de>
 *
 * (c) 2005 Research Group Dr. Fuhrmann, University of Karlsruhe, Germany
 * All rights reserved.
 *
 * $Id: rawmessage.cc,v 1.12 2006/01/05 10:24:27 kutzner Exp $
 *
 */

#include <string>
#include <sstream>
#include <map>
using namespace std;

#include "buf.h"
#include "utils.h"
#include "debug.h"
#include "rawmessage.h"

const string rawmessage::contentlengthstring = "ContentLength";
const string rawmessage::truestring = "True";
const string rawmessage::falsestring = "False";

/**
 * try to convert a buffer into a raw message
 * a raw message is a couple of attribute/value pairs plus an optional blob
 */
void 
rawmessage::parse(buf *b){
	dbg.print(dbg.RAWMSG, 6, "try parsing buffer of size %i\n", b->size());
	buf backup;
	backup.put(b); // XXX ouch; it copies a potentially large buffer
	buf * line;
	_vals.clear(); // XXX necessary?
	_bindata.clear(); // XXX necessary?
	_complete = false;
	string l;
	std::pair<string,string> av;
	
	while (1){
		line = b->getline();
		if (line == NULL){ // no line available
			dbg.print(dbg.RAWMSG, 7, "no more lines available\n");
			break;
		};
		if (line->size()==0){ // read empty line. now either return or read bindata
			delete line;
			int i = atoi(getval(contentlengthstring).c_str());
			dbg.print(dbg.RAWMSG, 7, "read empty line, %s is %i, %i bytes available\n", contentlengthstring.c_str(), i, b->size());
			if (i == 0){
				dbg.print(dbg.RAWMSG, 6, "msg without bindata complete\n");
				_complete = true;
				return;
			}
			if (b->size() >= i){
				_bindata.put(b->getraw(), i); // very interesting construction
				b->get(NULL, i); // destroy the data in the buffer
				dbg.print(dbg.RAWMSG, 6, "msg with bindata complete\n");
				_complete = true;
				return;
			}
			break;
		}
		// normal line read
		l = string((char*)(line->getraw()),line->size()); // why is converting char* -> string so hard?
		av = splitavpair(l);
		if (av.first.size()==0){
			// ignore that line?!
			dbg.print(dbg.RAWMSG, 6, "ignoring strange line %s\n", l.c_str());
		}
		addval(av.first, av.second);
		dbg.print(dbg.RAWMSG, 7, "read key '%s' and val '%s'\n", av.first.c_str(), av.second.c_str());
		delete line;
	}
	dbg.print(dbg.RAWMSG, 6, "reverting to orginal state\n");
	_vals.clear();
	_bindata.clear();
	_complete = false;
	b->clear();
	b->put(&backup); // 2nd ouch 
	return;
}

void
rawmessage::unparse(buf *b){
	map<string,string>::const_iterator i,j;
	// put type at the beginning
	i = _vals.find("Type");
	if (i!=_vals.end()){
		b->put(makeavpair(i->first, i->second));
	}
	for (j = _vals.begin(); j != _vals.end(); j++){
		if (j==i)
			continue;
		b->put(makeavpair(j->first, j->second));
	}
	if (_bindata.size()>0){
		b->put(makeavpair(contentlengthstring, _bindata.size()));
		b->put(string("\n"));
		b->put(&_bindata);
	} else {
		b->put(string("\n"));
	}
	return;
}

string 
rawmessage::getval(string a){
	map<string,string>::const_iterator i;
	i = _vals.find(a);
	if (i == _vals.end()){
		dbg.print(dbg.RAWMSG, 6, "getval %s from %p -> empty\n", a.c_str(), (void*)this);
		return "";
	} else {
		dbg.print(dbg.RAWMSG, 6, "getval %s from %p -> '%s'\n", a.c_str(), (void*)this, i->second.c_str());
		return i->second;
	}
}

int
rawmessage::getint(string a){
	string v = getval(a);
	int i = atoi(v.c_str());
	dbg.print(dbg.RAWMSG, 7, "converted to int %i\n", i);
	return i;
}

double
rawmessage::getdouble(string a){
	string v = getval(a);
	double d = 0;
	int scanret = sscanf(v.c_str(), "%lf", &d);
	dbg.print(dbg.RAWMSG, 7, "converted %s to %f with ret %i\n", v.c_str(), d, scanret);
	return d;
}

bool
rawmessage::getbool(string a){
	string v = getval(a);
	if (v == falsestring){
		dbg.print(dbg.RAWMSG, 7, "converted to bool false\n");
		return false;
	}
	if (v == truestring){
		dbg.print(dbg.RAWMSG, 7, "converted to bool true\n");
		return true;
	}
	dbg.print(dbg.RAWMSG, 7, "not convertible; return false\n");
	return false;
}

void 
rawmessage::addval(string a, string v){
	dbg.print(dbg.RAWMSG, 6, "adding a:'%s' v:'%s' to %p\n", a.c_str(), v.c_str(), (void*)this);
	_vals.insert(std::pair<string,string>(a,v));
}

void
rawmessage::addval(string a, int v){
	addval(a, itos(v));
}

void
rawmessage::addvalb(string a, bool v){
	if (v){
		addval(a, truestring);
	} else {
		addval(a, falsestring);
	}
}

void
rawmessage::addvald(string a, double d){
	char buf[50];
	snprintf(buf, 50, "%f", d);
	//stringstream ss;
	//ss.precision(20);
	//ss.width(20);
	//ss << d;
	//addval(a, ss.str());
	addval(a, buf);
}

void
rawmessage::getbin(buf *b){
	b->put(&_bindata);
}

void
rawmessage::putbin(buf *b){
	_bindata.put(b);
}
	

