/* 
 * Gnome Chemistry Utils
 * object.cc 
 *
 * Copyright (C) 2002-2007 Jean Bréfort <jean.brefort@normalesup.org>
 *
 * 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 2 of the
 * License, 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., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301
 * USA
 */

#include "config.h"
#include "object.h"
#include "document.h"
#include <string>
#include <iostream>
#include <vector>
#include <cstring>

using namespace gcu;

Object::Object(TypeId Id)
{
	m_Type = Id;
	m_Id = NULL;
	m_Parent = NULL;
	m_Locked = 0;
}

Object::~Object()
{
	if (m_Id)
	{
		if (m_Parent) m_Parent->m_Children.erase(m_Id);
		g_free(m_Id);
	}
	map<string, Object*>::iterator i;
	while(!m_Children.empty())
	{
		i = m_Children.begin();
		if (m_Parent) m_Parent->AddChild((*i).second);
		else {
			(*i).second->m_Parent = NULL;
			delete (*i).second;
			m_Children.erase ((*i).first);
		}
	}
}


void Object::SetId (gchar* Id)
{
	if (!Id)
		return;
	if (m_Id) {
		if (!strcmp (Id, m_Id))
			return;
		if (m_Parent) m_Parent->m_Children.erase (m_Id);
		g_free(m_Id);
	}
	m_Id = g_strdup (Id);
	if (m_Parent) {
		Object *parent = m_Parent;
		m_Parent = NULL;
		parent->AddChild (this);
	}
}

Object* Object::GetMolecule()
{
	Object* object = this;
	while (object && (object->m_Type != MoleculeType)) object = object->m_Parent;
	return object;
}

Object* Object::GetReaction()
{
	Object* object = this;
	while (object && (object->m_Type != ReactionType)) object = object->m_Parent;
	return object;
}

Object* Object::GetGroup()
{
	if (!m_Parent || m_Parent->GetType () == DocumentType) return NULL;
	Object* object = m_Parent;
	while (object->m_Parent->GetType () != DocumentType)
		object = object->m_Parent;
	return object;
}

Document* Object::GetDocument()
{
	Object* object = this;
	while (object && (object->m_Type != DocumentType)) object = object->m_Parent;
	return (Document*) object;
}

Object* Object::GetParentOfType(TypeId Id)
{
	Object* object = this;
	while (object && (object->m_Type != Id)) object = object->m_Parent;
	return object;
}

void Object::AddChild(Object* object)
{
	Document* pDoc = GetDocument();
	if (!pDoc)
	{
		cerr << "Cannot add an object outside a document" << endl;
	}
	if (object->m_Id == NULL)
	{
		int i = 1;
		char szId[16];
		while (snprintf(szId, sizeof(szId), "o%d", i++), pDoc->GetDescendant(szId) != NULL);
		object->m_Id = g_strdup(szId);
	}
	else
	{
		Object* o = pDoc->RealGetDescendant(object->m_Id);
		if (o && ((pDoc != object->GetDocument()) || (object != o)))
		{
			gchar *buf = pDoc->GetNewId (object->m_Id);
			 if (object->m_Parent) {
				object->m_Parent->m_Children.erase (object->m_Id);
				object->m_Parent = NULL;
			}
			g_free(object->m_Id);
			object->m_Id = g_strdup(buf);
			delete [] buf;
		}
	}
	if (object->m_Parent)
	{
		object->m_Parent->m_Children.erase(object->m_Id);
		object->m_Parent = NULL;
	}
	object->m_Parent = this;
	m_Children[object->m_Id] = object;
}

void Object::SetParent(Object* Parent)
{
	if (Parent) Parent->AddChild(this);
	else
	{
		if (m_Parent) m_Parent->m_Children.erase(m_Id); 
		m_Parent = NULL;
	}
}

Object* Object::GetChild(const gchar* Id)
{
	if (Id == NULL) return NULL;
	Object* object = m_Children[Id];
	if (!object) m_Children.erase(Id);
	return object;
}

Object* Object::GetDescendant(const gchar* Id)
{
	if (Id == NULL) return NULL;
	Document* pDoc = GetDocument();
	string sId = pDoc->GetTranslatedId (Id);
	if (sId.size()) Id = sId.c_str();
	else pDoc->EraseTranslationId(Id);
	return RealGetDescendant (Id);
}

Object* Object::RealGetDescendant(const gchar* Id)
{
	Object* object = m_Children[Id];
	if (!object)
	{
		m_Children.erase (Id);
		map<string, Object*>::iterator i;
		for (i = m_Children.begin (); i != m_Children.end (); i++)
			if ((*i).second->HasChildren () && (object = (*i).second->RealGetDescendant (Id)))
				break;
	}
	return object;
}

Object* Object::GetFirstChild(map<string, Object*>::iterator& i)
{
	i = m_Children.begin();
	if (i == m_Children.end()) return NULL;
	return (*i).second;
}
	
Object* Object::GetNextChild(map<string, Object*>::iterator& i)
{
	i++;
	if (i == m_Children.end()) return NULL;
	return (*i).second;
}

