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

    TRAVIS - Trajectory Analyzer and Visualizer

    http://www.travis-analyzer.de/

    Copyright (c) 2009-2026 Martin Brehm
                  2012-2017 Martin Thomas
                  2016-2020 Sascha Gehrke

    Please cite:  J. Chem. Phys. 2020, 152 (16), 164105.         (DOI 10.1063/5.0005078 )
                  J. Chem. Inf. Model. 2011, 51 (8), 2007-2023.  (DOI 10.1021/ci200217w )

    This file was written by Martin Brehm.

    ---------------------------------------------------------------------------

    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 3 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, see <http://www.gnu.org/licenses/>.

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


// This must always be the first include directive
#include "config.h"

#include "internalcoord2.h"
#include "globalvar.h"


const char *GetRevisionInfo_internalcoord2(unsigned int len) {
	static char buf[256];
	GET_REVISION_INFO( buf, len );
	return buf;
}


const char *GetSourceVersion_internalcoord2() {
	static char buf[256];
	GET_SOURCE_VERSION( buf );
	return buf;
}



bool CInternalCoordEngine::Parse( CTimeStep *ts ) {

	int z, z2, z3, z4, zb;
	int i0, i1, i2, i3;
	double tf;
	CMolecule *m;
	CSingleMolecule *sm;


	mprintf("      Scanning topology...\n");

	for (z3=0;z3<g_oaMolecules.GetSize();z3++) {

		m = (CMolecule*)g_oaMolecules[z3];

		for (z4=0;z4<m->m_laSingleMolIndex.GetSize();z4++) {

			sm = (CSingleMolecule*)g_oaSingleMolecules[m->m_laSingleMolIndex[z4]];

			for (z=0;z<sm->m_laBonds.GetSize();z++)
				m_iaBonds.push_back( sm->m_laBonds[z] );
		}
	}

	for (z=0;z<(int)m_iaBonds.size()/2;z++) {

		for (z2=z+1;z2<(int)m_iaBonds.size()/2;z2++) {

			if (m_iaBonds[z*2] == m_iaBonds[z2*2]) {

				m_iaAngles.push_back(m_iaBonds[z*2+1]);
				m_iaAngles.push_back(m_iaBonds[z*2]);
				m_iaAngles.push_back(m_iaBonds[z2*2+1]);

			} else if (m_iaBonds[z*2] == m_iaBonds[z2*2+1]) {

				m_iaAngles.push_back(m_iaBonds[z*2+1]);
				m_iaAngles.push_back(m_iaBonds[z*2]);
				m_iaAngles.push_back(m_iaBonds[z2*2]);

			} else if (m_iaBonds[z*2+1] == m_iaBonds[z2*2]) {

				m_iaAngles.push_back(m_iaBonds[z*2]);
				m_iaAngles.push_back(m_iaBonds[z*2+1]);
				m_iaAngles.push_back(m_iaBonds[z2*2+1]);

			} else if (m_iaBonds[z*2+1] == m_iaBonds[z2*2+1]) {

				m_iaAngles.push_back(m_iaBonds[z*2]);
				m_iaAngles.push_back(m_iaBonds[z*2+1]);
				m_iaAngles.push_back(m_iaBonds[z2*2]);
			}
		}
	}

	for (z=0;z<(int)m_iaAngles.size()/3;z++) {

		for (z2=z+1;z2<(int)m_iaAngles.size()/3;z2++) {

			if ((m_iaAngles[z*3] == m_iaAngles[z2*3+1]) && (m_iaAngles[z*3+1] == m_iaAngles[z2*3+2])) {

				m_iaDihedrals.push_back(m_iaAngles[z2*3]);
				m_iaDihedrals.push_back(m_iaAngles[z2*3+1]);
				m_iaDihedrals.push_back(m_iaAngles[z*3+1]);
				m_iaDihedrals.push_back(m_iaAngles[z*3+2]);

			} else if ((m_iaAngles[z*3+2] == m_iaAngles[z2*3+1]) && (m_iaAngles[z*3+1] == m_iaAngles[z2*3+2])) {

				m_iaDihedrals.push_back(m_iaAngles[z2*3]);
				m_iaDihedrals.push_back(m_iaAngles[z2*3+1]);
				m_iaDihedrals.push_back(m_iaAngles[z*3+1]);
				m_iaDihedrals.push_back(m_iaAngles[z*3]);

			} else if ((m_iaAngles[z*3] == m_iaAngles[z2*3+1]) && (m_iaAngles[z*3+1] == m_iaAngles[z2*3])) {

				m_iaDihedrals.push_back(m_iaAngles[z2*3+2]);
				m_iaDihedrals.push_back(m_iaAngles[z2*3+1]);
				m_iaDihedrals.push_back(m_iaAngles[z*3+1]);
				m_iaDihedrals.push_back(m_iaAngles[z*3+2]);

			} else if ((m_iaAngles[z*3+2] == m_iaAngles[z2*3+1]) && (m_iaAngles[z*3+1] == m_iaAngles[z2*3])) {

				m_iaDihedrals.push_back(m_iaAngles[z2*3+2]);
				m_iaDihedrals.push_back(m_iaAngles[z2*3+1]);
				m_iaDihedrals.push_back(m_iaAngles[z*3+1]);
				m_iaDihedrals.push_back(m_iaAngles[z*3]);
			}
		}
	}

	for (z=0;z<(int)m_iaBonds.size()/2;z++) {

		for (zb=0;zb<2;zb++) {

			if (zb == 0) {
				i0 = m_iaBonds[z*2];
				i1 = m_iaBonds[z*2+1];
			} else {
				i0 = m_iaBonds[z*2+1];
				i1 = m_iaBonds[z*2];
			}

			for (z2=z+1;z2<(int)m_iaBonds.size()/2;z2++) {

				if (m_iaBonds[z2*2] == i0)
					i2 = m_iaBonds[z2*2+1];
				else if (m_iaBonds[z2*2+1] == i0)
					i2 = m_iaBonds[z2*2];
				else
					continue;

				for (z3=z2+1;z3<(int)m_iaBonds.size()/2;z3++) {

					if (m_iaBonds[z3*2] == i0)
						i3 = m_iaBonds[z3*2+1];
					else if (m_iaBonds[z3*2+1] == i0)
						i3 = m_iaBonds[z3*2];
					else
						continue;

				/*	mprintf("      %2s%-3d - %2s%-3d - %2s%-3d - %2s%-3d ...\n",
						(const char*)((CAtom*)g_oaAtoms[sm->m_baAtomIndex[((CMolAtom*)sm->m_oaMolAtoms[i0])->m_iType]])->m_sName,
						((CMolAtom*)sm->m_oaMolAtoms[i0])->m_iNumber+1,
						(const char*)((CAtom*)g_oaAtoms[sm->m_baAtomIndex[((CMolAtom*)sm->m_oaMolAtoms[i1])->m_iType]])->m_sName,
						((CMolAtom*)sm->m_oaMolAtoms[i1])->m_iNumber+1,
						(const char*)((CAtom*)g_oaAtoms[sm->m_baAtomIndex[((CMolAtom*)sm->m_oaMolAtoms[i2])->m_iType]])->m_sName,
						((CMolAtom*)sm->m_oaMolAtoms[i2])->m_iNumber+1,
						(const char*)((CAtom*)g_oaAtoms[sm->m_baAtomIndex[((CMolAtom*)sm->m_oaMolAtoms[i3])->m_iType]])->m_sName,
						((CMolAtom*)sm->m_oaMolAtoms[i3])->m_iNumber+1
					);*/

					// Check for fourth bond on i0
					for (z4=z3+1;z4<(int)m_iaBonds.size()/2;z4++) {

						if ((m_iaBonds[z4*2] == i0) || (m_iaBonds[z4*2+1] == i0)) {
				//			mprintf("        Fourth atom.\n");
							goto _ni;
						}
					}

					tf = Dihedral(
							FoldVector(ts->m_vaCoords[i0] - ts->m_vaCoords[i1]),
							FoldVector(ts->m_vaCoords[i3] - ts->m_vaCoords[i2]),
							FoldVector(ts->m_vaCoords[i1] - ts->m_vaCoords[i2]),
							true
						);

					if (tf > 90.0)
						tf = 180.0 - tf;

				//	mprintf("        Angle is %.2f degree.\n",tf);

					if (tf > 25.0)
						continue;

					m_iaImpropers.push_back(i0);
					m_iaImpropers.push_back(i1);
					m_iaImpropers.push_back(i2);
					m_iaImpropers.push_back(i3);
_ni:;
				}
			}
		}
	}

	mprintf("        Found %lu bonds, %lu angles, %lu dihedrals, and %lu impropers.\n\n",(unsigned long)m_iaBonds.size()/2,(unsigned long)m_iaAngles.size()/3,(unsigned long)m_iaDihedrals.size()/4,(unsigned long)m_iaImpropers.size()/4);

	FILE *a;

	a = OpenFileWrite( "icoord_indices.txt", true );

	fprintf(a,"# %lu bonds, %lu angles, %lu dihedrals, %lu impropers\n",(unsigned long)m_iaBonds.size()/2,(unsigned long)m_iaAngles.size()/3,(unsigned long)m_iaDihedrals.size()/4,(unsigned long)m_iaImpropers.size()/4);

	for (z=0;z<(int)m_iaBonds.size()/2;z++)
		fprintf( a, "%4d  %4d              #  %s[%d] %2s%-2d - %s[%d] %2s%-2d\n",
			m_iaBonds[z*2],
			m_iaBonds[z*2+1],
			((CMolecule*)g_oaMolecules[ g_waAtomMolIndex[ m_iaBonds[z*2] ] ])->m_sName,
			g_laAtomSMLocalIndex[ m_iaBonds[z*2] ]+1,
			(const char*)((CAtom*)g_oaAtoms[ g_waAtomRealElement[ m_iaBonds[z*2] ] ])->m_sName,
			g_waAtomMolNumber[ m_iaBonds[z*2] ]+1,
			((CMolecule*)g_oaMolecules[ g_waAtomMolIndex[ m_iaBonds[z*2+1] ] ])->m_sName,
			g_laAtomSMLocalIndex[ m_iaBonds[z*2+1] ]+1,
			(const char*)((CAtom*)g_oaAtoms[ g_waAtomRealElement[ m_iaBonds[z*2+1] ] ])->m_sName,
			g_waAtomMolNumber[ m_iaBonds[z*2+1] ]+1
		);

	for (z=0;z<(int)m_iaAngles.size()/3;z++)
		fprintf( a, "%4d  %4d  %4d        #  %s[%d] %2s%-2d - %s[%d] %2s%-2d - %s[%d] %2s%-2d\n",
			m_iaAngles[z*3],
			m_iaAngles[z*3+1],
			m_iaAngles[z*3+2],
			((CMolecule*)g_oaMolecules[ g_waAtomMolIndex[ m_iaAngles[z*3] ] ])->m_sName,
			g_laAtomSMLocalIndex[ m_iaAngles[z*3] ]+1,
			(const char*)((CAtom*)g_oaAtoms[ g_waAtomRealElement[ m_iaAngles[z*3] ] ])->m_sName,
			g_waAtomMolNumber[ m_iaAngles[z*3] ]+1,
			((CMolecule*)g_oaMolecules[ g_waAtomMolIndex[ m_iaAngles[z*3+1] ] ])->m_sName,
			g_laAtomSMLocalIndex[ m_iaAngles[z*3+1] ]+1,
			(const char*)((CAtom*)g_oaAtoms[ g_waAtomRealElement[ m_iaAngles[z*3+1] ] ])->m_sName,
			g_waAtomMolNumber[ m_iaAngles[z*3+1] ]+1,
			((CMolecule*)g_oaMolecules[ g_waAtomMolIndex[ m_iaAngles[z*3+2] ] ])->m_sName,
			g_laAtomSMLocalIndex[ m_iaAngles[z*3+2] ]+1,
			(const char*)((CAtom*)g_oaAtoms[ g_waAtomRealElement[ m_iaAngles[z*3+2] ] ])->m_sName,
			g_waAtomMolNumber[ m_iaAngles[z*3+2] ]+1
		);

	for (z=0;z<(int)m_iaDihedrals.size()/4;z++)
		fprintf( a, "%4d  %4d  %4d  %4d  #  %s[%d] %2s%-2d - %s[%d] %2s%-2d - %s[%d] %2s%-2d - %s[%d] %2s%-2d\n",
			m_iaDihedrals[z*4],
			m_iaDihedrals[z*4+1],
			m_iaDihedrals[z*4+2],
			m_iaDihedrals[z*4+3],
			((CMolecule*)g_oaMolecules[ g_waAtomMolIndex[ m_iaDihedrals[z*4] ] ])->m_sName,
			g_laAtomSMLocalIndex[ m_iaDihedrals[z*4] ]+1,
			(const char*)((CAtom*)g_oaAtoms[ g_waAtomRealElement[ m_iaDihedrals[z*4] ] ])->m_sName,
			g_waAtomMolNumber[ m_iaDihedrals[z*4] ]+1,
			((CMolecule*)g_oaMolecules[ g_waAtomMolIndex[ m_iaDihedrals[z*4+1] ] ])->m_sName,
			g_laAtomSMLocalIndex[ m_iaDihedrals[z*4+1] ]+1,
			(const char*)((CAtom*)g_oaAtoms[ g_waAtomRealElement[ m_iaDihedrals[z*4+1] ] ])->m_sName,
			g_waAtomMolNumber[ m_iaDihedrals[z*4+1] ]+1,
			((CMolecule*)g_oaMolecules[ g_waAtomMolIndex[ m_iaDihedrals[z*4+2] ] ])->m_sName,
			g_laAtomSMLocalIndex[ m_iaDihedrals[z*4+2] ]+1,
			(const char*)((CAtom*)g_oaAtoms[ g_waAtomRealElement[ m_iaDihedrals[z*4+2] ] ])->m_sName,
			g_waAtomMolNumber[ m_iaDihedrals[z*4+2] ]+1,
			((CMolecule*)g_oaMolecules[ g_waAtomMolIndex[ m_iaDihedrals[z*4+3] ] ])->m_sName,
			g_laAtomSMLocalIndex[ m_iaDihedrals[z*4+3] ]+1,
			(const char*)((CAtom*)g_oaAtoms[ g_waAtomRealElement[ m_iaDihedrals[z*4+3] ] ])->m_sName,
			g_waAtomMolNumber[ m_iaDihedrals[z*4+3] ]+1
		);

	for (z=0;z<(int)m_iaImpropers.size()/4;z++)
		fprintf( a, "%4d  %4d  %4d  %4d  #  %s[%d] %2s%-2d - %s[%d] %2s%-2d - %s[%d] %2s%-2d - %s[%d] %2s%-2d\n",
			m_iaImpropers[z*4],
			m_iaImpropers[z*4+1],
			m_iaImpropers[z*4+2],
			m_iaImpropers[z*4+3],
			((CMolecule*)g_oaMolecules[ g_waAtomMolIndex[ m_iaImpropers[z*4] ] ])->m_sName,
			g_laAtomSMLocalIndex[ m_iaImpropers[z*4] ]+1,
			(const char*)((CAtom*)g_oaAtoms[ g_waAtomRealElement[ m_iaImpropers[z*4] ] ])->m_sName,
			g_waAtomMolNumber[ m_iaImpropers[z*4] ]+1,
			((CMolecule*)g_oaMolecules[ g_waAtomMolIndex[ m_iaImpropers[z*4+1] ] ])->m_sName,
			g_laAtomSMLocalIndex[ m_iaImpropers[z*4+1] ]+1,
			(const char*)((CAtom*)g_oaAtoms[ g_waAtomRealElement[ m_iaImpropers[z*4+1] ] ])->m_sName,
			g_waAtomMolNumber[ m_iaImpropers[z*4+1] ]+1,
			((CMolecule*)g_oaMolecules[ g_waAtomMolIndex[ m_iaImpropers[z*4+2] ] ])->m_sName,
			g_laAtomSMLocalIndex[ m_iaImpropers[z*4+2] ]+1,
			(const char*)((CAtom*)g_oaAtoms[ g_waAtomRealElement[ m_iaImpropers[z*4+2] ] ])->m_sName,
			g_waAtomMolNumber[ m_iaImpropers[z*4+2] ]+1,
			((CMolecule*)g_oaMolecules[ g_waAtomMolIndex[ m_iaImpropers[z*4+3] ] ])->m_sName,
			g_laAtomSMLocalIndex[ m_iaImpropers[z*4+3] ]+1,
			(const char*)((CAtom*)g_oaAtoms[ g_waAtomRealElement[ m_iaImpropers[z*4+3] ] ])->m_sName,
			g_waAtomMolNumber[ m_iaImpropers[z*4+3] ]+1
		);

	fclose(a);

	a = OpenFileWrite( "icoord_values.txt", true );
	fclose( a );

	return true;
}



