/******************************************************************************
 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 <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 <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)
{
	GSimpleFileTreeNodeX(tree, entry, name, isBranch);
	if (parent != NULL)
		{
		parent->Append(this);
		}
}

void
GSimpleFileTreeNode::GSimpleFileTreeNodeX
	(
	GSimpleFileTree* tree, 
	JUNIXDirEntry* 	entry,
	const char* 	name,
	const JBoolean 	isBranch
	)
{
	itsEntry 				= entry;
	itsChildrenInitialized	= kFalse;
	itsDirInfo 				= NULL;
}

/******************************************************************************
	Destructor

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

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



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

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

void
GSimpleFileTreeNode::BuildChildList()
{
	if (itsDirInfo == NULL)
		{
		const JBoolean ok = JUNIXDirInfo::Create(GetFullPathAndName(),&itsDirInfo);
		assert( ok );
		}
		
	const JSize numChildren = itsDirInfo->GetElementCount();

	JBoolean entryIsBranch;
	
	for (JIndex i = 1; i<= numChildren; 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);
			assert( entry != NULL );
			GSimpleFileTreeNode* node;
			CreateChild(entry, &node, entryIsBranch);
			}
		}
}

/******************************************************************************
 OKToOpen (virtual protected)

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

JBoolean 
GSimpleFileTreeNode::OKToOpen()
	const
{
	if (!IsOpenable())
		{
		return kFalse;
		}
	if (!JUNIXDirInfo::OKToCreateUNIXDirInfo(GetFullPathAndName()))
		{
		return kFalse;
		}
	GSimpleFileTreeNode* th = const_cast<GSimpleFileTreeNode*>(this);
	if (itsChildrenInitialized)
		{
		th->UpdateDir();
		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();
}

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

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

void
GSimpleFileTreeNode::UpdateChildren()
{
	JPtrArray<JTreeNode> theChildren;
	theChildren.SetSortOrder(JOrderedSetT::kSortAscending);

	if (itsDirInfo == NULL)
		{
		const JBoolean ok = JUNIXDirInfo::Create(GetFullPathAndName(),&itsDirInfo);
		assert( ok );
		}
	
	const JSize numChildren = itsDirInfo->GetElementCount();
	JBoolean entryIsBranch;
	
	for (JIndex i = 1; i<= numChildren; 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 (JIndex 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);
		GSimpleFileTreeNode* snode = dynamic_cast(GSimpleFileTreeNode*, node);
		assert (snode != NULL);
		}
		
	// something like the mod times may have changed, so you need to re-sort
	SortChildren();
}

/******************************************************************************
 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());
}

/******************************************************************************
 UpdateNode (public)

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

JBoolean
GSimpleFileTreeNode::UpdateNode
	(
	const JBoolean force
	)
{
	if (force)
		{
		itsEntry->ForceUpdate();
		return kTrue;
		}
	return itsEntry->Update();
}

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

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

JBoolean 
GSimpleFileTreeNode::UpdateDir
	(
	const JBoolean force
	)
{
	JBoolean changed = UpdateNode(force);
//	cout << changed << " " << itsEntry->GetModTime() << " " << itsEntry->GetStatusTime() << endl;
	if (itsDirInfo != NULL)
		{
		if (changed )
			{
			JString cwd = itsDirInfo->GetCWD();
			itsDirInfo->ForceUpdate();
			UpdateChildren();
			if (IsRoot() && (cwd != itsDirInfo->GetCWD()))
				{
				SetFullNameAndUpdate(itsDirInfo->GetCWD());
				PathChanged();
				}
			}
		JBoolean childchanged = kFalse;
		JSize count = GetChildCount();
		for (JSize i = 1; i <= count; i++)
			{
			JTreeNode* tnode = GetChild(i);
			GSimpleFileTreeNode* node = dynamic_cast(GSimpleFileTreeNode*, tnode);
			assert (node != NULL);
			if (tnode->IsOpenable())
				{
				JTree* tree = GetTree(); 
				GSimpleFileTree* stree = dynamic_cast(GSimpleFileTree*, tree);
				assert(stree != NULL);
				if (stree->GetTreeList()->IsOpen(tnode))
					{
					childchanged = node->UpdateDir(force);
					}
				else
					{
					childchanged = node->UpdateNode(force);
					}
				}
			else
				{
				childchanged = node->UpdateNode(force);
				}
			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::kBrokenLink) ||
		 (itsEntry->GetType() == JUNIXDirEntry::kUnknownLink) ||
		 (itsEntry->GetType() == JUNIXDirEntry::kDirLink) )
		{
		italic = kTrue;	
		}
	JColorIndex color = JGetCurrColormap()->GetBlackColor();
	if ((itsEntry->GetType() == JUNIXDirEntry::kBrokenLink) ||
		(itsEntry->GetType() == JUNIXDirEntry::kUnknownLink))
		{
		color = JGetCurrColormap()->GetGray60Color();
		}
	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
	)
{
	JTree* tree 			= NULL;
	GSimpleFileTree* stree	= NULL;
	if (GetTree(&tree))
		{
		stree = dynamic_cast(GSimpleFileTree*, tree);
		assert(stree != NULL);
		}

	if (append)
		{
		*node = new GSimpleFileTreeNode(stree, entry, 
					this, entry->GetName(), isBranch);
		assert( node != NULL);
		}
	else
		{
		*node = new GSimpleFileTreeNode(stree, 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 = NULL;
	itsEntry = new JUNIXDirEntry(path, newName);
	assert(itsEntry != NULL);
}

/******************************************************************************
 SetFullNameAndUpdate (public)

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

void	
GSimpleFileTreeNode::SetFullNameAndUpdate
	(
	const JCharacter* newFullName
	)
{
	delete itsEntry;
	itsEntry = NULL;
	itsEntry = new JUNIXDirEntry(newFullName);
	assert(itsEntry != NULL);
	SetName(newFullName);
}

/******************************************************************************
 CompareUser (static protected)

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

JOrderedSetT::CompareResult
GSimpleFileTreeNode::CompareUser
	(
	JTreeNode * const & e1,
	JTreeNode * const & e2
	)
{
	GSimpleFileTreeNode* n1	= dynamic_cast(GSimpleFileTreeNode*, e1);
	GSimpleFileTreeNode* n2	= dynamic_cast(GSimpleFileTreeNode*, e2);

	JString u1 = n1->itsEntry->GetUserName();
	JString u2 = n2->itsEntry->GetUserName();
	JOrderedSetT::CompareResult result =
		JCompareStringsCaseInsensitive(&(u1), &(u2));

	if (result == JOrderedSetT::kFirstEqualSecond)
		{
		return JNamedTreeNode::DynamicCastCompareNames(e1, e2);
		}
	return result;
}

/******************************************************************************
 CompareGroup (static protected)

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

JOrderedSetT::CompareResult
GSimpleFileTreeNode::CompareGroup
	(
	JTreeNode * const & e1,
	JTreeNode * const & e2
	)
{
	GSimpleFileTreeNode* n1	= dynamic_cast(GSimpleFileTreeNode*, e1);
	GSimpleFileTreeNode* n2	= dynamic_cast(GSimpleFileTreeNode*, e2);

	JString u1 = n1->itsEntry->GetGroupName();
	JString u2 = n2->itsEntry->GetGroupName();
	JOrderedSetT::CompareResult result =
		JCompareStringsCaseInsensitive(&(u1), &(u2));

	if (result == JOrderedSetT::kFirstEqualSecond)
		{
		return JNamedTreeNode::DynamicCastCompareNames(e1, e2);
		}
	return result;
}

/******************************************************************************
 CompareSize (static protected)

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

JOrderedSetT::CompareResult
GSimpleFileTreeNode::CompareSize
	(
	JTreeNode * const & e1,
	JTreeNode * const & e2
	)
{
	GSimpleFileTreeNode* n1	= dynamic_cast(GSimpleFileTreeNode*, e1);
	GSimpleFileTreeNode* n2	= dynamic_cast(GSimpleFileTreeNode*, e2);

	JOrderedSetT::CompareResult result;

	if (n1->IsOpenable() && n2->IsOpenable())
		{
		result = JOrderedSetT::kFirstEqualSecond;
		}
	else if (n1->IsOpenable())
		{
		result = JOrderedSetT::kFirstLessSecond;
		}
	else if (n2->IsOpenable())
		{
		result = JOrderedSetT::kFirstGreaterSecond;
		}
	else
		{
		result = JUNIXDirEntry::CompareSizes(n1->itsEntry, n2->itsEntry);
		}

	if (result == JOrderedSetT::kFirstEqualSecond)
		{
		return JNamedTreeNode::DynamicCastCompareNames(e1, e2);
		}
	return result;
}

/******************************************************************************
 CompareSize (static protected)

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

JOrderedSetT::CompareResult
GSimpleFileTreeNode::CompareDate
	(
	JTreeNode * const & e1,
	JTreeNode * const & e2
	)
{
	GSimpleFileTreeNode* n1	= dynamic_cast(GSimpleFileTreeNode*, e1);
	GSimpleFileTreeNode* n2	= dynamic_cast(GSimpleFileTreeNode*, e2);

	JOrderedSetT::CompareResult result =
		JUNIXDirEntry::CompareModTimes(n1->itsEntry, n2->itsEntry);

	if (result == JOrderedSetT::kFirstEqualSecond)
		{
		return JNamedTreeNode::DynamicCastCompareNames(e1, e2);
		}
	return result;
}

/******************************************************************************
 ChangeSort (private)

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

void
GSimpleFileTreeNode::ChangeSort
	(
	JOrderedSetT::CompareResult (*compareFn)(JTreeNode * const &, 
											 JTreeNode * const &),
	const JOrderedSetT::SortOrder order
	)
{	
	SetChildCompareFunction(compareFn, order);
	
	JSize count = GetChildCount();
	for (JSize i = 1; i <= count; i++)
		{
		JTreeNode* jnode = GetChild(i);
		GSimpleFileTreeNode* node = dynamic_cast(GSimpleFileTreeNode*, jnode);
		assert(node != NULL);
		node->ChangeSort(compareFn, order);
		}
}

/******************************************************************************
 PathChanged (virtual protected)

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

void
GSimpleFileTreeNode::PathChanged()
{
}
