/******************************************************************************
 GSimpleFileTreeNode.cc

	Data structure for JXFileTree.

	Copyright <A9> 1997 by Glenn W. Bach. All rights reserved.

 *****************************************************************************/

#include "GSimpleFileTreeNode.h"
#include "GSimpleFileTree.h"
#include "JTreeList.h"

#include <JXApplication.h>

#include <JString.h>
#include <JPtrArray.h>
#include <JUNIXDirInfo.h>
#include <JError.h>
#include <JColormap.h>

#include <jGlobals.h>
#include <jDirUtil.h>
#include <jFileUtil.h>
#include <jStreamUtil.h>

#include <jXGlobals.h>

#include <iostream.h>
#include <fstream.h>
#include <iomanip.h>
#include <sys/stat.h>
#include <unistd.h>
#include <jAssert.h>


/******************************************************************************
	Constructor

******************************************************************************/

GSimpleFileTreeNode::GSimpleFileTreeNode
	(
	GSimpleFileTree* tree, 
	JUNIXDirEntry* 	entry,
	const char* 	name,
	const JBoolean 	isBranch
	)
	:
	JNamedTreeNode(tree, name, isBranch)
{	
	GSimpleFileTreeNodeX(tree, entry, name, isBranch);
}

GSimpleFileTreeNode::GSimpleFileTreeNode
	(
	GSimpleFileTree* tree, 
	JUNIXDirEntry* 	entry,
	JNamedTreeNode* parent,
	const char* 	name,
	const JBoolean 	isBranch
	)
	:
	JNamedTreeNode(NULL, name, isBranch)
{
	if (parent != NULL)
		{
		parent->Append(this);
		}
	GSimpleFileTreeNodeX(tree, entry, name, isBranch);
}

void
GSimpleFileTreeNode::GSimpleFileTreeNodeX
	(
	GSimpleFileTree* tree, 
	JUNIXDirEntry* 	entry,
	const char* 	name,
	const JBoolean 	isBranch
	)
{
	itsTree 				= tree;
	itsEntry 				= entry;
	itsChildrenInitialized	= kFalse;
	
	struct stat stbuf;
	stat(GetFullPathAndName(), &stbuf);
	itsModTime = stbuf.st_mtime;
	itsStatusTime = stbuf.st_ctime;
	itsDirInfo = NULL;
	if (isBranch && (GetName().GetLastCharacter() != '/'))
		{
		SetName(GetName() + "/");
		}
}
/******************************************************************************
	Destructor

******************************************************************************/

GSimpleFileTreeNode::~GSimpleFileTreeNode()
{
	if (itsDirInfo != NULL)
		{
		delete itsDirInfo;
		}
	delete itsEntry;
}



/******************************************************************************
	BuildChildList

******************************************************************************/

void
GSimpleFileTreeNode::BuildChildList()
{
	if (itsDirInfo == NULL)
		{
		JUNIXDirInfo::Create(GetFullPathAndName(),&itsDirInfo);
		}
		
	JSize itsNumChildren = itsDirInfo->GetElementCount();

	JBoolean entryIsBranch;
	
	for (JSize i = 1; i<= itsNumChildren; i++)
		{
		const JUNIXDirEntry& theDirEntry = itsDirInfo->GetEntry(i);

		if ( (theDirEntry.GetType() == JUNIXDirEntry::kDir) || 
			 (theDirEntry.GetType() == JUNIXDirEntry::kDirLink) )
			{
			entryIsBranch = kTrue;
			}
		
		else
			{
			entryIsBranch = kFalse;
			}
			
		if ((theDirEntry.GetName() != "..") && OKToAppendChild(theDirEntry))
			{
			JUNIXDirEntry* entry = new JUNIXDirEntry(theDirEntry);
			GSimpleFileTreeNode* node;
			CreateChild(entry, &node, entryIsBranch);
			}
		}

}

/******************************************************************************
 OKToOpen

 ******************************************************************************/

JBoolean 
GSimpleFileTreeNode::OKToOpen()
	const
{
	if (!JNamedTreeNode::OKToOpen())
		{
		return kFalse;
		}
	JXGetApplication()->DisplayBusyCursor();
	GSimpleFileTreeNode* th = const_cast<GSimpleFileTreeNode*>(this);
	if (itsChildrenInitialized)
		{
		th->Update();
		return kTrue;
		}
	else
		{

		if (!IsRoot())
			{
			th->BuildChildList();
			}
		th->itsChildrenInitialized = kTrue;
		return kTrue;
		}
	return kFalse;		
}

