// -*- C++ -*-

/* 
 * GChemPaint atoms plugin
 * electrontool.cc 
 *
 * Copyright (C) 2004-2005 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 "electrontool.h"
#include "lib/application.h"
#include "lib/document.h"
#include "lib/electron.h"
#include "lib/molecule.h"
#include "lib/operation.h"
#include "lib/settings.h"
#include "libgcpcanvas/gcp-canvas-bpath.h"
#include <glib/gi18n-lib.h>
#include <cmath>
#include <stdexcept>

gcpElectronTool::gcpElectronTool (gcpApplication *App, string Id): gcpTool (App, Id)
{
	if (Id == string ("ElectronPair"))
		m_bIsPair = true;
	else if (Id == string ("UnpairedElectron"))
		m_bIsPair = false;
	else throw logic_error ("Unknown tool Id!"); // This should not happen.
}

gcpElectronTool::~gcpElectronTool ()
{
}

bool gcpElectronTool::OnClicked ()
{
	if (!m_pObject || (m_pObject->GetType () != AtomType))
		return false;
	/* explicit electrons will be authorized in fragments only when they will
	be fully implemented */
	if (m_pObject->GetParent ()->GetType () == FragmentType)
		return false;
	double x, y;
	gcpAtom *pAtom = (gcpAtom*) m_pObject;
	if (m_bIsPair) {
		if (!pAtom->HasImplicitElectronPairs ())
			return false;
	} else {
		if (!pAtom->MayHaveImplicitUnpairedElectrons ())
			return false;
	}
	pAtom->GetCoords (&m_x0, &m_y0);
	m_Pos = pAtom->GetAvailablePosition (x, y);
	m_x = x - m_x0;
	m_y = y - m_y0;
	ArtDRect rect;
	m_pData->GetObjectBounds (m_pObject, &rect);
	m_x0 *= m_dZoomFactor;
	m_y0 *= m_dZoomFactor;
	m_dDistMax = min (sqrt (square (rect.x0 - m_x0) + square (rect.y0 - m_y0)),
				sqrt (square (rect.x1 - m_x0) + square (rect.y0 - m_y0)));
	m_dAngle = atan (- m_y / m_x);
		if (m_x < 0) m_dAngle += M_PI;
	x *= m_dZoomFactor;
	y *= m_dZoomFactor;
	x += 2. * cos (m_dAngle);
	y -= 2. * sin (m_dAngle);
	if (m_bIsPair) {
		double deltax = 3. * sin (m_dAngle);
		double deltay = 3. * cos (m_dAngle);
		m_pItem =  gnome_canvas_item_new(
						m_pGroup,
						gnome_canvas_group_get_type (),
						NULL);
		gnome_canvas_item_new(
						GNOME_CANVAS_GROUP (m_pItem),
						gnome_canvas_ellipse_get_type(),
						"width_units", 0.0,
						"fill_color", AddColor,
						"x1", x + deltax - 2. ,
						"x2", x + deltax + 2.,
						"y1", y + deltay - 2.,
						"y2", y + deltay + 2.,
						NULL);
		gnome_canvas_item_new(
						GNOME_CANVAS_GROUP (m_pItem),
						gnome_canvas_ellipse_get_type(),
						"width_units", 0.0,
						"fill_color", AddColor,
						"x1", x - deltax - 2. ,
						"x2", x - deltax + 2.,
						"y1", y - deltay - 2.,
						"y2", y - deltay + 2.,
						NULL);
	} else {
		m_pItem = gnome_canvas_item_new(
						m_pGroup,
						gnome_canvas_ellipse_get_type(),
						"width_units", 0.0,
						"fill_color", AddColor,
						"x1", x - 2. ,
						"x2", x + 2.,
						"y1", y - 2.,
						"y2", y + 2.,
						NULL);
	}
	char tmp[32];
	snprintf(tmp, sizeof(tmp) - 1, _("Orientation: %g"), m_dAngle * 180. / M_PI);
	m_pApp->SetStatusText(tmp);
	m_bChanged = true;
	return true;
}

