
/* Web Polygraph       http://www.web-polygraph.org/
 * Copyright 2003-2011 The Measurement Factory
 * Licensed under the Apache License, Version 2.0 */

#ifndef POLYGRAPH__BASE_UNIQID_H
#define POLYGRAPH__BASE_UNIQID_H

#include "xstd/h/iosfwd.h"

class Area;
class String;
class OLog;
class ILog;
class OBStream;
class IBStream;

// we often need an id with these characteristics:
//     - truly unique across hosts, processes, time (within ttl), etc
//     - at least one year ttl
//     - compact to store
//     - fast to produce [possibly in huge quantaties!]
//     - fast to compare on equality
//     - has one and only one "permutated" value that
//       cannot be generated by means other than "mutating" an id

class UniqId {
	public:
		static UniqId Create() { return UniqId().create(); }
		static void Space(int id); // generate ids in a given space
		static UniqId FromStr(const Area &area);

	public:
		UniqId();
		UniqId(int aSecs, int aMix, int aCnt);

		void clear() { theCnt = 0; }
		UniqId &create(); // returns itself
		operator void*() const { return theCnt > 0 ? (void*)-1 : 0; } // created?

		bool mutant() const { return theCnt & 1; } // is a mutatant?
		inline bool myMutant(const UniqId &mid) const;
		inline UniqId genMutant() const; // produce a mutant

		// returns a not so unique integer
		int hash() const;

		String str() const;

		inline bool operator ==(const UniqId &id) const;
		inline bool operator !=(const UniqId &id) const;
		inline bool operator <(const UniqId &id) const; // arbirtary order

		ostream &print(ostream &os) const;
		OLog &store(OLog &ol) const;
		ILog &load(ILog &il);
		OBStream &store(OBStream &os) const;
		IBStream &load(IBStream &is);

		bool parse(const char *&buf, const char *end);

	protected:
		void RefreshSessionId();

	protected:
		int theSecs;    // seconds since Nov 29 06:21:18 MST 1998 (or space XXX)
		int theMix;     // usec and process id
		int theCnt;     // instance
};


/* inline definitions */

inline
ostream &operator <<(ostream &os, const UniqId &id) { return id.print(os); }

inline
OLog &operator <<(OLog &ol, const UniqId &id) { return id.store(ol); }

inline
ILog &operator >>(ILog &il, UniqId &id) { return id.load(il); }

inline
OBStream &operator <<(OBStream &os, const UniqId &id) { return id.store(os); }

inline
IBStream &operator >>(IBStream &is, UniqId &id) { return id.load(is); }

inline
UniqId UniqId::genMutant() const { 
	return UniqId(theSecs, theMix, ~theCnt & 0x7fffffff);
}

inline
bool UniqId::myMutant(const UniqId &m) const {
	return (m.theCnt == (~theCnt & 0x7fffffff)) &&
		theMix == m.theMix &&
		theSecs == m.theSecs;
}

inline
bool UniqId::operator ==(const UniqId &id) const {
	return theCnt == id.theCnt && // most selective first
		theMix == id.theMix &&
		theSecs == id.theSecs;
}

inline
bool UniqId::operator !=(const UniqId &id) const {
	return !(*this == id);
}

inline
bool UniqId::operator <(const UniqId &id) const {
	if (theCnt < id.theCnt)
		return true;
	if (theCnt > id.theCnt)
		return false;

	if (theMix < id.theMix)
		return true;
	if (theMix > id.theMix)
		return false;
	
	return theSecs < id.theSecs;
}


#endif