/******************************************************************************
 OKToChange

 ******************************************************************************/

JBoolean 
GSimpleFileTreeNode::OKToChange()
	const
{
	return kTrue;
}

/******************************************************************************
	GetType
 
 ******************************************************************************/

JUNIXDirEntry::Type
GSimpleFileTreeNode::GetType()
	const
{
	return itsEntry->GetType();
}


/******************************************************************************
 Update

 ******************************************************************************/

void 
GSimpleFileTreeNode::Update()
{
	struct stat info;
	if (lstat(GetFullPathAndName(), &info) != 0 || 
		stat(GetFullPathAndName(), &info) != 0 ||
		itsModTime != info.st_mtime ||
		itsStatusTime != info.st_ctime )
		{
		ForceUpdate();
		}
		
}

/******************************************************************************
 ForceUpdate

 ******************************************************************************/

void 
GSimpleFileTreeNode::ForceUpdate()
{
	struct stat info;
	stat(GetFullPathAndName(), &info);
	itsModTime = info.st_mtime;
	itsStatusTime = info.st_ctime;

	itsModTime = info.st_mtime;
	itsStatusTime = info.st_ctime;

	if (itsDirInfo != NULL)
		{
		itsDirInfo->ForceUpdate();
		UpdateChildren();
		
		JPtrArray<JTreeNode>* children;
		JSize count = GetChildCount();
		for (JSize i = 1; i <= count; i++)
			{
			JTreeNode* tnode = GetChild(i);
			if (itsTree->GetTreeList()->IsOpen(tnode))
				{
				GSimpleFileTreeNode* node = dynamic_cast(GSimpleFileTreeNode*, tnode);
				assert (node != NULL);
				node->Update();
				}
			}
		}
		
	itsEntry->ForceUpdate();				
}


/******************************************************************************
 UpdateChildren

 ******************************************************************************/

void
GSimpleFileTreeNode::UpdateChildren()
{
	JPtrArray<JTreeNode>* theChildren = new JPtrArray<JTreeNode>;
	assert(theChildren != NULL);

	theChildren->SetSortOrder(JOrderedSetT::kSortAscending);

	if (itsDirInfo == NULL)
		{
		JUNIXDirInfo::Create(GetFullPathAndName(),&itsDirInfo);
		}
	
	JSize itsNumChildren = itsDirInfo->GetElementCount();
	JBoolean entryIsBranch;
	
	for (JSize i = 1; i<= itsNumChildren; i++)
		{
		const JUNIXDirEntry& theDirEntry = itsDirInfo->GetEntry(i);

		if ( (theDirEntry.GetType() == JUNIXDirEntry::kDir) || 
			 (theDirEntry.GetType() == JUNIXDirEntry::kDirLink) )
			{
			entryIsBranch = kTrue;
			}
		
		else
			{
			entryIsBranch = kFalse;
			}
			
		if ((theDirEntry.GetName() != "..") && OKToAppendChild(theDirEntry))
			{
			JUNIXDirEntry* entry = new JUNIXDirEntry(theDirEntry);
			GSimpleFileTreeNode* node;
			CreateChild(entry, &node, entryIsBranch, kFalse);
			theChildren->Append(node);
			}
		}
		
	for (JSize i = 1; i <= GetChildCount(); i++)
		{
		// get old child
		JTreeNode* node = GetChild(i);
		GSimpleFileTreeNode* oldEntry = dynamic_cast(GSimpleFileTreeNode*, node);
		assert (oldEntry != NULL);
		JBoolean match = kFalse;
		// loop through new children
		for (JSize j = 1; j <= theChildren->GetElementCount(); j++)
			{
			GSimpleFileTreeNode* newEntry = 
				dynamic_cast(GSimpleFileTreeNode*, theChildren->NthElement(j));
			assert (newEntry != NULL);
			
			if (newEntry->GetName() == oldEntry->GetName() &&
				newEntry->GetType() == oldEntry->GetType())
				{
				theChildren->DeleteElement(j);
				match = kTrue;
				break;
				}
			}
				
		if (!match)
			{
			delete node;
			i--;
			}

		}
				
		// add entries sorted.
	for (JSize j = 1; j <= theChildren->GetElementCount(); j++)
		{
		JNamedTreeNode* node = 
			dynamic_cast(JNamedTreeNode*, theChildren->NthElement(j));
		assert(node != NULL);
		InsertSorted(node);
		}

	delete theChildren;
}

/******************************************************************************
 GetDirEntry

 ******************************************************************************/