void gcpElectronTool::OnDrag ()
{
	if (!m_pItem)
		return;
	int old_pos = m_Pos;
	m_x -= m_x0;
	m_y -= m_y0;
	m_dDist = sqrt (square (m_x) + square (m_y));
	double Angle = atan (- m_y / m_x);
	if (isnan (Angle))
		Angle = m_dAngle;
	else if (m_x < 0)
		Angle += M_PI;
	if (!(m_nState & GDK_CONTROL_MASK)) {
		int pos = (int) rint (Angle * 4. / M_PI);
		Angle = (double) pos * M_PI / 4.;
		if (m_nState & GDK_SHIFT_MASK)
			pos = 8;
		else if (pos < 0)
			pos += 8;
		switch (pos) {
		case 0:
			m_Pos = POSITION_E;
			break;
		case 1:
			m_Pos = POSITION_NE;
			break;
		case 2:
			m_Pos = POSITION_N;
			break;
		case 3:
			m_Pos = POSITION_NW;
			break;
		case 4:
			m_Pos = POSITION_W;
			break;
		case 5:
			m_Pos = POSITION_SW;
			break;
		case 6:
			m_Pos = POSITION_S;
			break;
		case 7:
			m_Pos = POSITION_SE;
			break;
		default:
			m_Pos = 0;
		}
	} else
		m_Pos = 0;
	if ((Angle == m_dAngle) && !(m_nState & GDK_SHIFT_MASK)) {
		if (m_dDist < m_dDistMax) {
			if (!m_bChanged) {
				gnome_canvas_item_show (m_pItem);
				m_bChanged = true;
			}
		} else {
			if (m_bChanged) {
				gnome_canvas_item_hide (m_pItem);
				m_bChanged = false;
			}
		}
	} else {
		double x, y, x1, y1, x2, y2;
		gcpAtom *pAtom = (gcpAtom*)m_pObject;
		if (!(m_nState & GDK_SHIFT_MASK) && (m_dDist >= m_dDistMax) && m_bChanged) {
			gnome_canvas_item_hide (m_pItem);
			m_bChanged = false;
		} else if (pAtom->GetPosition (Angle * 180. / M_PI, x, y)) {
			m_dAngle = Angle;
			if (m_pItem)
			{
				gnome_canvas_item_get_bounds(GNOME_CANVAS_ITEM(m_pItem), &x1, &y1, &x2, &y2);
				gtk_object_destroy(GTK_OBJECT(GNOME_CANVAS_ITEM(m_pItem)));
				gnome_canvas_request_redraw(GNOME_CANVAS(m_pWidget), (int)x1, (int)y1, (int)x2, (int)y2);
				m_pItem = NULL;
			}
			if (m_nState & GDK_SHIFT_MASK) {
				x = m_x0 + m_dDist * cos (m_dAngle);
				y = m_y0 - m_dDist * sin (m_dAngle);
			} else {
				x = x * m_dZoomFactor;
				y = y * m_dZoomFactor;
				x += 2. * cos (m_dAngle);
				y -= 2. * sin (m_dAngle);
			}
			if (m_bIsPair) {
				double deltax = 3. * sin (m_dAngle);
				double deltay = 3. * cos (m_dAngle);
				m_pItem =  gnome_canvas_item_new(
								m_pGroup,
								gnome_canvas_group_get_type (),
								NULL);
				gnome_canvas_item_new(
								GNOME_CANVAS_GROUP (m_pItem),
								gnome_canvas_ellipse_get_type(),
								"width_units", 0.0,
								"fill_color", AddColor,
								"x1", x + deltax - 2. ,
								"x2", x + deltax + 2.,
								"y1", y + deltay - 2.,
								"y2", y + deltay + 2.,
								NULL);
				gnome_canvas_item_new(
								GNOME_CANVAS_GROUP (m_pItem),
								gnome_canvas_ellipse_get_type(),
								"width_units", 0.0,
								"fill_color", AddColor,
								"x1", x - deltax - 2. ,
								"x2", x - deltax + 2.,
								"y1", y - deltay - 2.,
								"y2", y - deltay + 2.,
								NULL);
			} else {
				m_pItem = gnome_canvas_item_new(
								m_pGroup,
								gnome_canvas_ellipse_get_type(),
								"width_units", 0.0,
								"fill_color", AddColor,
								"x1", x - 2. ,
								"x2", x + 2.,
								"y1", y - 2.,
								"y2", y + 2.,
								NULL);
			}
			m_bChanged = true;
		} else
			m_Pos = old_pos;
	}
	char tmp[32];
	snprintf(tmp, sizeof(tmp) - 1, _("Orientation: %g"), m_dAngle * 180. / M_PI);
	m_pApp->SetStatusText(tmp);
}

void gcpElectronTool::OnRelease ()
{
	if (!m_bChanged)
		return;
	gcpAtom *pAtom = (gcpAtom*) m_pObject;
	Object* pObj = m_pObject->GetGroup ();
	gcpDocument* pDoc = m_pView->GetDoc ();
	gcpOperation* pOp = pDoc-> GetNewOperation (GCP_MODIFY_OPERATION);
	pOp->AddObject (pObj, 0);
	gcpElectron *electron = new gcpElectron (pAtom, m_bIsPair);
	double Angle = m_dAngle * 180. / M_PI;
	if (!(m_nState & GDK_SHIFT_MASK))
		m_dDist = 0.;
	electron->SetPosition (m_Pos, Angle, m_dDist);
	m_pObject->EmitSignal (OnChangedSignal);
	pOp->AddObject(pObj, 1);
	pDoc->FinishOperation();
	m_pView->AddObject (electron);
	m_pView->Update (pAtom);
}


syntax highlighted by Code2HTML, v. 0.9.1