/*
   Project: UL

   Copyright (C) 2005 Michael Johnston & Jordi Villa-Freixa

   Author: Michael Johnston

   Created: 2005-07-14 13:34:47 +0200 by michael johnston

   This application 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 application 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
   Library General Public License for more details.

   You should have received a copy of the GNU General Public
   License along with this library; if not, write to the Free
   Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111 USA.
*/

#include "ULInteractionsBuilder.h"


/*
Make "Base Additions" - Base functions that include
objective-c?
**/

static int atomIndexSorter(id,  id, void *);

static int sortIndexesByNames(id indexOne, id indexTwo, void* atomNames)
{
	NSString* atomOneName, *atomTwoName;

	atomOneName = [(NSArray*)atomNames objectAtIndex: [indexOne intValue]];
	atomTwoName = [(NSArray*)atomNames objectAtIndex: [indexTwo intValue]];

	return [atomOneName compare: atomTwoName 
		options: NSCaseInsensitiveSearch];
}


@implementation ULInteractionsBuilder

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

Finding parameters

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

/**
This is a workaround until FFML is fully developed.
We know we are working with Enzymix so we can make
certain assumptions here e.g. the diehedral interactions

note: Improper Torsions that have no parameters are removed
*/

- (void) _findParametersForInteractions: (NSDictionary*) topology ofAtoms: (NSMutableArray*) atomList
{
	int columns, rowCount;
	id class, topologyNode, interaction, row, idString, idStrings, atom;
	NSEnumerator *interactionEnum, *rowEnum, *atomEnum;	
	NSDictionary* topologyClassNodes;
	NSMutableArray* atomArray;
	NSString* interactionType;
	NSMutableString* failString, *missingParams;
	NSMutableIndexSet* unknownInteractions;

	//get the correct topology information

	topologyClassNodes = [parameterLibrary topologiesForClass: @"generic"];

	NSDebugLLog(@"ULInteractionsBuilder", @"Finding parameters for %@", [topology valueForKey:@"InteractionType"]);

	rowEnum = [[[topology valueForKey:@"Matrix"] matrixRows] objectEnumerator];
	class = [topologyClassNodes valueForKey:[topology valueForKey:@"InteractionType"]];
	atomArray  = [NSMutableArray arrayWithCapacity:1];
	interactionType = [topology valueForKey: @"InteractionType"];
	unknownInteractions = [NSMutableIndexSet indexSet];
	
	failString = [NSMutableString stringWithCapacity: 1];
	missingParams = [NSMutableString stringWithCapacity: 1];
	rowCount = 0;	

	while(row = [rowEnum nextObject])
	{
		//hackity hack hack hack!

		if([interactionType isEqual:@"FourierTorsion"])
		{
			[atomArray addObject: 
				[atomList objectAtIndex: [[row objectAtIndex:1] intValue]]];
			[atomArray addObject: 
				[atomList objectAtIndex: [[row objectAtIndex:2] intValue]]];
		}
		else if([interactionType isEqual:@"HarmonicImproperTorsion"])
		{
			[atomArray addObject: 
				[atomList objectAtIndex: [[row objectAtIndex:2] intValue]]];
		}
		else
		{
			atomEnum = [row objectEnumerator];
			while(atom = [atomEnum nextObject])
				[atomArray addObject:
					 [atomList objectAtIndex: [atom intValue]]];
		}

		idString = [atomArray componentsJoinedByString:@""];
		interactionEnum = [[class children] objectEnumerator];
		
		while(interaction = [interactionEnum nextObject])
		{
			idStrings = [interaction idStringsForInteraction];
			if([idStrings containsObject: idString])
			{
				[row addObjectsFromArray: [interaction parameters]];
				[row addObjectsFromArray: [interaction constraints]];
				break;
			}
		}

		if([row count] == [[topology valueForKey: @"ElementsPerInteraction"] intValue])
		{
			[missingParams appendFormat: @"%@\n", atomArray];
			[unknownInteractions addIndex: rowCount];
		} 
		
		[atomArray removeAllObjects];
		rowCount++;
	}

	if([missingParams length] > 0)
	{
		[failString appendFormat: @"\nUnable to find parameters for the following %@ interactions.\n",
			interactionType];
		[failString appendString: missingParams];
		
		if([interactionType isEqual: @"Bond"] || [interactionType isEqual: @"Angle"])
		{
			[[NSException exceptionWithName: @"ULBuildException"
				reason: [NSString stringWithFormat: @"Missing %@ parameters.", interactionType]
				userInfo: [NSDictionary dictionaryWithObject: failString
						forKey: @"ULBuildExceptionDetailedDescriptionKey"]] 
				raise];
		}
		else	
		{
			[[topology valueForKey:@"Matrix"] removeRowsWithIndexes: unknownInteractions];
			[errorString appendString: failString];
			[buildString appendFormat: @"\t\tRemoved %d %@ interactions.\nSee errors.\n", 
				[unknownInteractions count],
				interactionType];
		}
	}

	//update the number of columns
	
	columns = [[topology valueForKeyPath:@"Matrix.Columns"] intValue];
	columns += [[[[class children] objectAtIndex: 0] parameters] count];
	columns += [[[[class children] objectAtIndex: 0] constraints] count];
	[topology setValue: [NSNumber numberWithInt:columns] 
		forKeyPath:@"Matrix.Columns"];
}

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

