// -*- C++ -*-

/* 
 * GChemPaint library
 * reaction-step.cc 
 *
 * Copyright (C) 2004-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 "gchempaint-config.h"
#include "reaction-step.h"
#include "reaction.h"
#include "reactant.h"
#include "reaction-arrow.h"
#include "reaction-operator.h"
#include "document.h"
#include "theme.h"
#include "view.h"
#include "widgetdata.h"
#include "libgnomecanvas/gnome-canvas.h"

TypeId ReactionStepType = NoType;

gcpReactionStep::gcpReactionStep (): Object (ReactionStepType)
{
	SetId ((char*) "rs1");
	m_bLoading = false;
}

gcpReactionStep::gcpReactionStep (gcpReaction *reaction, map<double, Object*>& Children, map<Object*, ArtDRect> Objects): Object (ReactionStepType)
{
	SetId ((char*) "rs1");
	reaction->AddChild (this);
	GetDocument ()->EmptyTranslationTable ();
	gcpDocument *pDoc = dynamic_cast <gcpDocument*> (GetDocument ());
	gcpTheme *pTheme = pDoc->GetTheme ();
	gcpWidgetData  *pData= (gcpWidgetData*) g_object_get_data (G_OBJECT (pDoc->GetWidget ()), "data");
	map<double, Object*>::iterator im, endm;
	double x, y, x0, y0, x1, y1;
	ArtDRect *rect;
	Object *cur;
	im = Children.begin ();
	new gcpReactant (this, (*im).second);
	endm = Children.end ();
	rect = &Objects[(*im).second];
	x = rect->x1;
	y = (*im).second->GetYAlign ();
	for (im++; im != endm; im++) {
		x += pTheme->GetSignPadding ();
		//Add a sign
		gcpReactionOperator* pOp = new gcpReactionOperator ();
		AddChild (pOp);
		pOp->SetCoords (x / pTheme->GetZoomFactor (), y);
		pDoc->AddObject (pOp);
		gnome_canvas_update_now (GNOME_CANVAS (pData->Canvas));
		gnome_canvas_item_get_bounds (GNOME_CANVAS_ITEM (pData->Items[pOp]), &x0, &y0, &x1, &y1);
		pOp->Move ((x - x0) / pTheme->GetZoomFactor (), 0);
		x += pTheme->GetSignPadding () + x1 - x0;
		cur = (*im).second;
		new gcpReactant (this, cur);
		rect = &Objects[cur];
		y0 = cur->GetYAlign ();
		cur->Move ((x - rect->x0) / pTheme->GetZoomFactor (), y - y0);
		x+= rect->x1 - rect->x0;
	}
	Update (pData->Canvas);
	gnome_canvas_update_now (GNOME_CANVAS (pData->Canvas));
	m_bLoading = false;
}

gcpReactionStep::~gcpReactionStep ()
{
	if (IsLocked ())
		return;
	set<gcpReactionArrow *>::iterator i, end = m_Arrows.end();
	for (i = m_Arrows.begin (); i != end; i++)
		(*i)->RemoveStep (this);
	/* If there are no chidren, don't care and exit */
	if (!GetChildrenNumber ())
		return;
	/* Now, destroy children and add the reactant contents to the reaction parent,
	not to the reaction */
	gcpDocument *pDoc = reinterpret_cast<gcpDocument *> (GetDocument ());
	gcpOperation *pOp = pDoc->GetCurrentOperation ();
	gcpReaction *Reaction = reinterpret_cast<gcpReaction *> (GetParent ());
	if (!Reaction)
		return;
	Object *pObj = Reaction->GetParent (), *Child, *Group = Reaction->GetGroup ();
	map<string, Object *>::iterator j;
	gcpReactant *Reactant;
	while (HasChildren ()) {
		Child = GetFirstChild (j);
		if (Child->GetType () == ReactionOperatorType) {
			pDoc->Remove (Child);
			continue;
		}
		Reactant = reinterpret_cast<gcpReactant *> (Child);
		Child = Reactant->GetStoichChild ();
		if (Child)
			pDoc->Remove (Child);
		Child = Reactant->GetChild ();
		if (Child) {
			Child->SetParent (pObj);
			if (pOp && !Group)
				pOp->AddObject (Child, 1);
		}
		delete Reactant;
	}
}
	
xmlNodePtr gcpReactionStep::Save (xmlDocPtr xml)
{
	xmlNodePtr node;
	node = xmlNewDocNode (xml, NULL, (xmlChar*) "reaction-step", NULL);
	if (!node) return NULL;
	SaveId (node);
	map<string, Object*>::iterator i;
	Object *obj = GetFirstChild (i);
	xmlNodePtr child;
	while (obj) {
		if ((*i).second->GetType () != ReactionOperatorType) {
			if ((child = (*i).second->Save (xml)))
				xmlAddChild (node, child);
			else
				return NULL;
		}
		obj = GetNextChild (i);
	}
	return node;
}
	