JUNIXDirEntry*
GSimpleFileTreeNode::GetDirEntry()
{
	return itsEntry;
}

/******************************************************************************
 GetFullPathAndName

 ******************************************************************************/

JString
GSimpleFileTreeNode::GetFullPathAndName()
	const
{
	if (IsRoot())
		{
		return GetName();
		}

	const JTreeNode* parent;
	if (!GetParent(&parent))
		{
		return GetName();
		}

	GSimpleFileTreeNode* node = dynamic_cast(GSimpleFileTreeNode*, parent);
	assert(node != NULL);
	return JString(itsEntry->GetFullName());
}

/******************************************************************************
 UpdateDir

 ******************************************************************************/

JBoolean 
GSimpleFileTreeNode::UpdateDir
	(
	JTreeList* list
	)
{
	JBoolean changed = kFalse;
	if (itsDirInfo != NULL)
		{
		struct stat info;
		if (lstat(GetFullPathAndName(), &info) != 0 || 
			stat(GetFullPathAndName(), &info) != 0 ||
			itsModTime != info.st_mtime ||
			itsStatusTime != info.st_ctime )
			{
			itsModTime = info.st_mtime;
			itsStatusTime = info.st_ctime;
			
			itsDirInfo->ForceUpdate();
			UpdateChildren();
			itsEntry->Update();
			changed = kTrue;
			}
		JBoolean childchanged = kFalse;
		JSize count = GetChildCount();
		for (JSize i = 1; i <= count; i++)
			{
			JTreeNode* tnode = GetChild(i);
			if (list->IsOpen(tnode))
				{
				GSimpleFileTreeNode* node = dynamic_cast(GSimpleFileTreeNode*, tnode);
				assert (node != NULL);
				childchanged = node->UpdateDir(list);
				if (childchanged)
					{
					changed = kTrue;
					}
				}
			}
		}
	return changed;
}

/******************************************************************************
 FindChild

 ******************************************************************************/

JBoolean 
GSimpleFileTreeNode::FindChild
	(
	const JCharacter* name, 
	GSimpleFileTreeNode** node
	)
{
	if (itsDirInfo == NULL)
		{
		return kFalse;
		}
	for (JSize i = 1; i <= GetChildCount(); i++)
		{
		JTreeNode* jnode = GetChild(i);
		GSimpleFileTreeNode* child = dynamic_cast(GSimpleFileTreeNode*, jnode);
		assert(child != NULL);
		JString test = child->GetName();
		if (test.GetLastCharacter() == '/')
			{
			test.RemoveSubstring(test.GetLength(), test.GetLength());
			}
		if (test == name)
			{
			*node = child;
			return kTrue;
			}
		}
	return kFalse;
}

/******************************************************************************
 GetFontStyle

 ******************************************************************************/

JFontStyle	
GSimpleFileTreeNode::GetFontStyle()
{
	JBoolean italic = kFalse;
	if ( (itsEntry->GetType() == JUNIXDirEntry::kFileLink) ||
		 (itsEntry->GetType() == JUNIXDirEntry::kDirLink) )
		{
		italic = kTrue;	
		}
	JColorIndex color = JGetCurrColormap()->GetBlackColor();
	return JFontStyle(kFalse, italic, 0, kFalse, color);
}

/******************************************************************************
 OKToAppendChild

 ******************************************************************************/

JBoolean	
GSimpleFileTreeNode::OKToAppendChild
	(
	const JUNIXDirEntry& entry
	)
{
	return kTrue;
}
/******************************************************************************
 CreateChild

 ******************************************************************************/

void	
GSimpleFileTreeNode::CreateChild
	(
	JUNIXDirEntry* 			entry,
	GSimpleFileTreeNode** 	node,
	const JBoolean 			isBranch,
	const JBoolean			append
	)
{
	if (append)
		{
		*node = new GSimpleFileTreeNode(itsTree, entry, 
					this, entry->GetName(), isBranch);
		assert( node != NULL);
		}
	else
		{
		*node = new GSimpleFileTreeNode(itsTree, entry, 
					NULL, entry->GetName(), isBranch);
		assert( node != NULL);
		}
}

/******************************************************************************
 SetNameAndUpdate (public)

 ******************************************************************************/

void	
GSimpleFileTreeNode::SetNameAndUpdate
	(
	const JCharacter* newName
	)
{
	SetName(newName);
	JString path = itsEntry->GetPath();
	delete itsEntry;
	itsEntry = new JUNIXDirEntry(path, newName);
}