Interaction Building

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

- (id) _buildBondsForAtoms: (NSMutableArray*) atomNames withBondedAtoms: (NSMutableArray*) bondedAtomsList
{
	int i;
	NSEnumerator* bondedAtomsListEnum, *bondedAtomsEnum;
	NSMutableDictionary* interaction;
	NSMutableArray* row;
	id bondedAtoms, atomIndex;
	ULMatrix* bondMatrix;

	bondMatrix = [[ULMatrix alloc] initWithRows: 0 columns: 0];
	bondedAtomsListEnum = [bondedAtomsList objectEnumerator];

	i = 0;
	while(bondedAtoms = [bondedAtomsListEnum nextObject])
	{
		bondedAtomsEnum = [bondedAtoms objectEnumerator];
		while(atomIndex = [bondedAtomsEnum nextObject])
			if([atomIndex intValue] > i)
			{
				row = [NSMutableArray arrayWithCapacity:2];
				[row addObject: [NSNumber numberWithInt: i]];
				[row addObject: atomIndex];
				[bondMatrix extendMatrixWithRow: row];
			}
		i++;
	}

	interaction = [NSMutableDictionary dictionaryWithCapacity: 1];
	[interaction setValue: @"HarmonicBond" forKey: @"InteractionType"];
	[interaction setValue: [NSNumber numberWithInt: 2] forKey: @"ElementsPerInteraction"];
	[interaction setValue: bondMatrix forKey: @"Matrix"];
	[self _findParametersForInteractions: interaction ofAtoms: atomNames];

	GSPrintf(buildOutput, @"There are %d bonded interactions\n", [bondMatrix Rows]);
	[buildString appendFormat: @"\t\tThere are %d bonds\n", [bondMatrix Rows]];
	return interaction;
}

- (id) _buildAnglesForAtoms: (NSMutableArray*) atomNames withBondedAtoms: (NSMutableArray*) bondedAtomsList
{
	int i, j, atom;
	NSEnumerator *bondedAtomsListEnum;
	NSMutableArray *angle;
	NSMutableDictionary* interaction;
	ULMatrix *angleMatrix;
	id bondedAtoms; 

	angleMatrix = [[ULMatrix alloc] initWithRows:0 columns: 0];
	bondedAtomsListEnum = [bondedAtomsList objectEnumerator];

	atom = 0;	
	while(bondedAtoms = [bondedAtomsListEnum nextObject])
	{
		if([bondedAtoms count] > 1)
			for(i=0; i<[bondedAtoms count] - 1; i++)
				for(j=i+1; j < [bondedAtoms count]; j++)	
				{
					angle = [NSMutableArray arrayWithCapacity: 3];
					[angle addObject: [bondedAtoms objectAtIndex: i]];
					[angle addObject: [NSNumber numberWithInt: atom]];
					[angle addObject: [bondedAtoms objectAtIndex: j]];
					[angleMatrix extendMatrixWithRow: angle];
				}
		atom++;
	}
	
	interaction = [NSMutableDictionary dictionaryWithCapacity: 1];
	[interaction setValue: @"HarmonicAngle" forKey: @"InteractionType"];
	[interaction setValue: [NSNumber numberWithInt: 3] forKey: @"ElementsPerInteraction"];
	[interaction setValue: angleMatrix forKey: @"Matrix"];
	[self _findParametersForInteractions: interaction ofAtoms: atomNames];

	GSPrintf(buildOutput,@"There are %d angle interactions\n", [angleMatrix Rows]);
	[buildString appendFormat: @"\t\tThere are %d angle interactions\n", [angleMatrix Rows]];
	return interaction;
}