xmlNodePtr Object::Save (xmlDocPtr xml)
{
	xmlNodePtr node;
	node = xmlNewDocNode (xml, NULL, (xmlChar*) GetTypeName (m_Type).c_str (), NULL);
	if (!node) return NULL;
	SaveId (node);
	
	if (!SaveChildren (xml, node))
	{
		xmlFreeNode (node);
		return NULL;
	}
	return node;
}

void Object::SaveId(xmlNodePtr node)
{
	if (m_Id && *m_Id) xmlNewProp(node, (xmlChar*)"id", (xmlChar*)m_Id);
}

bool Object::Load (xmlNodePtr node)
{
	xmlChar* tmp;
	xmlNodePtr child;
	Object* pObject;

	m_Locked++;
	tmp = xmlGetProp (node, (xmlChar*) "id");
	if (tmp) {
		SetId ((char*) tmp);
		xmlFree (tmp);
	}
	child = node->children;
	while (child) {
		node = (strcmp ((const char*) child->name, "object"))? child: child->children;
		pObject = CreateObject ((const char*) node->name, this);
		if (pObject) {
			if (!pObject->Load (node))
				delete pObject;
		} else {
			m_Locked--;
			return false;
		}
		child = child->next;
	}
	m_Locked--;
	return true;
}

bool Object::SaveChildren(xmlDocPtr xml, xmlNodePtr node)
{
	map<string, Object*>::iterator i;
	xmlNodePtr child;
	for (i = m_Children.begin(); i != m_Children.end(); i++)
	{
		if ((child = (*i).second->Save(xml))) xmlAddChild(node, child);
		else return false;
	}
	return true;
}

xmlNodePtr Object::GetNodeByProp(xmlNodePtr root, char* Property, char* Id)
{
	return GetNextNodeByProp(root->children, Property, Id);
}

xmlNodePtr Object::GetNextNodeByProp(xmlNodePtr node, char* Property, char* Id)
{
	char *txt;
	while (node)
	{
		txt = (char*)xmlGetProp(node, (xmlChar*)Property);
		if (!strcmp(txt, Id)) break;
		node = node ->next;
	}
	return node;
}

xmlNodePtr Object::GetNodeByName(xmlNodePtr root, char* Name)
{
	return GetNextNodeByName(root->children, Name);
}

xmlNodePtr Object::GetNextNodeByName(xmlNodePtr node, char* Name)
{
	while (node)
	{
		if (!strcmp((char*)node->name, Name)) break;
		node = node ->next;
	}
	return node;
}

void Object::Move(double x, double y, double z)
{
	map<string, Object*>::iterator i;
	for (i = m_Children.begin(); i != m_Children.end(); i++) (*i).second->Move(x, y, z);
}

void Object::Transform2D(Matrix2D& m, double x, double y)
{
	map<string, Object*>::iterator i;
	for (i = m_Children.begin(); i != m_Children.end(); i++) (*i).second->Transform2D(m, x, y);
}

bool Object::BuildContextualMenu (GtkUIManager *UIManager, Object *object, double x, double y)
{
	return (m_Parent)? m_Parent->BuildContextualMenu (UIManager, object, x, y): false;
}

void Object::Add (GtkWidget* w)
{
	map<string, Object*>::iterator i;
	Object* p = GetFirstChild (i);
	while (p)
	{
		p->Add (w);
		p = GetNextChild (i);
	}
}

void Object::Print(GnomePrintContext *pc)
{
}

void Object::Update(GtkWidget* w)
{
	map<string, Object*>::iterator i;
	for (i = m_Children.begin(); i != m_Children.end(); i++) (*i).second->Update(w);
}

void Object::SetSelected(GtkWidget* w, int state)
{
	map<string, Object*>::iterator i;
	for (i = m_Children.begin(); i != m_Children.end(); i++) (*i).second->SetSelected(w, state);
}

Object* Object::GetAtomAt(double x, double y, double z)
{
	return NULL;
}

bool Object::Build (list<Object*>& Children) throw (invalid_argument)
{
	return false;
}

double Object::GetYAlign ()
{
	return 0.0;
}

static TypeId NextType = OtherType;

class TypeDesc
{
public:
	TypeDesc ();

	TypeId Id;
	Object* (*Create) ();
	set <TypeId> PossibleChildren;
	set <TypeId> PossibleParents;
	set <TypeId> RequiredChildren;
	set <TypeId> RequiredParents;
	string CreationLabel;
};

TypeDesc::TypeDesc ()
{
	Id = NoType;
	Create = NULL;
}

static map<string, TypeDesc> Types;
static vector<string> TypeNames;

TypeId Object::AddType(string TypeName, Object*(*Create)(), TypeId id)
{
	TypeDesc& typedesc = Types[TypeName];
	typedesc.Create = Create;
	if (id == OtherType) {
		typedesc.Id = NextType;
		NextType = TypeId ((unsigned) NextType + 1);
	} else
		typedesc.Id = id;
	if (TypeNames.size() <= typedesc.Id) {
		size_t max = (((size_t) typedesc.Id / 10) + 1) * 10;
		TypeNames.resize (max);
	}
#if HAS_VECTOR_AT
	TypeNames.at (typedesc.Id) = TypeName;
#else
	vector<string>::iterator it;
	it = TypeNames.begin ();
	it += typedesc.Id;
	it = TypeNames.insert (it, TypeName);
	it++;
	TypeNames.erase (it);
#endif
	return typedesc.Id;
}

