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

static id globalEnvironment;

@implementation AdEnvironment 

//At the moment AdEnvironment is singleton - however in the future
//we may want different environments for differents system etc.
+ (id) globalEnvironment
{
	return globalEnvironment;
}

- (id) initWithEnvironment: (id) object observe: (BOOL) value
{
	char *locale = "C";	
	NSMutableSet* debugLevels;
	NSMutableDictionary* registerDefs;
	
	if(globalEnvironment != nil)
		return globalEnvironment;

	//care since this means the AdEnvironment contains a ref to itself
	if(self = [super initWithEnvironment: object observe: value])
	{
		keyObserverDict = [NSMutableDictionary new];

		//first set the current locale to C

		setlocale(LC_ALL, locale);
		defaults = [NSUserDefaults standardUserDefaults];
		registerDefs = [NSMutableDictionary dictionaryWithCapacity: 1];
		[registerDefs setObject: @"Cell" forKey: @"ListManagementMethod"];
		[defaults registerDefaults: registerDefs];

		[NSProcessInfo processInfo];
		debugLevels = [[NSProcessInfo processInfo] debugSet];
		[debugLevels addObjectsFromArray: [[NSUserDefaults standardUserDefaults] objectForKey: @"DebugLevels"]];
		GSPrintf(stderr, @"The debug levels are %@\n", debugLevels);
		globalEnvironment = self;
	}

	return self;
}

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

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

- (void) dealloc
{
	[keyObserverDict release];
	[optionsDict release];
	globalEnvironment = nil;
	[super dealloc];
}

- (void) loadOptions
{
	ioManager = [AdIOManager appIOManager];
	optionsDict = [ioManager simulationOptions];
	[optionsDict retain];

	NSDebugLLog(@"AdEnvironment", @"Options are %@", optionsDict);
}	

- (NSDictionary*) options
{
	return [optionsDict copy];
}

- (void) setOptions: (NSDictionary*) dict
{
	//FIXME: validate the options

	optionsDict = [[dict mutableCopy] retain];
	
	//broadcast update to all observers
	//FIXME: implement
}

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

Observing

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

- (void) addObserver: (id) anObserver forKey: (NSString*) key
{
	NSMutableArray  *observingObjects;

	if(![anObserver conformsToProtocol: @protocol(AdEnvironmentObservation)])
		[NSException raise: NSInvalidArgumentException
			format: [NSString stringWithFormat: 
			@"%@ does not conform to AdEnvironmentObservation", anObserver]];

	//test for the keys existance using valueForKey
	//may be better to keep an internal array of all the environment keys
	
	[self valueForKey: key];

	if((observingObjects = [keyObserverDict objectForKey: key]) != nil)
		[observingObjects addObject: anObserver];
	else
	{
		observingObjects = [NSMutableArray arrayWithCapacity: 1];
		[keyObserverDict setObject: observingObjects forKey: key];
		[observingObjects addObject: anObserver];
	}
	NSDebugLLog(@"AdEnvironment", @"Registers %@ for %@.", anObserver, key);
	NSDebugLLog(@"AdEnvironment", @"Observed keys %@", keyObserverDict);
}

- (void) removeObserver: (id) anObserver forKey: (NSString*) key
{
	NSMutableArray  *observingObjects;

	[self valueForKey: key];

	if((observingObjects = [keyObserverDict objectForKey: key]) != nil)
		[observingObjects removeObject: anObserver];
}

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

Application Options

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

- (int) Seed
{
	return [[optionsDict valueForKeyPath: @"Application.General.Seed"] intValue];
}

- (NSString*) OutputPrefix
{
	return [optionsDict valueForKeyPath: @"Application.IO.OutputPrefix"];
}

- (int) EnergyWriteInterval
{
	return [[optionsDict valueForKeyPath: @"Application.IO.OutputIntervals.Energy"] intValue];
}