- (id) _buildTorsionsForAtoms: (NSMutableArray*) atomNames 
		withBondedAtoms: (NSMutableArray*) bondedAtomsList 
		bonds: (ULMatrix*) bondMatrix
{
	int atomOneIndex, atomTwoIndex, connections1, connections2;
	NSEnumerator* bondEnum, *arrayTwoEnum, *arrayOneEnum;
	ULMatrix* torsionMatrix;
	NSMutableDictionary* interaction;
	NSMutableArray* torsion;
	id firstAtomIndex, lastAtomIndex, bond;

	bondEnum = [[bondMatrix matrixRows] objectEnumerator];
	torsionMatrix = [[ULMatrix alloc] initWithRows: 0 columns: 0];	

	//now use the bond list to build the torsions
	//each bond is a possible torsion center

	while(bond = [bondEnum nextObject])
	{
		atomOneIndex = [[bond objectAtIndex:0] intValue];
		atomTwoIndex = [[bond objectAtIndex:1] intValue];
		connections1 = [[bondedAtomsList objectAtIndex: atomOneIndex] count];
		connections2 = [[bondedAtomsList objectAtIndex: atomTwoIndex] count];

		if(connections1 > 1 && connections2 > 1)
		{
			arrayOneEnum = [[bondedAtomsList objectAtIndex: atomOneIndex] objectEnumerator];
			while(firstAtomIndex = [arrayOneEnum nextObject])
				if([firstAtomIndex intValue] != atomTwoIndex)
				{ 
					arrayTwoEnum = [[bondedAtomsList objectAtIndex: atomTwoIndex] 
										objectEnumerator];
					while(lastAtomIndex = [arrayTwoEnum nextObject])
						if([lastAtomIndex intValue] != atomOneIndex)
						{
							torsion = [NSMutableArray arrayWithCapacity: 4];
							[torsion addObject: firstAtomIndex];
							[torsion addObject: [bond objectAtIndex: 0]];
							[torsion addObject: [bond objectAtIndex: 1]];
							[torsion addObject: lastAtomIndex];	
							[torsionMatrix extendMatrixWithRow: torsion];
						}
				}
		}
	}
	
	interaction = [NSMutableDictionary dictionaryWithCapacity: 1];
	[interaction setValue: @"FourierTorsion" forKey: @"InteractionType"];
	[interaction setValue: [NSNumber numberWithInt: 4] forKey: @"ElementsPerInteraction"];
	[interaction setValue: torsionMatrix forKey: @"Matrix"];
	[self _findParametersForInteractions: interaction ofAtoms: atomNames];

	GSPrintf(buildOutput, @"There are %d torsion interactions\n", [torsionMatrix Rows]);
	[buildString appendFormat: @"\t\tThere are %d proper torsion interactions\n", [torsionMatrix Rows]];
	return interaction;
}

/*
Improper Torsions Note:

The itor interactions have the center as the third atom.
The other atoms are in alphabetical order (AMBER convention applied to Enzymix).
If we dont find parameters for the itor then it is removed
*/

