/******************************************************************************
 GAddressBookMgr.cc


	Copyright © 1998 by Glenn W. Bach. All rights reserved.

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

#include "GAddressBookMgr.h"
#include "GAddressBookEntry.h"
#include "GAddressBookTreeNode.h"
#include "GAddressEntryTreeNode.h"
#include "GAddressItemTreeNode.h"

#include <JNamedTreeNode.h>
#include <JTree.h>

#include <GMGlobals.h>
#include <gMailUtils.h>

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

#include <X11/keysym.h>
#include <stdio.h>
#include <strstream.h>
#include <fstream.h>
#include <iostream.h>
#include <jAssert.h>

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

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

GAddressBookMgr::GAddressBookMgr()
	:
	JBroadcaster()
{
	itsAddresses = new JStringPtrMap<GAddressBookEntry>;
	assert(itsAddresses != NULL);
}

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

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

GAddressBookMgr::~GAddressBookMgr()
{
	itsAddresses->DeleteAll();
	delete itsAddresses;
}

/*****************************************************************************
 NameIsAlias

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

JBoolean
GAddressBookMgr::NameIsAlias
	(
	const JCharacter* 	name, 
	JString& 			alias, 
	JString& 			fcc
	)
{
	GAddressBookEntry* entry;
	if (itsAddresses->GetElement(name, &entry))
		{
		alias = entry->address;
		if (alias.IsEmpty())
			{
			return kFalse;
			}
		if (alias.GetFirstCharacter() == '(')
			{
			if (entry->address.GetLength() > 2)
				{
				alias = entry->address.GetSubstring(2, entry->address.GetLength() - 1);
				}
			}
		fcc = entry->fcc;
		if (!fcc.IsEmpty() && (fcc.GetFirstCharacter() == '('))
			{
			if (entry->fcc.GetLength() > 2)
				{
				fcc = entry->fcc.GetSubstring(2, entry->fcc.GetLength() - 1);
				}
			}
		return kTrue;
		}
	return kFalse;
}

/*****************************************************************************
 GetNextRecord

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

JBoolean
GAddressBookMgr::GetNextRecord
	(
	JString& line, 
	JString& record, 
	istream& is
	)
{
	if (line.IsEmpty())
		{
		return kFalse;
		}
	JIndex index;
	if (line.LocateSubstring("\t", &index))
		{
		if (index > 1)
			{
			record = line.GetSubstring(1, index - 1);
			line.RemoveSubstring(1, index);
			return kTrue;
			}
		line.RemoveSubstring(1, 1);
		return kFalse;
		}
	record = line;
	if (record.Contains("(") && !record.Contains(")"))
		{
		JString temp = JReadUntil(is, ')');
		record += temp + ")";
		line = JReadLine(is);
		if (!line.IsEmpty() && (line.GetFirstCharacter() == '\t'))
			{
			line.RemoveSubstring(1, 1);
			}
		}
	else
		{
		line.Clear();
		}
	return kTrue;
}

/******************************************************************************
 AddDefaultAddressBook

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

void
GAddressBookMgr::AddDefaultAddressBook
	(
	JTree* tree
	)
{
	JString homeDir;
	if (JGetHomeDirectory(&homeDir))
		{
		JAppendDirSeparator(&homeDir);
		JString addfile = homeDir + ".addressbook";
		AddAddressBook(addfile, tree);
		}
}

/******************************************************************************
 AddAddressBook (public)

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

void
GAddressBookMgr::AddAddressBook
	(
	const JCharacter* 	name,
	JTree* 				tree
	)
{
	if (!JFileExists(name))
		{
		return;
		}
	JUNIXDirEntry* dirEntry = new JUNIXDirEntry(name);
	assert(dirEntry != NULL);
	JTreeNode* jbase = tree->GetRoot();
	JNamedTreeNode* base = dynamic_cast(JNamedTreeNode*, jbase);
	assert(base != NULL);
	GAddressBookTreeNode* book = new 
		GAddressBookTreeNode(dirEntry, base, dirEntry->GetName());
	assert(book != NULL);
	ifstream is(name);
	while (is.good())
		{
		JString line = JReadLine(is);
		GAddressBookEntry* entry = new GAddressBookEntry();
		assert(entry != NULL);
		JString name;
		if (GetNextRecord(line, name, is))
			{
			GetNextRecord(line, entry->fullname, is);
			if (GetNextRecord(line, entry->address, is))
				{
				GetNextRecord(line, entry->fcc, is);
				GetNextRecord(line, entry->comment, is);
				itsAddresses->SetElement(name, entry);
				if (tree != NULL)
					{
					GAddressEntryTreeNode* aEntry = 
						new GAddressEntryTreeNode(book, entry->fullname);
					assert(aEntry != NULL);
					GAddressItemTreeNode* item =
						new GAddressItemTreeNode(GAddressItemTreeNode::kName, 
												 aEntry, name, kFalse);
					assert(item != NULL);
					JString address = entry->address;
					if (address.BeginsWith("(") && (address.GetLength() > 2))
						{
						address = address.GetSubstring(2, address.GetLength() - 1);
						JPtrArray<JString> list;
						GParseNameList(address, list);
						const JSize count = list.GetElementCount();
						for (JSize i = count; i >= 1; i--)
							{
							item =
								new GAddressItemTreeNode(GAddressItemTreeNode::kEMail, 
														 aEntry, *(list.NthElement(i)), kFalse);
							assert(item != NULL);
							}
						list.DeleteAll();
						}
					else
						{
						item =
							new GAddressItemTreeNode(GAddressItemTreeNode::kEMail, 
													 aEntry, address, kFalse);
						}
					JString comment = entry->comment;
					JString fcc		= entry->fcc;
					if (!(comment.IsEmpty()))
						{
						item =
							new GAddressItemTreeNode(GAddressItemTreeNode::kComment, 
													 aEntry, comment, kFalse);
						assert(item != NULL);
						}
					if (!(fcc.IsEmpty()))
						{
						item =
							new GAddressItemTreeNode(GAddressItemTreeNode::kFcc, 
													 aEntry, fcc, kFalse);
						assert(item != NULL);
						}
					book->InsertSorted(aEntry);
					}
				continue;
				}
			}
		delete entry;
		}
}

/******************************************************************************
 ChangeNickName (public)

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

JBoolean
GAddressBookMgr::ChangeNickName
	(
	const JCharacter* oldname, 
	const JCharacter* newname
	)
{
	GAddressBookEntry* entry;
	if (itsAddresses->GetElement(newname, &entry))
		{
		return kFalse;
		}	
	if (itsAddresses->GetElement(oldname, &entry))
		{
		itsAddresses->RemoveElement(oldname);
		itsAddresses->SetElement(newname, entry);
		return kTrue;
		}
	return kFalse;
}

/******************************************************************************
 AdjustProperties (public)

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

void
GAddressBookMgr::AdjustProperties
	(
	GAddressEntryTreeNode* node
	)
{
	JTreeNode* jchild1 = node->GetChild(1);
	GAddressItemTreeNode* child1 =
		dynamic_cast(GAddressItemTreeNode*, jchild1);
	assert(child1 != NULL);
	GAddressBookEntry* entry;
	if (itsAddresses->GetElement(child1->GetText(), &entry))
		{
		entry->fullname = node->GetName();
		entry->fcc.Clear();
		entry->comment.Clear();
		const JSize count = node->GetChildCount();
		JSize expCount = 2;
		if (node->HasFcc())
			{
			expCount++;
			}
		if (node->HasComment())
			{
			expCount++;
			}
		JBoolean multi_address = kFalse;
		if (expCount < count)
			{
			multi_address = kTrue;
			}
		JString address;
		if (multi_address)
			{
			address = "(";
			}
		for (JSize i = 2; i <= count; i++)
			{
			JTreeNode* jchild = node->GetChild(i);
			GAddressItemTreeNode* child =
				dynamic_cast(GAddressItemTreeNode*, jchild);
			assert(child != NULL);
			GAddressItemTreeNode::Type type = child->GetType();
			if (type == GAddressItemTreeNode::kFcc)
				{
				entry->fcc = child->GetText();
				}
			else if (type == GAddressItemTreeNode::kComment)
				{
				entry->comment = child->GetText();
				}
			else
				{
				if (!multi_address)
					{
					address = child->GetText();
					}
				else
					{
					if (address != "(")
						{
						address += ",";
						}
					address += child->GetText();
					if (i == count)
						{
						address += ")";
						}
					}
				}
			}
		entry->address = address;
		}
}

/******************************************************************************
 NewNickName (public)

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

JBoolean
GAddressBookMgr::NewNickName
	(
	const JCharacter* addresses,
	JString* 		  name
	)
{
	JString alias = "Untitled";
	JIndex num	  = 1;
	JIndex count  = 1;
	GAddressBookEntry* entry;
	while (itsAddresses->GetElement(alias, &entry))
		{
		alias = alias + JString(num);
		count++;
		if (count == 100)
			{
			return kFalse;
			}
		}
	entry = new GAddressBookEntry();
	assert(entry != NULL);
	JString add(addresses);
	if (add.IsEmpty())
		{
		add = "nobody";
		}
	entry->address = add;
	itsAddresses->SetElement(alias, entry);
	*name = alias;
	return kTrue;
}

/******************************************************************************
 DeleteName (public)

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

void
GAddressBookMgr::DeleteName
	(
	const JCharacter* name
	)
{
	itsAddresses->DeleteElement(name);
}