/*
   Project: Adun

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

   Author: 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 "AdunKernel/AdListMemoryManagement.h"

#define BLOCKSIZE 524288

/**
Currently freedList elements are removed from the the main list and
added to a freed elements list. When getNewListElement is called
it returns an element from freeElements list first. However this 
causes the list to become nonlinear in memory i.e. adjacent elements in the
list are not adjacent in memory. This leads to list traversal performance
deteriorating as the simulation evolves, E.g. a listElelment that exists in the
first block may be referenced by the last element in the last block causing
a cache miss etc. 

Although one of the usual advantages of a linked list is that it doesnt care 
about the underlying memory structure in this case it can lead to improved
performance if we do. Therefore a bit of refactoring is in order -


1) Refactor the list allocation memory managment etc to a new class
2) Each list is associated with an instance of the class
3) The responsibility of the class is to create & destroy the list and
to add and remove elements from it.
4) freed list Elements are removed from the list but 
stored along with their previous and next memory partners (in an array).
5) getNewListElement returns an element already added to the list.
The exact location of this element is not determined unless it
is specified to be at the start or the end. This means we can reinsert
previously freed elements into the list at their original positions.
Only if their is no freedElements is the list extended
*/

@implementation AdNonBondedListHandler (AdListMemoryManagement)

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

Interaction List Memory Management 

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

- (ListElement*) createNewListBlock
{
	if(block_no == 50)
	{
		NSLog(@"Not Enough space in block array for a new block!!");
		exit(1);
	}

	block_no++;
	NSDebugLLog(@"AdListMemoryManagement", @"Creating New Block - There are now %d blocks", block_no);
	blocks[block_no-1] = (ListElement*)malloc(BLOCKSIZE*sizeof(ListElement));
	return blocks[block_no-1];
}

//This method returns newly created listelement structures that are
//retreived from previously malloced blocks of memory.
//if their are no free blocks a new block is assigned and ListElements 
//are returned from it

- (ListElement*) getNewListElement
{
	ListElement* el_p;

	if(block_no == 0)
	{	
		//if there are no blocks make one
		
		current_block = [self createNewListBlock];
		block_count = 0;
		el_p = &current_block[block_count];
		block_count++;
		return el_p;
	}
	else if(freedElementsCount > 0)
	{
		//else return a previously freed element
		//from the start of the freed list
		
		el_p = freedElementsList->next;
		AdUnsafeLinkedListRemove(el_p);
		freedElementsCount--;
		return el_p;
	}			
	else if(block_count != BLOCKSIZE)
	{
		//else return memory from the current block

		el_p = &current_block[block_count];
		block_count++;
		return el_p;
	}
	else if(current_block_no+1 != block_no)
	{
		//else move to the next available block
			
		current_block_no++;
		current_block = blocks[current_block_no];
		block_count = 0;
		el_p = &current_block[block_count];
		block_count++;
		return el_p;
	}	
	else
	{
		//else create a new block
		current_block = [self createNewListBlock];
		block_count = 0;
		el_p = &current_block[block_count];
		current_block_no++;
		block_count++;
		return el_p;
	}
}

/**
Returns a previously freed list element. This
element is already in the LinkedList but has no
bond associated with it. Objects that use this method
should simple set a valid bond value. If there are no
freed elements left this method returns NULL
**/
/*
- (ListElement*) getFreedListElement
{
}*/

- (void) freeListElement: (ListElement*) list_p
{
	//wipe the list Elements data and add its addreess
	//to the freed element list. We can then return
	//from this list first when an element structure
	//is requested to avoid thrasing memory

	list_p->bond[0] = 0;
	list_p->bond[1] = 0;
	list_p->length = 0;
	AdUnsafeLinkedListAdd(list_p, freeElementsEnd, 0);	
	freedElementsCount++;
}

- (void) initialiseListMemory
{
	block_count = 0;
	current_block_no = 0;
	current_block = blocks[0];
	freedElementsList = (ListElement*)malloc(sizeof(ListElement));
	freeElementsEnd = AdLinkedListCreate(freedElementsList);
	freedElementsCount = 0;
}

- (void) clearListMemory
{
	int i;
	
	//free all allocated blocks

	for(i=0; i<block_no; i++)
		free(blocks[i]);

	block_count = 0;
	current_block_no = 0;
	current_block = blocks[0];
	freedElementsCount = 0;
}

- (void) resetListMemory
{
	block_count = 0;
	current_block_no = 0;
	current_block = blocks[0];
	freedElementsCount = 0;
}

@end

