/*
** 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.
*/

/*
 * Author : Alexandre Parenteau <aubonbeurre@hotmail.com> --- April 1998
 * Modified: Jerzy Kaczorowski <kaczoroj@hotmail.com> - 27 Jan 2001 - fix warning C4800 (Add "0 != " for all the int variables forced to bool value)
 */

/*
 * CvsEntries.h --- adaptation from cvs/src/entries.c
 */

#ifndef CVSENTRIES_H
#define CVSENTRIES_H

#include "SortList.h"
#include "CvsIgnore.h"

typedef enum
{
	ENT_FILE,
	ENT_SUBDIR
} ent_type;

// ref counted full path description, see below
class EntnodePath
{
protected:
	// reference counted, we don't use delete on it.
	~EntnodePath();
public:
	EntnodePath(const char* path);

	// ref counting
	inline EntnodePath *Ref(void) {++ref; return this;}
	inline EntnodePath *UnRef(void)
	{
		if(--ref == 0)
		{
			delete this;
			return 0L;
		}
		return this;
	}

	const char* GetFullPathName(UStr & resPath, const char* name) const;

protected:
	const char* fullpathname; // fully qualified pathname of the folder containing the item
	int ref;
};

class EntnodeData
{
// Construction
protected:
	// used by derived classes only
	EntnodeData(const char* name, EntnodePath *path);

	// cannot use 'delete', use UnRef() instead.
	virtual ~EntnodeData();

public:

	enum
	{
		kName = 0,
		kStatus = 3,
		kPath = 7
	};

	virtual ent_type GetType(void) const = 0;
	inline static int Compare(const EntnodeData & node1, const EntnodeData & node2)
	{
#ifdef WIN32
		return stricmp(node1.user, node2.user);
#else /* !WIN32 */
		return strcmp(node1.user, node2.user);
#endif /* !WIN32 */
	}
	inline EntnodeData *Ref(void) {++ref; return this;}
	inline EntnodeData *UnRef(void)
	{
		if(--ref == 0)
		{
			delete this;
			return 0L;
		}
		return this;
	}

	virtual const char *operator[](int index) const
	{
		if(index == kName)
			return user;
		else if(index == kStatus)
			return desc;
		else if(index == kPath)
			return GetFullPathName(sbuffer);
		return 0L;
	}

	inline void SetMissing(bool state) {missing = state;}
	inline bool IsMissing(void) const {return 0 != missing;}
	inline void SetVisited(bool state) {visited = state;}
	inline bool IsVisited(void) const {return 0 != visited;}
	inline void SetUnknown(bool state) {unknown = state;}
	inline bool IsUnknown(void) const {return 0 != unknown;}
	inline void SetIgnored(bool state) {ignored = state;}
	inline bool IsIgnored(void) const {return 0 != ignored;}
	inline void SetLocked(bool state) {locked = state;}
	inline bool IsLocked(void) const {return 0 != locked;}
	inline void SetRemoved(bool state) {removed = state;}
	inline bool IsRemoved(void) const {return 0 != removed;}
	void SetDesc(const char *newdesc);
	inline const char *GetDesc(void) const {return desc;}

	// accessor for item name
	const char* GetName(void) const 
	{
		return user;
	}

	// accessor for fully qualified item name
	const char* GetFullPathName(UStr & resPath) const 
	{
		return fullpathname->GetFullPathName(resPath, user);
	}

	// returns non-null if this is a files that is marked as having merge conflict
	virtual const char* GetConflict() const
	{
		return 0L;
	}

	inline void SetUnmodified(bool state) {unmodified = state;}
	inline bool IsUnmodified(void) const {return 0 != unmodified;}

#ifdef macintosh
	FSSpec & MacSpec(void) {return macspec;}
	const FSSpec & GetMacSpec(void) const {return macspec;}
#endif /* macintosh */
protected:
	int ref;
	int missing : 1;
	int visited : 1;
	int unknown : 1;
	int unmodified : 1;
	int ignored : 1;
	int locked : 1;
	int removed : 1;
#ifdef macintosh
	FSSpec macspec;
#endif /* macintosh */
private:
	const char* user; // name of this item
	const char* desc; // its description
	EntnodePath *fullpathname; // fully qualified pathname of the folder containing the item
	
