/*
** This program is free software; you can redistribute it and/or modify
** it under the terms of the GNU General Public License as published by
** the Free Software Foundation; either version 1, or (at your option)
** any later version.

** This program is distributed in the hope that it will be useful,
** but WITHOUT ANY WARRANTY; without even the implied warranty of
** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
** GNU General Public License for more details.

** You should have received a copy of the GNU General Public License
** along with this program; if not, write to the Free Software
** Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/

#ifndef CVSLOG_H
#define CVSLOG_H

#ifdef WIN32
#	ifdef _DEBUG
#		define qCvsDebug 1
#	else
#		define qCvsDebug 0
#	endif
#endif /* WIN32 */

#include <vector>
#include <time.h>
#include <stdio.h>

#if defined(_MSC_VER) && _MSC_VER < 0x514 // VC7
#	include <iostream.h>
	using namespace std;
#	define OSTREAM ::ostream
#else
#	include <iostream>
#	define OSTREAM std::ostream
#endif

// a C-string
class CLogStr
{
public :
	inline CLogStr() : str(0L) {}
	inline CLogStr(const char *newstr)
	{
		str = 0L;
		*this = newstr;
	}
	inline CLogStr(const CLogStr & newstr)
	{
		str = 0L;
		*this = newstr;
	}
	virtual ~CLogStr() { flush(); }

	inline bool empty(void) const {return str == 0L || str[0] == '\0';}
	inline unsigned int length(void) const {return str == 0L ? 0 : strlen(str);}

	const char *operator=(const char *newstr);
		// set to a new C String (0L is OK)
	const CLogStr & operator=(const CLogStr & newstr);
		// set according to another CLogStr
	const CLogStr & set(const char *buf, unsigned int len);
		// set from a buffer

	bool operator<(const char *newstr) const;

	inline operator const char *() const {return str == 0L ? "" : str;}
	inline const char *c_str() const {return str == 0L ? "" : str;}
		// as a C string

	CLogStr & operator<<(const char *addToStr);
	CLogStr & operator<<(char addToStr);
	CLogStr & operator<<(int addToStr);
		// concatenate

	inline bool endsWith(char c) const {return !length() ? false : str[length()-1] == c;}
	
	const CLogStr & replace(char which, char bywhich);
		// replace a character
	
protected :
	void flush(void);
	char *str; // the String
};

// class to store a revision number
class CRevNumber
{
public:
	CRevNumber() {}
	virtual ~CRevNumber() { reset(); }

	void reset(void);
	inline int size() const {return allDigits.size();}
	inline int empty() const {return allDigits.empty();}

	int cmp(const CRevNumber & arev) const;
		// return -1,0,1 "a la" qsort

	CRevNumber & operator+=(int adigit);
	CRevNumber & operator=(const CRevNumber & arev);
	bool operator==(const CRevNumber & arev) const;
	int operator[](int index) const;
	bool operator<(const CRevNumber & arev) const;

	bool ischildof(const CRevNumber & arev) const;
		// 1.4.2.2 is child of 1.4
	bool issamebranch(const CRevNumber & arev) const;
		// 1.1.1.2 is same branch than 1.1.1.3
	bool ispartof(const CRevNumber & arev) const;
		// 1.1.1.2 is part of 1.1.1
		// 1.4.2.3 is part of 1.4.0.2
	bool issubbranchof(const CRevNumber & arev) const;
		// 1.1.1 is subbranch of 1.1
		// 1.4.0.2 is subbranch of 1.4

	inline const std::vector<int> & IntList(void) const { return allDigits; }
	inline std::vector<int> & IntList(void) { return allDigits; }

	inline const CLogStr & Tag(void) const { return tagName; }
	inline CLogStr & Tag(void) { return tagName; }

	const char *c_str(void) const;
protected:
	std::vector<int> allDigits;
	CLogStr tagName; // or an author
};

// a single revision for a file
class CRevFile
{
public:
	CRevFile();
	CRevFile(const CRevFile & afile);
	virtual ~CRevFile();

	CRevFile & operator=(const CRevFile & afile);
	bool operator<(const CRevFile & afile) const;
	bool operator==(const CRevFile & afile) const;

	void print(OSTREAM & out) const;