bool gcpReactionStep::Load (xmlNodePtr node)
{
	m_bLoading = true;
	if (!Object::Load (node))
		return false;
	map<Object*, ArtDRect> Objects;
	map<double, Object*> Children;
	ArtDRect rect;
	map<string, Object*>::iterator i;
	Object *pObj = GetFirstChild (i);
	gcpDocument *pDoc = dynamic_cast <gcpDocument*> (GetDocument ());
	gcpTheme *pTheme = pDoc->GetTheme ();
	gcpWidgetData  *pData= (gcpWidgetData*) g_object_get_data (G_OBJECT (pDoc->GetWidget ()), "data");
	map<double, Object*>::iterator im, endm;
	double x, y, x0, y0, x1, y1;
	gnome_canvas_update_now (GNOME_CANVAS (pData->Canvas));
	while (pObj) {
		pData->GetObjectBounds (pObj, &rect);
		x = (rect.x0 + rect.x1) / 2;
		while (Children[x] != NULL)
			x += 1e-5;
		Children[x] = pObj;
		Objects[pObj] = rect;
		pObj = GetNextChild (i);
	}
	im = Children.begin ();
	endm = Children.end ();
	rect = Objects[(*im).second];
	x = rect.x1;
	y = (*im).second->GetYAlign ();
	for (im++; im != endm; im++) {
		x += pTheme->GetSignPadding ();
		//Add a sign
		gcpReactionOperator* pOp = new gcpReactionOperator();
		AddChild (pOp);
		pOp->SetCoords(x / pTheme->GetZoomFactor (), y);
		pDoc->AddObject(pOp);
		gnome_canvas_update_now (GNOME_CANVAS (pData->Canvas));
		gnome_canvas_item_get_bounds (GNOME_CANVAS_ITEM (pData->Items[pOp]), &x0, &y0, &x1, &y1);
		pOp->Move ((x - x0) / pTheme->GetZoomFactor (), 0);
		x += pTheme->GetSignPadding () + x1 - x0;
		pObj = (*im).second;
		rect = Objects[pObj];
		x+= rect.x1 - rect.x0;
	}
	Update (pData->Canvas);
	m_bLoading = false;
	return true;
}

double gcpReactionStep::GetYAlign ()
{
	map<string, Object*>::iterator i;
	GetFirstChild (i);
	return ((*i).second)? (*i).second->GetYAlign (): 0.;
}

bool gcpReactionStep::OnSignal (SignalId Signal, Object *Child)
{
	if (Signal == OnChangedSignal) {
		if (m_bLoading)
			return false;
		map<Object*, ArtDRect> Objects;
		map<double, Object*> Children;
		list<Object*> Operators;
		ArtDRect rect;
		map<string, Object*>::iterator i;
		Object *pObj = GetFirstChild (i);
		gcpDocument *pDoc = dynamic_cast <gcpDocument*> (GetDocument ());
		gcpTheme *pTheme = pDoc->GetTheme ();
		gcpView *pView = pDoc->GetView ();
		gcpWidgetData  *pData= (gcpWidgetData*) g_object_get_data (G_OBJECT (pDoc->GetWidget ()), "data");
		map<double, Object*>::iterator im, endm;
		double x, y, x0, y0, x1, y1;
//		gnome_canvas_update_now (GNOME_CANVAS (pData->Canvas));
		while (pObj) {
			if (pObj->GetType () == ReactionOperatorType)
				Operators.push_front (pObj);
			else {
				pData->GetObjectBounds (pObj, &rect);
				x = (rect.x0 + rect.x1) / 2;
				while (Children[x] != NULL)
					x += 1e-5;
				Children[x] = pObj;
				Objects[pObj] = rect;
			}
			pObj = GetNextChild (i);
		}
		while (!Operators.empty ()) {
			pObj = Operators.front ();
			pView->Remove (pObj);
			delete pObj;
			Operators.pop_front ();
		}
		im = Children.begin ();
		endm = Children.end ();
		rect = Objects[(*im).second];
		x = rect.x1;
		y = (*im).second->GetYAlign ();
		for (im++; im != endm; im++) {
			x += pTheme->GetSignPadding ();
			//Add a sign
			gcpReactionOperator* pOp = new gcpReactionOperator();
			AddChild (pOp);
			pOp->SetCoords(x / pTheme->GetZoomFactor (), y);
			pDoc->AddObject(pOp);
			gnome_canvas_update_now (GNOME_CANVAS (pData->Canvas));
			gnome_canvas_item_get_bounds (GNOME_CANVAS_ITEM (pData->Items[pOp]), &x0, &y0, &x1, &y1);
			pOp->Move ((x - x0) / pTheme->GetZoomFactor (), 0);
			x += pTheme->GetSignPadding () + x1 - x0;
			pObj = (*im).second;
			rect = Objects[pObj];
			pObj->Move ((x - rect.x0) / pTheme->GetZoomFactor (), y - pObj->GetYAlign ());
			x+= rect.x1 - rect.x0;
		}
		Update (pData->Canvas);
		return true;
	} else
		return true;
}

void gcpReactionStep::RemoveArrow (gcpReactionArrow *arrow) {
	m_Arrows.erase (arrow);
	if (m_Arrows.empty ()) {
		// if there is no more arrows this is no more a reaction step
		delete this;
	}
}

void gcpReactionStep::Add (GtkWidget* w)
{
	/* Now that Object::Add adds children, we need to override this method
	because a molecule children (atoms, bonds, and fragments) are added to
	the widget by the document. FIXME! FIXME! FIXME! CHANGE THIS OLD WEIRD CODE!
	*/
}


syntax highlighted by Code2HTML, v. 0.9.1