- (id) _buildImproperTorsionsForAtoms: (NSMutableArray*) atomNames 
		withBondedAtoms: (NSMutableArray*) bondedAtomsList 
		bonds: (ULMatrix*) bondMatrix
{
	int i, j;
	id bondedAtoms;
	ULMatrix* improperMatrix;
	NSMutableDictionary* interaction;
	NSMutableArray* itor;

	improperMatrix = [ULMatrix new];	

	//The possible improper torsions are atoms that have three bonds
	//So we simply search for each of these in bonded atoms list

	for(i=0; i<[bondedAtomsList count]; i++)
	{
		bondedAtoms = [bondedAtomsList objectAtIndex: i];
		if([bondedAtoms count] == 3)
		{
			itor = [NSMutableArray array];

			//do we need to define a particular order!?

			[itor addObject: [bondedAtoms objectAtIndex: 0]];
			[itor addObject: [bondedAtoms objectAtIndex: 1]];
			[itor addObject: [bondedAtoms objectAtIndex: 2]];
			[itor sortUsingFunction: sortIndexesByNames 
				context: (void*)atomNames];
			[itor insertObject: [NSNumber numberWithInt: i]
				atIndex: 2];
			[improperMatrix extendMatrixWithRow: itor]; 
		}
	}
		
	interaction = [NSMutableDictionary dictionaryWithCapacity: 1];
	[interaction setValue: @"HarmonicImproperTorsion"
		forKey: @"InteractionType"];
	[interaction setValue: [NSNumber numberWithInt: 4] 
		forKey: @"ElementsPerInteraction"];
	[interaction setValue: improperMatrix
		forKey: @"Matrix"];
	[self _findParametersForInteractions: interaction 
		ofAtoms: atomNames];

	GSPrintf(buildOutput, @"There are %d improper torsion interactions\n", [improperMatrix Rows]);
	[buildString appendFormat: @"\t\tThere are %d improper torsion interactions\n",
		 [improperMatrix Rows]];

	return interaction;
}

- (id) _buildVDWForAtoms: (NSMutableArray*) atomNames withBondedAtoms: (NSMutableArray*) bondedAtomsList
{
	int i;
	ULMatrix* vdwMatrix;
	NSMutableDictionary* interaction;
	NSMutableArray* row;

	vdwMatrix = [[ULMatrix alloc] initWithRows: 0 columns: 0];

	for(i=0; i<[atomNames count]; i++)
	{
		row = [NSMutableArray arrayWithCapacity: 1];
		[row addObject: [NSNumber numberWithInt: i]];
		[vdwMatrix extendMatrixWithRow: row];
	}

	interaction = [NSMutableDictionary dictionaryWithCapacity: 1];
	[interaction setValue: @"TypeOneVDWInteraction" forKey: @"InteractionType"];
	[interaction setValue: vdwMatrix forKey: @"Matrix"];
	[interaction setValue: [NSNumber numberWithInt: 1] forKey: @"ElementsPerInteraction"];
	[self _findParametersForInteractions: interaction ofAtoms: atomNames];

	return interaction;
}

- (id) _findMassesForAtoms: (NSMutableArray*) atomNames
{
	int i;
	ULMatrix* massMatrix;
	NSMutableDictionary* interaction;
	NSMutableArray* row;

	massMatrix = [[ULMatrix alloc] initWithRows: 0 columns: 0];

	for(i=0; i<[atomNames count]; i++)
	{
		row = [NSMutableArray arrayWithCapacity: 1];
		[row addObject: [NSNumber numberWithInt: i]];
		[massMatrix extendMatrixWithRow: row];
	}

	interaction = [NSMutableDictionary dictionaryWithCapacity: 1];
	[interaction setValue: @"Mass" forKey: @"InteractionType"];
	[interaction setValue: massMatrix forKey: @"Matrix"];
	[interaction setValue: [NSNumber numberWithInt: 1] forKey: @"ElementsPerInteraction"];
	[self _findParametersForInteractions: interaction ofAtoms: atomNames];

	return interaction;
}