Object* Object::CreateObject(const string& TypeName, Object* parent)
{
	TypeDesc& typedesc = Types[TypeName];
	Object* pObj = (typedesc.Create)? typedesc.Create(): NULL;
	if (parent && pObj) {
		if (pObj->m_Id) {
			char* newId = parent->GetDocument()->GetNewId (pObj->m_Id, false);
			pObj->SetId (newId);
			delete [] newId;
		}
		parent->AddChild(pObj);
	}
	return pObj;
}

TypeId Object::GetTypeId (const string& Name)
{
	TypeDesc& typedesc = Types[Name];
	TypeId res = typedesc.Id;
	if (res == NoType)
		Types.erase (Name);
	return res;
}

string Object::GetTypeName (TypeId Id)
{
	return TypeNames[Id];
}

void Object::AddRule (TypeId type1, RuleId rule, TypeId type2)
{
	AddRule (TypeNames[type1], rule, TypeNames[type2]);
}

void Object::AddRule (const string& type1, RuleId rule, const string& type2)
{
	if (!type1.size() || !type2.size ())
		return;
	TypeDesc& typedesc1 = Types[type1];
	if (typedesc1.Id == NoType) {
		Types.erase (type1);
		return;
	}
	TypeDesc& typedesc2 = Types[type2];
	if (typedesc2.Id == NoType) {
		Types.erase (type2);
		return;
	}
	switch (rule) {
		case RuleMustContain:
			typedesc1.RequiredChildren.insert (typedesc2.Id);
		case RuleMayContain:
			typedesc1.PossibleChildren.insert (typedesc2.Id);
			typedesc2.PossibleParents.insert (typedesc1.Id);
			break;
		case RuleMustBeIn:
			typedesc1.RequiredParents.insert (typedesc2.Id);
		case RuleMayBeIn:
			typedesc2.PossibleChildren.insert (typedesc1.Id);
			typedesc1.PossibleParents.insert (typedesc2.Id);
			break;
	}
}

const set<TypeId>& Object::GetRules (TypeId type, RuleId rule)
{
	return GetRules (TypeNames[type], rule);
}

const set<TypeId>& Object::GetRules (const string& type, RuleId rule)
{
	static set<TypeId> noId;
	TypeDesc& typedesc = Types[type];
	switch (rule) {
	case RuleMustContain:
		return typedesc.RequiredChildren;
	case RuleMayContain:
		return typedesc.PossibleChildren;
	case RuleMustBeIn:
		return typedesc.RequiredParents;
	case RuleMayBeIn:
		return typedesc.PossibleParents;
	default:
		return noId;
	}
	return noId;
}

static void AddAncestorTypes (TypeId type, set<TypeId>& types)
{
	const set<TypeId>& new_types = Object::GetRules (type, RuleMayBeIn);
	set<TypeId>::iterator i = new_types.begin (), end = new_types.end ();
	for (; i != end; i++) {
		types.insert (*i);
		AddAncestorTypes (*i, types);
	}
}

void Object::GetPossibleAncestorTypes (set<TypeId>& types)
{
	AddAncestorTypes (m_Type, types);
}

void Object::SetCreationLabel (TypeId Id, string Label)
{
	TypeDesc& type = Types[TypeNames[Id]];
	type.CreationLabel = Label;
}

const string& Object::GetCreationLabel (TypeId Id)
{
	return Types[TypeNames[Id]].CreationLabel;
}

const string& Object::GetCreationLabel (const string& TypeName)
{
	return Types[TypeName].CreationLabel;
}

static SignalId NextSignal = 0;

SignalId Object::CreateNewSignalId ()
{
	return NextSignal++;
}

void Object::EmitSignal (SignalId Signal)
{
	Object *obj = NULL;
	Object *ancestor = this;
	while (ancestor && !ancestor->IsLocked () && ancestor->OnSignal (Signal, obj)) {
		obj = ancestor;
		ancestor = obj->m_Parent;
	}
}

bool Object::OnSignal (SignalId Signal, Object *Child)
{
	return true;
}

Object* Object::GetFirstLink (set<Object*>::iterator& i)
{
	i = m_Links.begin();
	if (i == m_Links.end()) return NULL;
	return *i;
}

Object* Object::GetNextLink(set<Object*>::iterator& i)
{
	i++;
	if (i == m_Links.end()) return NULL;
	return *i;
}

void Object::Unlink (Object *object)
{
	m_Links.erase (object);
	OnUnlink (object);
}

void Object::OnUnlink (Object *object)
{
}

void Object::Lock (bool state)
{
	if (state)
		m_Locked++;
	else if (m_Locked > 0)
		m_Locked--;
}


syntax highlighted by Code2HTML, v. 0.9.1