- (int) ConfigWriteInterval
{
	return [[optionsDict valueForKeyPath: @"Application.IO.OutputIntervals.Configuration"] intValue];
}

- (int) LogInterval
{
	return [[optionsDict valueForKeyPath: @"Application.IO.OutputIntervals.Status"] intValue];
}

- (int) EnergyDumpInterval
{
	return [[optionsDict valueForKeyPath: @"Application.IO.OutputIntervals.EnergyDump"] intValue];
}


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

Simulation Options

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

- (double) TimeStep
{
	return [[optionsDict valueForKeyPath: @"Simulation.General.TimeStep"] doubleValue];
}

- (int) NumberConfigurations
{
	return [[optionsDict valueForKeyPath: @"Simulation.General.NumberOfSteps"] intValue];
}

- (int) TargetTemperature
{
	return [[optionsDict valueForKeyPath: @"Simulation.General.TargetTemperature"] intValue];

}

- (NSString*) SimulationType
{
	return [[optionsDict valueForKeyPath: @"Simulation.Dynamics.Selection"] objectAtIndex: 0];	
}

- (int) CheckFPErrorInterval
{
	return [[optionsDict valueForKeyPath: @"Simulation.General.CheckFPError"] intValue];
}

- (double) MaximumSpaceSize
{
	id value;

	value = [optionsDict valueForKeyPath: @"Simulation.General.MaximumSpaceSize"];
	if(value == nil)
		return 0;
	else
		return [value doubleValue];
}

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

Solvation Options

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

- (BOOL) ExplicitSolvent
{
	if([[[optionsDict valueForKeyPath: @"Solvation.Selection"] objectAtIndex: 0]  isEqual: @"Explicit"])
		return YES;
	else	
		return NO;
}

- (BOOL) LangevinDipoles
{
	if([[[optionsDict valueForKeyPath: @"Solvation.Selection"] objectAtIndex: 0] isEqual: @"LangevinDipoles"])
		return YES;
	else	
		return NO;
}

- (double) CouplingFactor
{
	return [[optionsDict valueForKeyPath: @"Solvation.Continuum.CouplingFactor"] doubleValue];
}

- (double) SolvationSphereRadius
{
	return [[optionsDict valueForKeyPath: @"Solvation.Explicit.SphereRadius"] doubleValue];
}

- (double) SolventDensity
{
	return [[optionsDict valueForKeyPath: @"Solvation.Explicit.SolventDensity"] doubleValue];
}

- (double) BoundarySize
{
	return [[optionsDict valueForKeyPath: @"Solvation.Explicit.BoundarySize"] doubleValue];
}

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

Electrostatic Options

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

/**
Also change to DielectricConstant to be more precise
**/

- (double) Permittivity
{
	return [[optionsDict valueForKeyPath: @"Electrostatic.General.RelativePermittivity"] doubleValue];
}

- (float) CutOff
{
	return [[optionsDict valueForKeyPath: @"Electrostatic.General.Cutoff"] floatValue];
}

- (int) UpdateInterval
{
	return [[optionsDict valueForKeyPath: @"Electrostatic.General.UpdateInterval"] intValue];
}

- (NSString*) ShortRangeInteractions
{
	return [[optionsDict valueForKeyPath: @"Electrostatic.ShortRange.Selection"] objectAtIndex: 0];
}

- (NSString*) ListManagementMethod
{
	return [defaults objectForKey: @"ListManagementMethod"];
}

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

Controller Options

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

- (NSString*) Controller
{
	return [optionsDict valueForKey: @"Controller"];
}

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

Misc

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

/**** Print Function - to be moved to AdIOManager ***/

- (void) printBanner: (NSString*) banner
{
	GSPrintf(stderr, @"********************%12@  ********************\n\n", banner);
	[banner release];
}

- (void) printSeperator
{
	GSPrintf(stderr, @"\n******************************************************\n\n", "");
}

@end

