//
//  gravityView.m
//  gravity
//
//  Created by Mike Mintz on 12/30/05.
//  Copyright (c) 2005, __MyCompanyName__. All rights reserved.
//

#import "gravityView.h"
#import "Particle.h"

@implementation gravityView

static NSString * const MyModuleName = @"com.mikemintz.gravity";

+(BOOL)performGammaFade
{
	return NO;
}

- (id)initWithFrame:(NSRect)frame isPreview:(BOOL)isPreview
{
	defaults = [ScreenSaverDefaults defaultsForModuleWithName:MyModuleName];
	originalDefaults = [NSDictionary dictionaryWithObjectsAndKeys:
		@"15", @"nParticles",
		@"60", @"FPS",
		@"10", @"gravityCoefficient",
		@"200", @"electricCoefficient",
		@"10", @"maxSpeed",
		@"10", @"maxCharge",
		@"10", @"maxMass",
		@"0.03125", @"maxInitialSpeedFraction",
		@"500", @"particleDensity",
		@"0", @"trailLength",
		@"YES", @"fractionalCharge",
		@"NO", @"outlinedCircles",
		@"1.0", @"particleAlpha",
		@"NO", @"displayDynamicMass",
		@"Wrap", @"edges",
		@"Standard", @"relativistic",
		nil];
	[originalDefaults retain];
	[defaults registerDefaults:originalDefaults];
	[defaults synchronize];
	[self loadConfig];
	
	self = [super initWithFrame:frame isPreview:isPreview];
	if (self) {
		[self setAnimationTimeInterval:1.0/[defaults floatForKey: @"FPS"]];
	}
	return self;
}

- (void)startAnimation
{
	// borrowed from helios
    int mainScreen;
    int thisScreen;
	thisScreen = [[[[[self window] screen] deviceDescription] objectForKey:@"NSScreenNumber"] intValue];
	mainScreen = [[[[NSScreen mainScreen] deviceDescription] objectForKey:@"NSScreenNumber"] intValue];
	if (thisScreen == mainScreen) {
		[self initializeParticles];
		[super startAnimation];
	}
 }

- (void)stopAnimation
{
	[self releaseParticles];
    [super stopAnimation];
}

- (void)drawRect:(NSRect)rect
{
    [super drawRect:rect];
}

- (void)animateOneFrame
{
	int i, j;
	Particle *p;
	NSBezierPath *path;
	//if ([defaults integerForKey: @"trailLength"] != 0)
		[self blankView];
	for (i = 0; i < nParticles; i++) {
		p = [particles objectAtIndex: i];
		[p moveOneFrame];
		[p stayOnScreenWithRadius: [self radiusFromMass: [p mass]]];
		//if ([defaults integerForKey: @"trailLength"] == 0)
		//	[self blankPath: [dirtyPaths objectAtIndex: i]];
		path = [self drawParticle: p];
		[dirtyPaths replaceObjectAtIndex: i withObject: path];
	}
	for (i = 0; i < nParticles; i++) {
		p = [particles objectAtIndex: i];
		for (j = 0; j < nParticles; j++) {
			if (i != j) {
				[p getInfluencedBy: [particles objectAtIndex: j]];
			}
		}
	}
}

- (BOOL)hasConfigureSheet
{
    return YES;
}