	static UStr sbuffer;
};

class EntnodeFile : public EntnodeData
{
public:
	EntnodeFile(const char* name, EntnodePath *path, const char *newvn = 0L,
		const char *newts = 0L, const char *newoptions = 0L,
		const char *newtag = 0L, const char *newdate = 0L,
		const char *newts_conflict = 0L);
protected:
	// cannot use 'delete', use UnRef() instead.
	virtual ~EntnodeFile();

// Attribute access
public:

	enum
	{
		kVN = 1,
		kTS = 5,
		kOption = 2,
		kTag = 4,
		kConflict = 6
	};

	// access fields
	virtual const char *operator[](int index) const
	{
		if(index == kVN)
			return vn;
		else if(index == kTS)
			return ts;
		else if(index == kOption)
			return option;
		else if(index == kTag)
			return tag != 0L ? tag : date;
		else if(index == kConflict)
			return ts_conflict;

		return EntnodeData::operator[](index);
	}

	virtual ent_type GetType(void) const
	{
		return ENT_FILE;
	}

	// returns non-null if this is a files that is marked as having merge conflict
	virtual const char* GetConflict() const
	{
		return ts_conflict;
	}

protected:
	const char *vn;
	const char *ts;
	const char *option;
	const char *tag; // can be nil
	const char *date; // can be nil
	const char *ts_conflict; // can be nil
};

class EntnodeFolder : public EntnodeData
{
// Construction
public:
	EntnodeFolder(const char* name, EntnodePath * path, const char *newvn = 0L,
		const char *newts = 0L, const char *newoptions = 0L, const char *newtag = 0L,
		const char *newdate = 0L, const char *newts_conflict = 0L);

protected:
	// cannot use 'delete', use UnRef() instead.
	virtual ~EntnodeFolder();

// Attribute access
public:
	// access fields
	virtual const char *operator[](int index) const
	{
		return EntnodeData::operator[](index);
	}

	virtual ent_type GetType(void) const
	{
		return ENT_SUBDIR;
	}

protected:
};

// ENTNODE gets a node file or a node folder
// and reference it.
class ENTNODE
{
public:
	ENTNODE() : shareData(0L) {}
	ENTNODE(EntnodeData *data) : shareData(data->Ref()) {}
	ENTNODE(EntnodeData & data) : shareData(data.Ref()) {}
	ENTNODE(const ENTNODE & anode) : shareData(0L)
	{
		*this = anode;
	}
	~ENTNODE()
	{
		if(shareData != 0L)
			shareData->UnRef();
	}

	inline static int Compare(const ENTNODE & node1, const ENTNODE & node2)
	{
		return EntnodeData::Compare(*node1.shareData, *node2.shareData);
	}

	inline ENTNODE & operator=(const ENTNODE & anode)
	{
		EntnodeData* oldData = shareData;
		shareData = anode.shareData;
		if(shareData != 0L)
		{
			shareData->Ref();
		}
		if(oldData != 0L)
		{
			oldData->UnRef();			
		}
		return *this;
	}

	EntnodeData* Data() const
	{
		return shareData;
	}

protected:
	EntnodeData *shareData;
};

bool Entries_Open (CSortList<ENTNODE> & entries, const char *fullpath);
	// return false if no CVS/Entries

EntnodeData *Entries_SetVisited(const char *path, CSortList<ENTNODE> & entries, const char *name,
	const struct stat & finfo, bool isFolder, const std::vector<CStr> * ignlist = 0L);
	// fill an ENTNODE when the file appears on the disk : it set
	// some flags like "visited", "unknown", "ignored"... and return
	// a reference to the node.

void Entries_SetMissing(CSortList<ENTNODE> & entries);
	// After the traversal and for the entries which didn't have a
	// "Entries_SetVisited" call associated with, mark them to be "missing".

bool Tag_Open(CStr & tag, const char *fullpath);
	// return false if no CVS/Tag

#endif /* CVSENTRIES_H */