	inline const CRevNumber & RevNum(void) const { return revNum; }
	inline CRevNumber & RevNum(void) { return revNum; }

	inline const struct tm & RevTime(void) const { return revTime; }
	inline struct tm & RevTime(void) { return revTime; }

	inline const CLogStr & Locker(void) const { return locker; }
	inline CLogStr & Locker(void) { return locker; }

	inline const std::vector<CRevNumber> & BranchesList(void) const { return branchesList; }
	inline std::vector<CRevNumber> & BranchesList(void) { return branchesList; }
	inline CRevNumber & LastBranche(void) { return branchesList[branchesList.size() - 1]; }

	inline const CLogStr & Author(void) const { return author; }
	inline CLogStr & Author(void) { return author; }

	inline const CLogStr & State(void) const { return state; }
	inline CLogStr & State(void) { return state; }

	inline int ChgPos(void) const { return chgPos; }
	inline int & ChgPos(void) { return chgPos; }

	inline int ChgNeg(void) const { return chgNeg; }
	inline int & ChgNeg(void) { return chgNeg; }

	inline const CLogStr & DescLog(void) const { return descLog; }
	inline CLogStr & DescLog(void) { return descLog; }
	
	inline const CLogStr & KeywordSubst(void) const { return keywordSubst; }
	inline CLogStr & KeywordSubst(void) { return keywordSubst; }

	inline const CLogStr & CommitID(void) const { return commitID; }
	inline CLogStr & CommitID(void) { return commitID; }

	inline const CLogStr & Filename(void) const { return filename; }
	inline CLogStr & Filename(void) { return filename; }

	inline const CRevNumber & MergePoint(void) const { return mergePoint; }
	inline CRevNumber & MergePoint(void) { return mergePoint; }

protected:
	CRevNumber revNum;
	struct tm revTime;
	CLogStr locker;
	std::vector<CRevNumber> branchesList;
	CLogStr author;
	CLogStr state;
	int chgPos;
	int chgNeg;
	CLogStr descLog;
	CLogStr keywordSubst;
	CLogStr commitID;
	CRevNumber mergePoint;
	CLogStr filename;
};

// RCS infos for a file
class CRcsFile
{
public:
	CRcsFile();
	CRcsFile(const CRcsFile & afile);
	virtual ~CRcsFile();

	CRcsFile & operator=(const CRcsFile & afile);
	bool operator<(const CRcsFile & afile) const;
	bool operator==(const CRcsFile & afile) const;

	void sort(void);

	void print(OSTREAM & out) const;

	inline const CLogStr & RcsFile(void) const { return rcsFile; }
	inline CLogStr & RcsFile(void) { return rcsFile; }

	inline const CLogStr & WorkingFile(void) const { return workingFile; }
	inline CLogStr & WorkingFile(void) { return workingFile; }

	inline const CRevNumber & HeadRev(void) const { return headRev; }
	inline CRevNumber & HeadRev(void) { return headRev; }

	inline const CRevNumber & BranchRev(void) const { return branchRev; }
	inline CRevNumber & BranchRev(void) { return branchRev; }

	inline const CLogStr & KeywordSubst(void) const { return keywordSubst; }
	inline CLogStr & KeywordSubst(void) { return keywordSubst; }

	inline int SelRevisions(void) const { return selRevisions; }
	inline int & SelRevisions(void) { return selRevisions; }

	inline int TotRevisions(void) const { return totRevisions; }
	inline int & TotRevisions(void) { return totRevisions; }

	inline const std::vector<CLogStr> & AccessList(void) const { return accessList; }
	inline std::vector<CLogStr> & AccessList(void) { return accessList; }
	inline CLogStr & LastAccess(void) { return accessList[accessList.size() - 1]; }

	inline const std::vector<CRevNumber> & SymbolicList(void) const { return symbolicList; }
	inline std::vector<CRevNumber> & SymbolicList(void) { return symbolicList; }
	inline CRevNumber & LastSymbName(void) { return symbolicList[symbolicList.size() - 1]; }

	inline const std::vector<CRevNumber> & LocksList(void) const { return locksList; }
	inline std::vector<CRevNumber> & LocksList(void) { return locksList; }
	inline CRevNumber & LastLock(void) { return locksList[locksList.size() - 1]; }