- (NSWindow*)configureSheet
{
	if (!configSheet)
	{
		if (![NSBundle loadNibNamed:@"ConfigureSheet" owner:self]) 
		{
			NSLog( @"Failed to load configure sheet." );
			NSBeep();
		}
	}
	[nParticlesField setIntValue:[defaults integerForKey:@"nParticles"]];
	[FPSField setFloatValue:[defaults floatForKey:@"FPS"]];
	[gravityCoefficientField setFloatValue:[defaults floatForKey:@"gravityCoefficient"]];
	[electricCoefficientField setFloatValue:[defaults floatForKey:@"electricCoefficient"]];
	[maxSpeedField setFloatValue:[defaults floatForKey:@"maxSpeed"]];
	[maxChargeField setFloatValue:[defaults floatForKey:@"maxCharge"]];
	[maxMassField setFloatValue:[defaults floatForKey:@"maxMass"]];
	[maxInitialSpeedFractionField setFloatValue:[defaults floatForKey:@"maxInitialSpeedFraction"]];
	[particleDensityField setIntValue:[defaults integerForKey:@"particleDensity"]];
	[trailLengthField setIntValue:[defaults integerForKey:@"trailLength"]];
	[fractionalChargeField setState:[defaults boolForKey:@"fractionalCharge"]];
	[outlinedCirclesField setState:[defaults boolForKey:@"outlinedCircles"]];
	[particleAlphaField setFloatValue:[defaults floatForKey:@"particleAlpha"]];
	[displayDynamicMassField setState:[defaults boolForKey:@"displayDynamicMass"]];
	[edgesField selectItemWithTitle:[defaults stringForKey:@"edges"]];
	[relativisticField selectItemWithTitle:[defaults stringForKey:@"relativistic"]];

	return configSheet;
}

- (IBAction)cancelClick: (id)sender
{
	[[NSApplication sharedApplication] endSheet:configSheet];
}

- (IBAction)okClick: (id)sender
{
	[defaults setInteger:[nParticlesField intValue] forKey:@"nParticles"];
	[defaults setFloat:[FPSField floatValue] forKey:@"FPS"];
	[defaults setFloat:[gravityCoefficientField floatValue] forKey:@"gravityCoefficient"];
	[defaults setFloat:[electricCoefficientField floatValue] forKey:@"electricCoefficient"];
	[defaults setFloat:[maxSpeedField floatValue] forKey:@"maxSpeed"];
	[defaults setFloat:[maxChargeField floatValue] forKey:@"maxCharge"];
	[defaults setFloat:[maxMassField floatValue] forKey:@"maxMass"];
	[defaults setFloat:[maxInitialSpeedFractionField floatValue] forKey:@"maxInitialSpeedFraction"];
	[defaults setInteger:[particleDensityField intValue] forKey:@"particleDensity"];
	[defaults setInteger:[trailLengthField intValue] forKey:@"trailLength"];
	[defaults setBool:[fractionalChargeField state] forKey:@"fractionalCharge"];
	[defaults setBool:[outlinedCirclesField state] forKey:@"outlinedCircles"];
	[defaults setFloat:[particleAlphaField floatValue] forKey:@"particleAlpha"];
	[defaults setBool:[displayDynamicMassField state] forKey:@"displayDynamicMass"];
	[defaults setObject:[[edgesField selectedItem] title] forKey:@"edges"];
	[defaults setObject:[[relativisticField selectedItem] title] forKey:@"relativistic"];

	[defaults synchronize];
	[self loadConfig];
	[[NSApplication sharedApplication] endSheet:configSheet];
}

- (IBAction)resetClick: (id)sender
{
	NSEnumerator *enumerator = [originalDefaults keyEnumerator];
	id key;
	while ((key = [enumerator nextObject])) {
		[defaults setObject: [originalDefaults objectForKey: key] forKey: key];
	}
	[defaults synchronize];
	[self configureSheet];
}

- (void)loadConfig
{
	nParticles = [defaults integerForKey: @"nParticles"];
	maxSpeed = [defaults floatForKey: @"maxSpeed"];
	maxCharge = [defaults floatForKey: @"maxCharge"];
	maxMass = [defaults floatForKey: @"maxMass"];
}

- (void)blankView
{
	if ([defaults integerForKey: @"trailLength"] != 0) {
		NSColor *bgColor;
		bgColor = [NSColor blackColor];
		if ([defaults integerForKey: @"trailLength"] != 0)
			bgColor = [bgColor colorWithAlphaComponent: 1.0/[defaults integerForKey: @"trailLength"]];
		[bgColor set];
		[NSBezierPath fillRect:[self frame]];
	} else {
		[super drawRect:[self frame]];
	}
}

- (void)blankPath: (NSBezierPath*)path
{
	NSColor *color;
	color = [NSColor blackColor];
	[color set];
	[path setLineWidth: 3.0];
	if ([defaults boolForKey: @"outlinedCircles"]) {
		[path stroke];
	} else {
		//[NSBezierPath fillRect:[path bounds]];
		[path fill];
		[path stroke];
	}
}

