/*
   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/AdunMemoryManager.h"

static id memoryManager;

#define MEM_CON 1048576

@implementation AdMemoryManager

+ (id) appMemoryManager
{
	if(memoryManager == nil)
		memoryManager = [[AdMemoryManager alloc] init];

	return memoryManager;
}

- (id) initWithEnvironment: (id) object observe: (BOOL) value
{
	if(self = [super initWithEnvironment: object observe: value])
	{
		MEMORY_STATS=[[NSUserDefaults standardUserDefaults] boolForKey: @"OutputMemoryStatistics"];
		memoryManager = self;
	}		

	return self;
}

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

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

}

- (void) dealloc
{

}

- (void*) allocateArrayOfSize: (int) size
{
	void* array;
	NSError* error;
	NSMutableDictionary* errorDict;

	if(MEMORY_STATS==YES)
	{
		mem_struct = mallinfo();
		GSPrintf(stderr, @"Before Array Alloc - Arena : %lf MB. Hblks : %lf MB. Uordblocks %lf MB. Fordblocks %d\n", 
			(float)mem_struct.arena/(float)MEM_CON,
		 	(float)mem_struct.hblkhd/(float)MEM_CON, 
			(float)mem_struct.uordblks/(float)MEM_CON, 
			mem_struct.fordblks); 
	}

	//The return value of malloc(0) is implementation dependant
	//It can return NULL in which case it is indistinguisable from
	//the results of mallocing an array that will exhaust virtual memory.
	//We want to avoid this since we dont want malloc(0) to trigger the
	//array == NULL error below. 
	//malloc(0) can also return a special pointer. This is problematic
	//since we dont know what this special pointer is and attempting
	//to free it later could cause a segmentation fault. Hence if
	//malloc(0) is attempted with immediatly return NULL. In this
	//way when freeing an array or matrix that was created using 0 
	//we can recognise and handle it. 

	//Unfortunatly something is trying to use the NULL pointer
	//returned here and causing the program to crash. Until this
	//is corrected we cant use this method.

	/*if(size == 0)
	{
		NSLog(@"Attempting to allocate a zero size array");
		return NULL;
	}
	else*/
		array = malloc(size);
	
	if(array == NULL)
	{
		NSWarnLog(@"Attempt to allocate array of size %d will exhaust virtual memory!\n", size);
		
		errorDict = [NSMutableDictionary new];
		[errorDict setObject: [NSString stringWithFormat: 
			@"Simulator attempted to allocate an array that would have exhausted virtual memory (size %d bytes).\n"
				, size]
			forKey: NSLocalizedDescriptionKey];
		[errorDict setObject: @"This is probably a symptom of the simulation exploding due to excessive forces.\n"
			forKey: @"AdDetailedDescriptionKey"];
		[errorDict setObject: @"You may need to relax the system before performing a full simulation.\nSee the User Guide for\
 details on how to do this (diana.imim.es/Adun).\n"
			forKey: @"NSRecoverySuggestionKey"];
		[errorDict setObject: NSInternalInconsistencyException
			forKey: NSUnderlyingErrorKey];

		error = [NSError errorWithDomain: @"AdKernelErrorDomain"
				code: 2
				userInfo: errorDict];

		[[NSException exceptionWithName: NSInternalInconsistencyException
			reason: [NSString stringWithFormat:
			@"Attempted to allocate an array that would have exhausted virtual memory (size %d bytes).", size]
			userInfo: [NSDictionary dictionaryWithObject: error
					forKey: @"AdKnownExceptionError"]] 
			raise];
	}
	
	memset(array, 0, size);
	
	if(MEMORY_STATS==YES)
	{
		mem_struct = mallinfo();
		GSPrintf(stderr, @"After Array Alloc (%d) - Arena : %lf MB. Hblks : %lf MB. Uordblocks %lf MB. Fordblocks %d\n\n", size, 
			(float)mem_struct.arena/(float)MEM_CON, 
			(float)mem_struct.hblkhd/(float)MEM_CON, 
			(float)mem_struct.uordblks/(float)MEM_CON, 
			mem_struct.fordblks); 
		fflush(stderr);
	}


	return array;
}

