/*
   Project: Adun

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

   Author: Michael Johnston

   Created: 2005-06-23 11:06:55 +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 "Components/SimpleListHandler.h"

@implementation SimpleListHandler

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

Initialisation

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

- (void) _useEnvironmentDefaults
{
	cutoff = 12;
}

- (id) initWithEnvironment: (id) object observe: (BOOL) value
{
	if(self = [super initWithEnvironment: object observe: value])
	{
		dependantsDict = [NSMutableDictionary dictionaryWithCapacity:4];
		[dependantsDict setValue: @"InteractionLists" forKey: @"Cutoff"];
		[dependantsDict retain];

		if(environment == nil)
			[self _useEnvironmentDefaults];
		else
		{
			[self synchroniseWithEnvironment];
			[self registerWithEnvironment];
		}
	}

	return self;
}

- (id) initWithEnvironment: (id) object
{
	return [self initWithEnvironment: object observe: YES];
}

- (id) init
{
	return [self initWithEnvironment: nil];
}

- (void) dealloc
{
	ListElement* list_p, *holder;

	list_p = in_p;
	while(list_p->next != NULL)	
	{		
		holder = list_p->next;
		free(list_p);
		list_p= holder;
	}
	free(list_p);		
	list_p = out_p;
	while(list_p->next != NULL)	
	{		
		holder = list_p->next;
		free(list_p);
		list_p= holder;
	}
	free(list_p);	
	[dependantsDict release];	
}

- (void) _checkListStatus
{
	ListElement* list_p, *holder;
	
	//if in_p is initalised a list exists

	if(in_p != NULL)
	{
		list_p = in_p;
		while(list_p->next != NULL)	
		{		
			holder = list_p->next;
			free(list_p);
			list_p= holder;
		}
		free(list_p);		
		list_p = out_p;
		while(list_p->next != NULL)	
		{		
			holder = list_p->next;
			free(list_p);
			list_p= holder;
		}
		free(list_p);		
	}
}	

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

Main methods

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

- (void) createList
{
	int i, j, k;
	int incount, outcount;
	int retVal, noAtoms;
	int* indexBuffer;
	NSIndexSet* indexSet;
	NSRange indexRange;
	ListElement *list_p;
	Vector3D seperation_s;
	
	//Check if there are any current lists
	//if there are free them

	[self _checkListStatus];

	//create two empty lists
	
	in_p =  (ListElement*)malloc(sizeof(ListElement));
	out_p = (ListElement*)malloc(sizeof(ListElement));
	endin_p = AdLinkedListCreate(in_p);
	endout_p = AdLinkedListCreate(out_p);
	indexBuffer = (int*)malloc(100*sizeof(int));
	noAtoms = [interactions count];	

	incount = outcount = 0;
	for(i=0; i < noAtoms; i++)
	{	
		indexSet = [interactions objectAtIndex: i];
		if([indexSet firstIndex] != NSNotFound)
		{
			indexRange.location = [indexSet firstIndex];
			indexRange.length = [indexSet lastIndex] - indexRange.location + 1;
			do
			{	
				retVal = [indexSet getIndexes: indexBuffer maxCount: 100 inIndexRange: &indexRange];
				for(k=0; k<retVal; k++)
				{
					//calculate distance	
				
					for(j=0; j<3; j++)
						seperation_s.vector[j] = coordinates->matrix[i][j] -
									 coordinates->matrix[indexBuffer[k]][j];

					//calculate the length of the seperation vector

					Ad3DVectorLength(&seperation_s);
					
					if(seperation_s.length < cutoff)
					{
						//add to inside list;

						list_p = (ListElement*)malloc(sizeof(ListElement));
						list_p->bond[0] = i;
						list_p->bond[1] = indexBuffer[k];
						list_p->params[0] = parameters->table[i][0]*parameters->table[indexBuffer[k]][0];
						list_p->params[1] = parameters->table[i][1]*parameters->table[indexBuffer[k]][1];
						list_p->length = seperation_s.length;
						AdSafeLinkedListAdd(list_p, endin_p, 0);
						incount++;
					}
					else
					{
						//add to outside list

						list_p = (ListElement*)malloc(sizeof(ListElement));
						list_p->bond[0] = i;
						list_p->bond[1] = indexBuffer[k];
						list_p->params[0] = parameters->table[i][0]*parameters->table[indexBuffer[k]][0];
						list_p->params[1] = parameters->table[i][1]*parameters->table[indexBuffer[k]][1];
						AdSafeLinkedListAdd(list_p, endout_p, 0);
						outcount++;			
					}
				}
			}
			while(retVal == 100); 
		}
	}	
		
	free(indexBuffer);
	numberOfInteractions = AdLinkedListCount(in_p) - 1;
	
	GSPrintf(stderr, @"Number of nonbonded interactions inside cuttoff = %d.\n", incount);
	GSPrintf(stderr, @"Number of nonbonded interactions outside cuttoff = %d.\n", outcount);
}

- (void) update
{
	int j;
	ListElement *holder, *list_p;
	Vector3D seperation_s;
	
	list_p = out_p->next;

	while(list_p->next != NULL)
	{
		//calculate seperation of current bond
		
		for(j=0; j<3; j++)
			seperation_s.vector[j] = coordinates->matrix[list_p->bond[0]][j] - coordinates->matrix[list_p->bond[1]][j];

		//calculate the length of the seperation vector

		Ad3DVectorLength(&seperation_s);

		if(seperation_s.length < cutoff)
		{
			//hold the address of the next element
			
			holder = list_p;
			list_p = list_p->next;
			
			//remove the current element from the list
			
			AdUnsafeLinkedListRemove(holder);
			holder->length = - 1;
			AdUnsafeLinkedListAdd(holder, endin_p, 0);
		}
		else
			list_p = list_p->next;
	}

	//check through the inside list for any interactions that are now outside the cutoff
	//move to the first real element of inside

	list_p = in_p->next;
	
	while(list_p->length > 0 && list_p->next != NULL )
	{
		if(list_p->length > cutoff)
		{
			//hold the address of the next element
			
			holder = list_p;
			list_p = list_p->next;
			
			//remove the current element from the list
			
			AdUnsafeLinkedListRemove(holder);
			AdUnsafeLinkedListAdd(holder, endout_p, 0);
		}
		else
		{
			list_p = list_p->next;
		}
	}

	numberOfInteractions = AdLinkedListCount(in_p) - 1;
}

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

Protocols 

*****************/
/*
 * Environment observation
 */