- (id) _buildNonBondedForAtoms: (NSMutableArray*) atomNames 
		bondedInteractions: (NSMutableDictionary*) bondedInteractions
		atomsPerResidue: (NSMutableArray*) atomsPerResidue
{
	int i, j, elementsPerInteraction;
	int noAtoms, noResidues, index, residueStart, residueEnd;
	NSMutableArray* nonbonded; 
	NSMutableIndexSet* indexes, *topIndexes, *interactionIndexes;
	NSEnumerator *interactionEnum;
	NSRange indexRange;
	id topology, matrix, interaction;

	noAtoms = [atomNames count];
	noResidues = [atomsPerResidue count];
	nonbonded = [NSMutableArray arrayWithCapacity: 1];
	
	for(i=0; i<noAtoms-1; i++)
	{
		indexRange.location = i+1;
		indexRange.length = noAtoms - indexRange.location;
		indexes = [NSMutableIndexSet indexSetWithIndexesInRange: indexRange];
		[nonbonded addObject: indexes];
	}

	residueEnd = 0;
	interactionIndexes = [NSMutableIndexSet indexSet];

	index = 0;
	for(i=0; i<[nonbonded count]; i++)
		index += [[nonbonded objectAtIndex:i] count];

	NSDebugLLog(@"ULInteractionsBuilder", 
		@"There are %d nonbonded interactions before removal", 
		index);

	for(i=0; i<noResidues; i++)
	{	

		residueStart = residueEnd;
		residueEnd += [[atomsPerResidue objectAtIndex: i] intValue];

		//the last atom has no nonbonded interactions (newtons third law)
		//so we shouldnt iterate on it

		if(residueEnd == noAtoms)
			residueEnd = residueEnd - 1;

		interactionEnum = [[bondedInteractions allValues] objectEnumerator];
		
		while(topology = [interactionEnum nextObject])
		{
			if([[topology valueForKey:@"InteractionType"] isEqual: @"VDW"])
				continue;			

			topIndexes =  [[topology valueForKey:@"ResidueInteractions"] 
						objectAtIndex: i];
			elementsPerInteraction = [[topology valueForKey:@"ElementsPerInteraction"] intValue];
			matrix = [[topology valueForKey:@"Matrix"] matrixRows];
					
			//for each of the interactions in this residue go through
			//all the atoms in the residue. If they are invovled in the interaction
			//remove them from the nonbonded index set
			
			index = [topIndexes firstIndex];
			while(index != NSNotFound)
			{
				interaction = [matrix objectAtIndex: index];
				for(j=0; j<elementsPerInteraction; j++)
					[interactionIndexes addIndex: [[interaction objectAtIndex: j] intValue]];

				for(j=residueStart; j<residueEnd; j++)
					if([interactionIndexes containsIndex: j])
						[[nonbonded objectAtIndex: j] removeIndexes: interactionIndexes];
				
				index = [topIndexes indexGreaterThanIndex: index];
				[interactionIndexes removeAllIndexes];
			}
		}
	}

	index = 0;
	for(i=0; i<[nonbonded count]; i++)
		index += [[nonbonded objectAtIndex:i] count];

	GSPrintf(buildOutput, @"There are %d nonbonded interactions\n", index);
	[buildString appendFormat: @"\t\tThere are %d nonbonded interactions\n", index];

	return nonbonded;
}

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

Interactions Per Residue

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

- (id) _residueIndexes: (NSMutableArray*) atomsPerResidue
{
	NSIndexSet *indexSet;
	NSRange indexRange;
	NSEnumerator* residueEnum;
	NSMutableArray* residueIndexes;
	id number;

	residueIndexes = [NSMutableArray arrayWithCapacity:1];
	residueEnum = [atomsPerResidue objectEnumerator];
	indexRange.location = 0;
	while(number = [residueEnum nextObject])
	{
		indexRange.length = [number intValue];	
		indexSet = [NSIndexSet indexSetWithIndexesInRange: indexRange];
		[residueIndexes addObject: indexSet];
		indexRange.location += indexRange.length;
	}	

	return residueIndexes;
}


- (id) _subsetOfInteractions: (NSMutableDictionary*) interaction
	 withIndexesInRange: (NSRange) indexRange 
	 startAt: (int) startIndex
	 endAt: (int) endIndex
{
	int i, j, elementsPerInteraction;
	NSArray* interactionRows;
	NSMutableIndexSet* searchSet, *resultSet;
	id row, index, end;

	interactionRows = [[interaction valueForKey:@"Matrix"] matrixRows];
	elementsPerInteraction = [[interaction valueForKey:@"ElementsPerInteraction"] intValue];
	searchSet = [NSIndexSet indexSetWithIndexesInRange: indexRange];
	resultSet = [NSMutableIndexSet indexSet];
	end = [NSNumber numberWithInt: endIndex +1];	

	for(j=startIndex; j<[interactionRows count]; j++)
	{
		row = [interactionRows objectAtIndex: j];
		if([row containsObject: end])
			break;

		for(i=0; i<elementsPerInteraction; i++)
		{
			index = [row objectAtIndex: i];
			if([searchSet containsIndex: [index intValue]])
			{
				[resultSet addIndex: j];
				break;
			}
		}
	}

	return resultSet;
}