- (AdMatrix*) allocateMatrixWithRows: (int) no_rows withColumns: (int) no_columns
{
	int i, j;
	double *array;
	AdMatrix *matrix;

	if(MEMORY_STATS==YES)
	{
		mem_struct = mallinfo();
		GSPrintf(stderr, @"Before Matrix Alloc - Arena : %lf MB. Hblks : %lf MB. Uordblocks %lf MB. Fordblocks %d\n", 
			(float)mem_struct.arena/(float)MEM_CON, 
			(float)mem_struct.hblkhd/(float)MEM_CON, 
			(float)mem_struct.uordblks/(float)MEM_CON, 
			mem_struct.fordblks); 
	}

	matrix = (AdMatrix*)malloc(sizeof(AdMatrix));
	matrix->no_rows = no_rows;
	matrix->no_columns = no_columns;
	array = (double*)[self allocateArrayOfSize: no_rows*no_columns*sizeof(double)];
	matrix->matrix = (double**)[self allocateArrayOfSize: no_rows*sizeof(double*)];
	for(i=0, j=0; i < no_rows; i++, j = j + no_columns)
			matrix->matrix[i] = array + j;

	if(MEMORY_STATS==YES)
	{
		mem_struct = mallinfo();
		GSPrintf(stderr, @"After Matrix Alloc - Arena : %lf MB. Hblks : %lf MB. Uordblocks %lf MB. Fordblocks %d\n", 
			(float)mem_struct.arena/(float)MEM_CON, 
			(float)mem_struct.hblkhd/(float)MEM_CON, 
			(float)mem_struct.uordblks/(float)MEM_CON,
		 	mem_struct.fordblks); 
	}

	return matrix;
}

- (IntMatrix*) allocateIntMatrixWithRows: (int) no_rows withColumns: (int) no_columns
{
	int i, j;
	int *array;
	IntMatrix *matrix;

	if(MEMORY_STATS==YES)
	{
		mem_struct = mallinfo();
		GSPrintf(stderr, @"Before Matrix Alloc - Arena : %lf MB. Hblks : %lf MB. Uordblocks %lf MB. Fordblocks %d\n", 
			(float)mem_struct.arena/(float)MEM_CON, 
			(float)mem_struct.hblkhd/(float)MEM_CON, 
			(float)mem_struct.uordblks/(float)MEM_CON, 
			mem_struct.fordblks); 
	}

	matrix = (IntMatrix*)malloc(sizeof(IntMatrix));
	matrix->no_rows = no_rows;
	matrix->no_columns = no_columns;
	array = (int*)[self allocateArrayOfSize: no_rows*no_columns*sizeof(int)];
	matrix->matrix = (int**)[self allocateArrayOfSize: no_rows*sizeof(int*)];
	for(i=0, j=0; i < no_rows; i++, j = j + no_columns)
			matrix->matrix[i] = array + j;

	if(MEMORY_STATS==YES)
	{
		mem_struct = mallinfo();
		GSPrintf(stderr, @"After Matrix Alloc - Arena : %lf MB. Hblks : %lf MB. Uordblocks %lf MB. Fordblocks %d\n", 
			(float)mem_struct.arena/(float)MEM_CON, 
			(float)mem_struct.hblkhd/(float)MEM_CON, 
			(float)mem_struct.uordblks/(float)MEM_CON, 
			mem_struct.fordblks); 
	}

	return matrix;
}

- (InterTable*) allocateInterTableWithRows: (int) no_rows withColumns: (int) no_columns
{
	int i, j;
	double *array;
	InterTable *table;
	
	if(MEMORY_STATS==YES)
	{
		mem_struct = mallinfo();
		GSPrintf(stderr, @"Before Table Alloc - Arena : %lf MB. Hblks : %lf MB. Uordblocks %lf MB. Fordblocks %d\n", 
			(float)mem_struct.arena/(float)MEM_CON, 
			(float)mem_struct.hblkhd/(float)MEM_CON, 
			(float)mem_struct.uordblks/(float)MEM_CON,
			 mem_struct.fordblks); 
	}

	table = (InterTable*)malloc(sizeof(InterTable));
	table->no_interactions = no_rows;
	table->no_columns = no_columns;

	array = (double*)[self allocateArrayOfSize: no_rows*no_columns*sizeof(double)];
	table->table = (double**)[self allocateArrayOfSize: no_rows*sizeof(double*)];

	for(i=0, j=0; i < no_rows; i++, j = j + no_columns)
			table->table[i] = array + j;
	
	if(MEMORY_STATS==YES)
	{
		mem_struct = mallinfo();
		GSPrintf(stderr, @"After Table Alloc Arena : %lf MB. Hblks : %lf MB. Uordblocks %lf MB. Fordblocks %d\n", 
			(float)mem_struct.arena/(float)MEM_CON, 
			(float)mem_struct.hblkhd/(float)MEM_CON, 
			(float)mem_struct.uordblks/(float)MEM_CON, 
			mem_struct.fordblks); 
	}

	return table;
}

- (void) freeArray: (void*)array
{	
	if(MEMORY_STATS==YES)
	{
		mem_struct = mallinfo();
		GSPrintf(stderr, @"Before Array Free - Arena : %lf MB. Hblks : %lf MB. Uordblocks %lf MB. Fordblocks %d\n", 
			(float)mem_struct.arena/(float)MEM_CON, 
			(float)mem_struct.hblkhd/(float)MEM_CON, 
			(float)mem_struct.uordblks/(float)MEM_CON, 
			mem_struct.fordblks); 
	}

	free(array);

	if(MEMORY_STATS==YES)
	{
		mem_struct = mallinfo();
		GSPrintf(stderr, @"After Array Free  - Arena : %lf MB. Hblks : %lf MB. Uordblocks %lf MB. Fordblocks %d\n\n",  
			(float)mem_struct.arena/(float)MEM_CON, 
			(float)mem_struct.hblkhd/(float)MEM_CON, 
			(float)mem_struct.uordblks/(float)MEM_CON, 
			mem_struct.fordblks); 
	}
}