- (void) updateForKey: (NSString*) key value: (id) value object: (id) object
{
	if([key isEqual: @"Cutoff"])
	{
		cutoff = [value floatValue];
	}
}

- (void) registerWithEnvironment
{
	if(observesEnvironment)
		[environment addObserver: self forKey: @"CutOff"];
}

- (void) deregisterWithEnvironment
{
	[environment removeObserver: self forKey: @"CutOff"];
}

- (void) synchroniseWithEnvironment
{
	cutoff = [[environment valueForKey: @"CutOff"] floatValue];
}

- (void) setEnvironment: (id) object
{
	[self deregisterWithEnvironment];
	object = environment;
	[self registerWithEnvironment];
}

- (id) initWithCoder: (NSCoder*) decoder
{
	self = [super initWithCoder: decoder];
	if([decoder allowsKeyedCoding])
	{
		memoryManager = [AdMemoryManager appMemoryManager];
		environment = [AdEnvironment globalEnvironment];
		if(environment != nil)
		{
			[self synchroniseWithEnvironment];
			[self registerWithEnvironment];
		}
		else
			[self _useEnvironmentDefaults];
	}
	else
		[NSException raise: NSInvalidArgumentException
			format: @"%@ class does not support non keyed coding", [self class]];

	return self;
}

- (void) encodeWithCoder: (NSCoder*) encoder
{
	[super encodeWithCoder: encoder];
}

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

Accessors

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

- (void) setCoordinates: (NSValue*) aValue
{
	coordinates = [aValue pointerValue];
}

- (void) setNonbondedTopology: (NSArray*) anArray
{
	interactions = anArray;
}

- (void) setParameters: (NSValue*) aValue
{
	parameters = [aValue pointerValue];
}

- (void) setCutoff: (int) aValue
{
	cutoff = aValue;
}

- (NSValue*) nonbondedInteractions
{
	return [NSValue valueWithPointer: in_p];
}

- (int) numberNonbondedInteractions
{
	return numberOfInteractions;
}

//Dependant setter

- (void) setInteractionLists
{
	[self update];
}

@end