- (id) _interactionsPerResidue: (NSMutableDictionary*) interaction residueIndexes: (NSArray*) residueIndexes
{
	int startIndex, endIndex, i, noResidues;
	NSRange indexRange;
	NSMutableArray* subsetIndexes;	//array of IndexSets - one for each residue
	NSMutableArray* interactionsPerResidue;
	id indexSet;

	subsetIndexes = [NSMutableArray arrayWithCapacity: 1];
	interactionsPerResidue = [NSMutableArray arrayWithCapacity: 1];
	startIndex = endIndex = 0;
	noResidues = [residueIndexes count];

	for(i=0; i< noResidues; i++)
	{
		indexRange.location = [[residueIndexes objectAtIndex: i] firstIndex];
		indexRange.length = [[residueIndexes objectAtIndex: i] count];

		if(i!= noResidues - 1)
			endIndex = [[residueIndexes objectAtIndex: i +1] lastIndex];
		else
			endIndex = [[residueIndexes lastObject] lastIndex];
		
		indexSet = [self _subsetOfInteractions: interaction 
					withIndexesInRange: indexRange 
					startAt: startIndex
					endAt: endIndex];

		[subsetIndexes addObject: indexSet];
		[interactionsPerResidue addObject: 
			[NSNumber numberWithInt: [indexSet count]]];
		
		if([indexSet count] != 0)
			startIndex = [indexSet firstIndex];
	}

	[interaction setValue: subsetIndexes forKey: @"ResidueInteractions"];
	[interaction setValue: interactionsPerResidue forKey: @"InteractionsPerResidue"];
	NSDebugLLog(@"ULInteractionsBuilder",
		@"%@\n%@", 
		[interaction objectForKey: @"InteractionType"], 
		interactionsPerResidue);
}

- (void) _setBuildError: (NSError**) buildError
{
	NSMutableDictionary* userInfo;

	[errorString insertString: @" and have been omitted.\n" atIndex: 0];
	[errorString insertString: @"The following interactions were missing paramters\n"
		atIndex: 0];

	userInfo = [NSMutableDictionary dictionary];
	[userInfo setObject: @"Errors while retrieving parameters for system.\n"
		forKey: @"NSLocalizedDescriptionKey"];
	[userInfo setObject: errorString
		forKey: @"ULBuildErrorDetailedDesciptionKey"];
	[userInfo setObject: @"Depending on the force field these omissions may or may not be critical.\n"
		forKey: @"ULBuildErrorRecoverySuggestionKey"];
	
	*buildError = [NSError errorWithDomain: @"ULBuildErrorDomain"
				code: 4
				userInfo: userInfo];
}


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

Public Methods

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

- (id) initWithParameterLibrary: (NSString*) libraryName
{
	NSString* libraryPath;

	libraryPath = [[[NSBundle mainBundle] resourcePath]
				stringByAppendingPathComponent: @"EnzymixParameters.ffml"];

	NSDebugLLog(@"ULInteractionsBuilder", @"Creating document tree for parmLib");
	parameterLibrary = [[ULParameterTree alloc] documentTreeForXMLFile: libraryPath];
	NSDebugLLog(@"ULInteractionsBuilder", @"Complete");

	return self;
}