- (void)initializeParticles
{
	size = [self bounds].size;
	dirtyPaths = [NSMutableArray arrayWithCapacity: nParticles];
	[dirtyPaths retain];	
	particles = [NSMutableArray arrayWithCapacity: nParticles];
	[particles retain];
	int i;
	for (i = 0; i < nParticles; i++) {
		[dirtyPaths addObject: [NSBezierPath bezierPath]];
		[particles addObject: [self randomNewParticle]];
	}
	
    /*
	// Sun
	NSPoint centerPt;
	centerPt.x = size.width / 2.0;
	centerPt.y = size.height / 2.0;
	NSPoint zeroVelocity;
	zeroVelocity.x = zeroVelocity.y = 0;
	Particle* sun;
	sun = [self newParticleWithPosition: centerPt withVelocity: zeroVelocity withMass: maxMass * 100 withCharge: 0];
    [dirtyPaths addObject: [NSBezierPath bezierPath]];
	[particles addObject: sun];
	nParticles++;
    */
	
	[dirtyPaths retain];
}

- (float)radiusFromMass: (float)mass
{
	return pow(mass, (1/3.0)) * size.width / [defaults integerForKey: @"particleDensity"];
}

- (Particle*)randomNewParticle
{
	float mass, radius, charge;
	NSPoint position, velocity;
	if ([defaults boolForKey: @"fractionalCharge"])
		charge = SSRandomFloatBetween(-1, 1) * maxCharge;
	else
		charge = SSRandomIntBetween(-1, 1) * maxCharge;
	mass = SSRandomFloatBetween(1.0, maxMass);
	radius = [self radiusFromMass: mass];
	position.x = SSRandomFloatBetween(radius, size.width - radius);
	position.y = SSRandomFloatBetween(radius, size.height - radius);
	velocity.x = [defaults floatForKey: @"maxInitialSpeedFraction"] * SSRandomFloatBetween(-1, 1) * maxSpeed;
	velocity.y = [defaults floatForKey: @"maxInitialSpeedFraction"] * SSRandomFloatBetween(-1, 1) * maxSpeed;
	return [self newParticleWithPosition: position withVelocity: velocity withMass: mass withCharge: charge];
}

- (Particle*)newParticleWithPosition: (NSPoint)position withVelocity: (NSPoint)velocity withMass: (float)mass withCharge: (float)charge
{
	Particle *p;
	p = [Particle alloc];
	p = [p initWithDefaults: defaults withViewSize: size withPosition: position withVelocity: velocity withMass: mass withCharge: charge];
	return p;
}

- (void)releaseParticles
{
	int i;
	for (i = 0; i < nParticles; i++) {
		[[particles objectAtIndex: i] release];
	}
	[particles release];
}

- (NSBezierPath*)drawParticle: (Particle*)p
{
	float charge;
	charge = [p charge];
	float mass;
	if ([defaults boolForKey: @"displayDynamicMass"])
		mass = [p mass];
	else
		mass = [p restMass];
	NSColor *color;
	float chargePercent;
	chargePercent = fabs(charge) / maxCharge;
	if (charge > 0)
		color = [NSColor colorWithCalibratedRed:1.0 green:(1-chargePercent) blue:(1-chargePercent) alpha:[defaults floatForKey: @"particleAlpha"]];
	else
		color = [NSColor colorWithCalibratedRed:(1-chargePercent) green:(1-chargePercent) blue:1.0 alpha:[defaults floatForKey: @"particleAlpha"]];
	NSBezierPath *path;
	path = [NSBezierPath bezierPath];
	[path appendBezierPathWithArcWithCenter: [p position]
                                     radius: [self radiusFromMass: mass]
                                 startAngle: 0.0
                                   endAngle: 360.0
                                  clockwise: 0];
	[color set];
	if ([defaults boolForKey: @"outlinedCircles"])
		[path stroke];
	else
		[path fill];
	return path;
}

@end