/** Do not use this method to free matrices not allocated by one of the
above methods **/

- (void) freeDoubleMatrix: (double**) matrix withRows: (int) no_rows 
{
	//matrices are allocated as arrays
	//with another array of indexes
	
	free(matrix[0]); 	//frees the number array	
	free(matrix);		//frees the index array	

	if(MEMORY_STATS==YES)
	{
		mem_struct = mallinfo();
		GSPrintf(stderr, @"Arena : %lf MB. Hblks : %lf MB. Uordblocks %lf MB. Fordblocks %d\n", 
			(float)mem_struct.arena/(float)MEM_CON, 
			(float)mem_struct.hblkhd/(float)MEM_CON, 
			(float)mem_struct.uordblks/(float)MEM_CON, 
			mem_struct.fordblks); 
	}

}

- (void) freeMatrix: (AdMatrix*) matrix 
{
	//matrices are allocated as arrays
	//with another array of indexes
	if(MEMORY_STATS==YES)
	{
		mem_struct = mallinfo();
		GSPrintf(stderr, @"Before Matrix free - Arena : %lf MB. Hblks : %lf MB. Uordblocks %lf MB. Fordblocks %d\n", 
			(float)mem_struct.arena/(float)MEM_CON, 
			(float)mem_struct.hblkhd/(float)MEM_CON, 
			(float)mem_struct.uordblks/(float)MEM_CON, 
			mem_struct.fordblks); 
	}

	if(matrix->no_rows != 0)
	{
		free(matrix->matrix[0]); //frees the number array	
		free(matrix->matrix);		 //frees the index array
	}
	free(matrix);
	
	if(MEMORY_STATS==YES)
	{
		mem_struct = mallinfo();
		GSPrintf(stderr, @"After Matrix free - Arena : %lf MB. Hblks : %lf MB. Uordblocks %lf MB. Fordblocks %d\n", 
			(float)mem_struct.arena/(float)MEM_CON, 
			(float)mem_struct.hblkhd/(float)MEM_CON, 
			(float)mem_struct.uordblks/(float)MEM_CON, 
			mem_struct.fordblks); 
	}
}

- (void) freeIntMatrix: (IntMatrix*) matrix 
{
	if(MEMORY_STATS==YES)
	{
		mem_struct = mallinfo();
		GSPrintf(stderr, @"Before Matrix free - Arena : %lf MB. Hblks : %lf MB. Uordblocks %lf MB. Fordblocks %d\n", 
			(float)mem_struct.arena/(float)MEM_CON, 
			(float)mem_struct.hblkhd/(float)MEM_CON, 
			(float)mem_struct.uordblks/(float)MEM_CON, 
			mem_struct.fordblks); 
	}

	if(matrix->no_rows != 0)
	{
		free(matrix->matrix[0]); 
		free(matrix->matrix);	
	}
	free(matrix);
	
	if(MEMORY_STATS==YES)
	{
		mem_struct = mallinfo();
		GSPrintf(stderr, @"After Matrix free - Arena : %lf MB. Hblks : %lf MB. Uordblocks %lf MB. Fordblocks %d\n", 
			(float)mem_struct.arena/(float)MEM_CON, 
			(float)mem_struct.hblkhd/(float)MEM_CON, 
			(float)mem_struct.uordblks/(float)MEM_CON, 
			mem_struct.fordblks); 
	}
}

- (void) freeInterTable: (InterTable*) matrix  
{
	//matrices are allocated as arrays
	//with another array of indexes
	
	if(MEMORY_STATS==YES)
	{
		mem_struct = mallinfo();
		GSPrintf(stderr, @"Before InterTable free - Arena : %lf MB. Hblks : %lf MB. Uordblocks %lf MB. Fordblocks %d\n", 
			(float)mem_struct.arena/(float)MEM_CON, 
			(float)mem_struct.hblkhd/(float)MEM_CON, 
			(float)mem_struct.uordblks/(float)MEM_CON, 
			mem_struct.fordblks); 
	}

	if(matrix->no_interactions != 0)
	{
		free(matrix->table[0]);
		free(matrix->table); 	//frees the number array	
	}

	free(matrix);
	
	if(MEMORY_STATS==YES)
	{
		mem_struct = mallinfo();
		GSPrintf(stderr, @"After Intertable free - Arena : %lf MB. Hblks : %lf MB. Uordblocks %lf MB. Fordblocks %d\n", 
			(float)mem_struct.arena/(float)MEM_CON, 
			(float)mem_struct.hblkhd/(float)MEM_CON, 
			(float)mem_struct.uordblks/(float)MEM_CON, 
			mem_struct.fordblks); 
	}
}

@end