- (void) buildInteractionsForSystem: (ULSystem*) system
		error: (NSError**) buildError
		userInfo: (NSString**) buildInfo
{
	NSMutableDictionary* interactions, *topology, *nonbonded;
	NSMutableArray* libraryNameList;
	NSMutableArray* bondedAtoms;
	NSMutableArray* residueIndexes;
	id interaction, nonbondedInteractions, masses, path;
	
	path = [[NSUserDefaults standardUserDefaults] stringForKey: @"BuildOutput"];
	buildOutput = fopen([path cString], "a");

	[buildString release];
	buildString = [[NSMutableString stringWithCapacity: 1] retain];
	*buildInfo = buildString;
	errorString = [NSMutableString stringWithCapacity: 1];

	interactions = [NSMutableDictionary dictionaryWithCapacity:1];
	nonbonded = [NSMutableDictionary dictionaryWithCapacity:1];
	libraryNameList = [system valueForKeyPath: @"Configuration.LibraryNames"];
	bondedAtoms = [system valueForKeyPath:@"Configuration.BondedAtoms"];
	topology = [NSMutableDictionary dictionaryWithCapacity:1];

	NSDebugLLog(@"ULInteractionsBuilder", @"%@", [system valueForKeyPath: @"Configuration.AtomsPerResidue"]);

	residueIndexes = [self _residueIndexes: [system valueForKeyPath: @"Configuration.AtomsPerResidue"]];

	[buildString appendString: @"\nBuilding interaction lists\n"];

	/*
   	 * Build the bonded interactions first
	 */
	
	[buildString appendString: @"\tHarmonicBonds:\n"];
	interaction = [self _buildBondsForAtoms: libraryNameList withBondedAtoms: bondedAtoms];
	[interactions setObject: interaction forKey: [interaction valueForKey: @"InteractionType"]];
	[self _interactionsPerResidue: interaction residueIndexes: residueIndexes];
	
	[buildString appendString: @"\tHarmonicAngles:\n"];
	interaction = [self _buildAnglesForAtoms: libraryNameList withBondedAtoms: bondedAtoms];
	[interactions setObject: interaction forKey: [interaction valueForKey: @"InteractionType"]];
	[self _interactionsPerResidue: interaction residueIndexes: residueIndexes];
	
	[buildString appendString: @"\tFourierTorsions:\n"];
	interaction = [self _buildTorsionsForAtoms: libraryNameList 	
			withBondedAtoms: bondedAtoms 
			bonds: [interactions valueForKeyPath: @"HarmonicBond.Matrix"]];
	[interactions setObject: interaction forKey: [interaction valueForKey: @"InteractionType"]];
	[self _interactionsPerResidue: interaction residueIndexes: residueIndexes];
	
	//testing improper torsions

	[buildString appendString: @"\tHarmonicImproperTorsions:\n"];
	interaction = [self _buildImproperTorsionsForAtoms: libraryNameList 	
			withBondedAtoms: bondedAtoms 
			bonds: [interactions valueForKeyPath: @"HarmonicBond.Matrix"]];
	[interactions setObject: interaction forKey: [interaction valueForKey: @"InteractionType"]];
	[self _interactionsPerResidue: interaction residueIndexes: residueIndexes];

	[topology setValue: interactions forKey: @"Bonded"];
	
	/*
	 * Now the nonbonded interactions
	 */

	[buildString appendString: @"\tNonbonded Interactions\n"];
	nonbondedInteractions = [self _buildNonBondedForAtoms: [system valueForKeyPath:@"Configuration.AtomNames"]
		bondedInteractions: [topology valueForKey:@"Bonded"]
		atomsPerResidue: [system valueForKeyPath:@"Configuration.AtomsPerResidue"]];	
	[nonbonded setValue: nonbondedInteractions forKey: @"Interactions"];
	
	interaction = [self _buildVDWForAtoms: libraryNameList withBondedAtoms: bondedAtoms];
	[self _interactionsPerResidue: interaction residueIndexes: residueIndexes];
	[nonbonded setValue: interaction forKey: @"VDWParameters"];

	[topology setValue: nonbonded forKey:@"Nonbonded"];

	//we have to retrieve the masses as if they were an interaction
	
	interaction = [self _findMassesForAtoms: libraryNameList];

	//now we will extract the masses as an array

	masses = [[interaction valueForKey:@"Matrix"] column: 1];
	[[system valueForKey:@"Configuration"] setValue: masses forKey: @"Masses"];
	[system setValue: topology forKey: @"Topology"];

	fclose(buildOutput);
	[buildString appendString: @"\nCompleted interactions build\n"];

	if([errorString length] != 0)
		[self _setBuildError: buildError];
}


@end
