/*******************************************************************************
 *	Boapns.cc		Boap NAme Server
 *				T.Barnaby,	BEAM Ltd,	4/3/04
 *******************************************************************************
 */

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <fcntl.h>
#include <string.h>
#include <errno.h>
#include <sys/time.h>
#include <netinet/in.h>

#include <BString.h>
#include <BList.h>
#include <BPoll.h>
#include <BSocket.h>
#include <BoapnsD.h>
#include <BoapnsS.h>
#include <BMutex.h>

#define DEBUG	0

#define	UseTimer	0

#if DEBUG
#define	dprintf(fmt, a...)       printf(fmt, ##a);
#else
#define	dprintf(fmt, a...)
#endif


class BoapnsObject : public Boapns::BoapnsService {
public:
				BoapnsObject(BoapServer& server, BString name);
	BError			getVersion(BString& version);
	BError			getEntryList(BList<Boapns::BoapEntry>& entryList);
	BError			getEntry(BString name, Boapns::BoapEntry& entry);
	BError			addEntry(Boapns::BoapEntry entry);
	BError			delEntry(BString name);
	BError			getNewName(BString& name);

	void			pingAll();
	BError			processEvent(BoapPacket& rx);
private:
	void				entryPrint(Boapns::BoapEntry entry);
	BList<Boapns::BoapEntry>	oservices;
	BUInt32				onextId;
	BMutex				olock;
};

BoapnsObject::BoapnsObject(BoapServer& server, BString name) : Boapns::BoapnsService(server, name){
	onextId = 0;
#ifdef ZAP
	oservices.append(Boapns::BoapEntry("test1", 0, 0, 1));
	oservices.append(Boapns::BoapEntry("test2", 0, 0, 2));
	oservices.append(Boapns::BoapEntry("test3", 0, 0, 3));
#endif
}

void BoapnsObject::entryPrint(Boapns::BoapEntry entry){
	BIter		i;
	BString		al;
	
	for(entry.addressList.start(i); !entry.addressList.isEnd(i); entry.addressList.next(i)){
		if(al.len())
			al = al + ",";
		al = al + entry.addressList[i];
	}
	
	printf("%-32.32s %s:%d %d\n", entry.name.retStr(), al.retStr(), entry.port, entry.service);
}

BError BoapnsObject::getVersion(BString& version){
	BError	err;
	
	version = "1.0.0";

	return err;
}

BError 	BoapnsObject::getEntryList(BList<Boapns::BoapEntry>& entryList){
	BError	err;

	dprintf("%s:\n", __FUNCTION__);
	olock.lock();
	entryList = oservices;
	olock.unlock();
	dprintf("%s: End\n", __FUNCTION__);
	
	return err;
}

BError BoapnsObject::getEntry(BString name, Boapns::BoapEntry& entry){
	BError	err;
	BIter	i;

	dprintf("BoapnsObject::getEntry: %s\n", name.retStr());
	olock.lock();
	for(oservices.start(i); !oservices.isEnd(i); oservices.next(i)){
		if(oservices[i].name == name){
			entry = oservices[i];
			olock.unlock();
			return err;
		}
	}
	
	olock.unlock();
	err.set(1, BString("Boapns: No such service: ") + name);
	return err;
}

BError BoapnsObject::addEntry(Boapns::BoapEntry entry){
	BError	err;
	BIter	i;

	dprintf("BoapnsObject::addEntry: %s %s:%d %d\n", entry.name.retStr(), entry.addressList[0].retStr(), entry.port, entry.service);
	olock.lock();
	for(oservices.start(i); !oservices.isEnd(i); oservices.next(i)){
		if(oservices[i].name == entry.name){
			oservices.del(i);
			break;
		}
	}
	oservices.append(entry);
	olock.unlock();

#if DEBUG
	for(oservices.start(i); !oservices.isEnd(i); oservices.next(i)){
		entryPrint(oservices[i]);
	}
#endif

	return err;
}

BError BoapnsObject::delEntry(BString name){
	BError	err;
	BIter	i;

	dprintf("%s:\n", __FUNCTION__);
	olock.lock();
	for(oservices.start(i); !oservices.isEnd(i); oservices.next(i)){
		if(oservices[i].name == name){
			oservices.del(i);
			break;
		}
	}
	olock.unlock();
	return err;	
}

BError BoapnsObject::getNewName(BString& name){
	BError	err;
	
	name = BString("object") + onextId++;
	
	return err;
}

void BoapnsObject::pingAll(){
	BIter			i;
	BError			err;
	BoapClientObject	obj;
	
	printf("PingAll\n");
	olock.lock();
#ifdef ZAP
	for(oservices.start(i); !oservices.isEnd(i); oservices.next(i)){
		printf("Ping: %s\n", oservices[i].name.retStr());
		obj.connectService(oservices[i].name);
		printf("Ping: Do %s\n", oservices[i].name.retStr());
		err = obj.ping();
		printf("PinRes: %d\n", err.getErrorNo());
	}
#endif
	olock.unlock();
}

BError BoapnsObject::processEvent(BoapPacket& rx){
	BError			err;
	BIter			i;
	BSocketAddressINET	nadd;
	BSize			nsent;
	
	dprintf("BoapnsObject::processEvent\n");
	for(oservices.start(i); !oservices.isEnd(i); oservices.next(i)){
		nadd.set(oservices[i].addressList[0], oservices[i].port);
		dprintf("BoapnsObject::processEvent: Nbytes: %d SendTo: %s :%s\n", rx.nbytes(), oservices[i].name.retStr(), nadd.getString().retStr());
		oserver.getEventSocket().sendTo(nadd, rx.data(), rx.size(), nsent);
		dprintf("BoapnsObject::processEvent: Result: %s\n", strerror(errno));
	}
	return err;
}

void* timer(void* data){
	BoapnsObject*	boapns = (BoapnsObject*)data;
	
	while(1){
		boapns->pingAll();
		sleep(1);
	}
	return 0;
}

int main(int argc, char** argv){
	BoapServer		server;
	BoapnsObject		boapns(server, "boapns");
	BError			err;
	int			a;
	int			foreground = 0;
#ifdef UseTimer
	pthread_t		timerThread;
#endif
	
	for(a = 1; a < argc; a++){
		if(argv[a][0] == '-'){
			switch(argv[a][1]){
			case 'f':	foreground = 1;	break;
			}
		}
	}

	dprintf("Initialise BoapServer\n");
	if(err = server.init("", 0, 0, 1)){
		std::cerr << err.getString() << "\n";
		return 1;
	}

	if(!foreground)
		daemon(0, 0);

	dprintf("Run Server\n");

#ifdef UseTimer
	pthread_create(&timerThread, 0, timer, &boapns);
#endif

	server.run();

	return 0;
}