	inline bool LockStrict(void) const { return lockStrict; }
	inline bool & LockStrict(void) { return lockStrict; }

	inline const std::vector<CRevFile> & AllRevs(void) const { return allRevs; }
	inline std::vector<CRevFile> & AllRevs(void) { return allRevs; }
	inline CRevFile & LastRev(void) { return allRevs[allRevs.size() - 1]; }

	inline const CLogStr & DescLog(void) const { return descLog; }
	inline CLogStr & DescLog(void) { return descLog; }

protected:
	CLogStr rcsFile;
	CLogStr workingFile;
	CRevNumber headRev;
	CRevNumber branchRev;
	CLogStr keywordSubst;
	std::vector<CLogStr> accessList;
	std::vector<CRevNumber> symbolicList;
	std::vector<CRevNumber> locksList;
	int selRevisions;
	int totRevisions;
	bool lockStrict;
	std::vector<CRevFile> allRevs;
	CLogStr descLog;
};

// classes used to build a tree off the cvs log output

typedef enum
{
	kNodeHeader,
	kNodeBranch,
	kNodeRev,
	kNodeTag
} kLogNode;

class CLogNode;

class CLogNodeData
{
public:
	CLogNodeData(CLogNode *node) { fNode = node; }
	virtual ~CLogNodeData() {}

	inline CLogNode *Node(void) { return fNode; }
protected:
	CLogNode *fNode;
};

class CLogNode
{
public:
	CLogNode(CLogNode * root = 0L);
	virtual ~CLogNode();

	virtual kLogNode GetType(void) const = 0;

	inline const std::vector<CLogNode *> & Childs(void) const { return childs; }
	inline std::vector<CLogNode *> & Childs(void) { return childs; }

	inline const CLogNode * Root(void) const { return root; }
	inline CLogNode * & Root(void) { return root; }

	inline const CLogNode * Next(void) const { return next; }
	inline CLogNode * & Next(void) { return next; }

	inline void SetUserData(CLogNodeData *data) { user = data; }
	inline CLogNodeData *GetUserData(void) { return user; }
protected:
	std::vector<CLogNode *> childs;
	CLogNode * root;
	CLogNode * next;
	CLogNodeData *user;
};

class CLogNodeHeader : public CLogNode
{
public:
	CLogNodeHeader(const CRcsFile & h, CLogNode * r = 0L) :
		CLogNode(r), header(h) {}

	virtual kLogNode GetType(void) const { return kNodeHeader; }

	inline const CRcsFile & operator *() const { return header; }
	inline CRcsFile & operator *() { return header; }

protected:
	CRcsFile header;
};

class CLogNodeRev : public CLogNode
{
public:
	CLogNodeRev(const CRevFile & r, CLogNode * root = 0L) :
		CLogNode(root), rev(r) {}

	virtual kLogNode GetType(void) const { return kNodeRev; }

	inline const CRevFile & operator *() const { return rev; }
	inline CRevFile & operator *() { return rev; }

protected:
	CRevFile rev;
};

class CLogNodeTag : public CLogNode
{
public:
	CLogNodeTag(const CLogStr & t, CLogNode * r = 0L) :
		CLogNode(r), tag(t) {}

	virtual kLogNode GetType(void) const { return kNodeTag; }

	inline const CLogStr & operator*() const { return tag; }
	inline CLogStr & operator*() { return tag; }

protected:
	CLogStr tag;
};

class CLogNodeBranch : public CLogNode
{
public:
	CLogNodeBranch(const CLogStr & b, CLogNode * r = 0L) :
		CLogNode(r), branch(b) {}

	virtual kLogNode GetType(void) const { return kNodeBranch; }

	inline const CLogStr & operator*() const { return branch; }
	inline CLogStr & operator*() { return branch; }

protected:
	CLogStr branch;
};

std::vector<CRcsFile> & CvsLogParse(FILE * file);
	// parse a file and return the set of RCS files

void CvsLogReset(void);
	// free the memory used

CLogNode *CvsLogGraph(CRcsFile & rcsfile);
	// build a tree (i.e. graph) of the history

#endif
