// -*- C++ -*-

/* 
 * GChemPaint library
 * cycle.cc 
 *
 * Copyright (C) 2001-2004 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 "molecule.h"

gcpCycle::gcpCycle(gcpMolecule* Molecule): gcpChain(Molecule, (gcpAtom*)NULL, CycleType)
{
}

gcpCycle::~gcpCycle()
{
}

void gcpCycle::Simplify()
{
	std::map<gcpAtom*, gcpChainElt>::iterator i, k;
	for (i = m_Bonds.begin(); i != m_Bonds.end(); i++)
	{
		if ((*i).second.fwd->IsCyclic() > 1)
		{
			std::list<gcpCycle*>::iterator j;
			gcpCycle* pCycle = (*i).second.fwd->GetFirstCycle(j, this);
			while (pCycle)
			{
				for (k = pCycle->m_Bonds.begin(); (*k).second.fwd != (*i).second.fwd; k++);
				gcpAtom *pAtom0 = (*i).first, *pAtom1, *pAtom2;
				bool bReverse = (pAtom0 != (*k).first);
				gcpBond *pBond0 = (*i).second.fwd, *pBond;
				gcpCycle Common(NULL);
				Common.m_Bonds[pAtom0].fwd = pBond0;
				pAtom1 = pAtom2 = pAtom0;
				pBond = pBond0;
				pAtom2 = (gcpAtom*)pBond0->GetAtom(pAtom2);
				Common.m_Bonds[pAtom2].rev = pBond;
				if (bReverse)
				{
					//explore forward
					while ((pBond = m_Bonds[pAtom2].fwd) == pCycle->m_Bonds[pAtom2].rev)
					{
						Common.m_Bonds[pAtom2].fwd = pBond;
						pAtom2 = (gcpAtom*)pBond->GetAtom(pAtom2);
						Common.m_Bonds[pAtom2].rev = pBond;
					}
					//explore reverse
					while ((pBond0 = m_Bonds[pAtom1].rev) == pCycle->m_Bonds[pAtom1].fwd)
					{
						Common.m_Bonds[pAtom1].rev = pBond0;
						pAtom1 = (gcpAtom*)pBond0->GetAtom(pAtom1);
						Common.m_Bonds[pAtom1].rev = pBond0;
					}
				}
				else
				{
					//explore forward
					while ((pBond = m_Bonds[pAtom2].fwd) == pCycle->m_Bonds[pAtom2].fwd)
					{
						Common.m_Bonds[pAtom2].fwd = pBond;
						pAtom2 = (gcpAtom*)pBond->GetAtom(pAtom2);
						Common.m_Bonds[pAtom2].rev = pBond;
					}
					//explore reverse
					while ((pBond0 = m_Bonds[pAtom1].rev) == pCycle->m_Bonds[pAtom1].rev)
					{
						Common.m_Bonds[pAtom1].rev = pBond0;
						pAtom1 = (gcpAtom*)pBond0->GetAtom(pAtom1);
						Common.m_Bonds[pAtom1].rev = pBond0;
					}
				}
				//Search if another portion is common to the two cycles
				gcpAtom* pAtom = (gcpAtom*)m_Bonds[pAtom2].fwd->GetAtom(pAtom2);
				while (pAtom != pAtom1)
				{
					if (pAtom->IsInCycle(pCycle)) break;
					pAtom = (gcpAtom*)m_Bonds[pAtom].fwd->GetAtom(pAtom);
				}
				if (pAtom != pAtom1) {pCycle = (*i).second.fwd->GetNextCycle(j, this); continue;}
				//Test for possible simplification
				bool bLargest = (m_Bonds.size() > pCycle->m_Bonds.size());
				unsigned n = (bLargest) ? pCycle->m_Bonds.size() : m_Bonds.size();
				if (2 * (Common.m_Bonds.size() - 1) > n)
				{
					if (bLargest)
					{
						Erase(pAtom1, pAtom2);
						if (bReverse)
						{
							pCycle->Extract(pAtom1, pAtom2, Common);
						}
						else
						{
							pCycle->Extract(pAtom2, pAtom1, Common);
							Common.Reverse();
						}
						Insert(pAtom1, pAtom2, Common);
					}
					else
					{
						Extract(pAtom2, pAtom1, Common);
						if (bReverse)
						{
							pCycle->Erase(pAtom2, pAtom1);
							pCycle->Insert(pAtom2, pAtom1, Common);
						}
						else
						{
							Common.Reverse();
							pCycle->Erase(pAtom1, pAtom2);
							pCycle->Insert(pAtom1, pAtom2, Common);
						}
					}
					Simplify();
					return;
				}
				pCycle = (*i).second.fwd->GetNextCycle(j, this);
			}
		}
	}
}

void gcpCycle::Erase(gcpAtom* pAtom1, gcpAtom* pAtom2)
{//This function is not safe
	gcpAtom *pAtom = (gcpAtom*)m_Bonds[pAtom1].fwd->GetAtom(pAtom1), *pAtom0;
	m_Bonds[pAtom1].fwd->RemoveCycle(this);
	m_Bonds[pAtom1].fwd = NULL;
	if (m_Bonds[pAtom1].rev->GetOrder() == 2) m_Bonds[pAtom1].rev->SetDirty();
	while (pAtom != pAtom2)
	{
		m_Bonds[pAtom].fwd->RemoveCycle(this);
		pAtom = (gcpAtom*)m_Bonds[pAtom].fwd->GetAtom(pAtom0 = pAtom);
		m_Bonds.erase(pAtom0);
	}
	m_Bonds[pAtom2].rev = NULL;
	if (m_Bonds[pAtom2].fwd->GetOrder() == 2) m_Bonds[pAtom2].fwd->SetDirty();
}

void gcpCycle::Insert(gcpAtom* pAtom1, gcpAtom* pAtom2, gcpChain& Chain)
{//This function is not safe
	if (m_Bonds[pAtom1].rev->GetOrder() == 2) m_Bonds[pAtom1].rev->SetDirty();
	m_Bonds[pAtom1].fwd = ((gcpCycle&)Chain).m_Bonds[pAtom1].fwd;
	m_Bonds[pAtom1].fwd->AddCycle(this);
	gcpAtom *pAtom = (gcpAtom*)m_Bonds[pAtom1].fwd->GetAtom(pAtom1);
	while (pAtom != pAtom2)
	{
		m_Bonds[pAtom] = ((gcpCycle&)Chain).m_Bonds[pAtom];
		m_Bonds[pAtom].fwd->AddCycle(this);
		pAtom = (gcpAtom*)m_Bonds[pAtom].fwd->GetAtom(pAtom);
	}
	m_Bonds[pAtom2].rev = ((gcpCycle&)Chain).m_Bonds[pAtom2].rev;
	if (m_Bonds[pAtom2].fwd->GetOrder() == 2) m_Bonds[pAtom2].fwd->SetDirty();
}

bool gcpCycle::IsBetterForBonds(gcpCycle* pCycle)
{
	//Returns true if pCycle is better
	//Prefered cycle should have a maximum unsaturation number
	unsigned n1 = GetUnsaturations(), n2 = pCycle->GetUnsaturations();
	if (n1 < n2) return true;
	else if (n1 > n2) return false;
	//Prefered cycle should be smallest
	n1 = m_Bonds.size();
	n2 = pCycle->m_Bonds.size();
	if (n1 > n2) return true;
	else if (n1 < n2) return false;
	//Prefered Cycle should have a maximum heteroatoms
	n1 = GetHeteroatoms();
	n2 = pCycle->GetHeteroatoms();
	if (n1 < n2) return true;
	//Prefered cycle should have the least common bonds
	n1 = GetFusedBonds();
	n2 = pCycle->GetFusedBonds();
	if (n1 > n2) return true;
	else return false;
	//if every thing is the same return false (this will be prefered)
}

void gcpCycle::GetAngles2D(gcpBond *pBond, double* a1, double* a2)
{
	gcpAtom *pAtom1 = (gcpAtom*)pBond->GetAtom(0), *pAtom2 = (gcpAtom*)pBond->GetAtom(1);
	if (m_Bonds[pAtom1].fwd == pBond)
	{
		*a1 = m_Bonds[pAtom1].rev->GetAngle2DRad(pAtom1);
		*a2 = m_Bonds[pAtom2].fwd->GetAngle2DRad(pAtom2);
	}
	else
	{
		*a1 = m_Bonds[pAtom1].fwd->GetAngle2DRad(pAtom1);
		*a2 = m_Bonds[pAtom2].rev->GetAngle2DRad(pAtom2);
	}
}

int gcpCycle::GetFusedBonds()
{
	int n = 0;
	std::map<gcpAtom*, gcpChainElt>::iterator i;
	for (i = m_Bonds.begin(); i != m_Bonds.end(); i++)
		if ((*i).second.fwd->IsCyclic() > 1) n++;
	return n;
}


syntax highlighted by Code2HTML, v. 0.9.1