void CInternalCoordEngine::ProcessStep( CTimeStep *ts ) {

	FILE *a;
	int z;
	CxDVector3 vec1, vec2;

	a = fopen( "icoord_values.txt", "ab" );

	fprintf(a,"# %lu bonds, %lu angles, %lu dihedrals, %lu impropers\n",(unsigned long)m_iaBonds.size()/2,(unsigned long)m_iaAngles.size()/3,(unsigned long)m_iaDihedrals.size()/4,(unsigned long)m_iaImpropers.size()/4);

	for (z=0;z<(int)m_iaBonds.size()/2;z++) {
		vec1 = ts->m_vaCoords[ m_iaBonds[z*2+1] ] - ts->m_vaCoords[ m_iaBonds[z*2] ];
		fprintf(a,"    %16.10f\n", FoldedLength( vec1 ) / 100.0 );
	}

	for (z=0;z<(int)m_iaAngles.size()/3;z++) {
		vec1 = ts->m_vaCoords[ m_iaAngles[z*3] ]   - ts->m_vaCoords[ m_iaAngles[z*3+1] ];
		vec2 = ts->m_vaCoords[ m_iaAngles[z*3+2] ] - ts->m_vaCoords[ m_iaAngles[z*3+1] ];
		fprintf(a,"    %16.10f\n", Angle_Deg( vec1, vec2 ) );
	}

	for (z=0;z<(int)m_iaDihedrals.size()/4;z++) {
		fprintf(a,"    %16.10f\n", Dihedral(
				FoldVector( ts->m_vaCoords[m_iaDihedrals[z*4]]   - ts->m_vaCoords[m_iaDihedrals[z*4+1]] ),
				FoldVector( ts->m_vaCoords[m_iaDihedrals[z*4+3]] - ts->m_vaCoords[m_iaDihedrals[z*4+2]] ),
				FoldVector( ts->m_vaCoords[m_iaDihedrals[z*4+2]] - ts->m_vaCoords[m_iaDihedrals[z*4+1]] ),
				true
			)
		);
	}
	for (z=0;z<(int)m_iaImpropers.size()/4;z++) {
		fprintf(a,"    %16.10f\n", Dihedral(
				FoldVector( ts->m_vaCoords[m_iaImpropers[z*4]]   - ts->m_vaCoords[m_iaImpropers[z*4+1]] ),
				FoldVector( ts->m_vaCoords[m_iaImpropers[z*4+3]] - ts->m_vaCoords[m_iaImpropers[z*4+2]] ),
				FoldVector( ts->m_vaCoords[m_iaImpropers[z*4+1]] - ts->m_vaCoords[m_iaImpropers[z*4+2]] ),
				true
			)
		);
	}

	fclose( a );
}



void CInternalCoordEngine::Finish() {

}






