//
//  MainViewController.m
//  BalanceBall
//
//  Created by Chris Adamson on 6/15/09.
//  Copyright Subsequently and Furthermore, Inc. 2009. All rights reserved.
//
//
//  Licensed with the Apache 2.0 License
//  http://apache.org/licenses/LICENSE-2.0
//


#import "MainViewController.h"
#import "MainView.h"


@implementation MainViewController

//START:code.BalanceBall.MainViewController.filterconsts
// filter types, as values from the multiswitch
enum SORT_TYPES {
	NO_FILTER = 0,
	LOW_PASS_FILTER,
	HIGH_PASS_FILTER
};

// how much weight we give to a single accelerometer value
float kFilteringFactor = 0.1;

// how much acceleration (high-pass filtered) counts as a shake
CGFloat SHAKE_THRESHOLD = 2.0;
//END:code.BalanceBall.MainViewController.filterconsts

// ball size
CGFloat BALL_WIDTH = 20.0;
CGFloat BALL_HEIGHT = 20.0;

// how many pixels/sec/sec an accel of 1.0 represents
CGFloat MAX_ACCEL_PER_SEC = 200;


- (id)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil {
    if (self = [super initWithNibName:nibNameOrNil bundle:nibBundleOrNil]) {
        // Custom initialization
    }
    return self;
}


//START:code.BalanceBall.MainViewController.reset
- (void) resetBall {
	ballVelocityX = 0.0;
	ballVelocityY = 0.0;
	filteredAccelX = 0.0;
	filteredAccelY = 0.0;
	ballX = self.view.frame.size.width / 2;
	ballY = self.view.frame.size.height / 2;
}

- (void) checkBallInPlay {
	if	((ballX + BALL_WIDTH < 0) ||
		 (ballY + BALL_HEIGHT < 0) ||
		 (ballX > self.view.frame.size.width) ||
		 (ballY > self.view.frame.size.height)) {
		NSLog (@"reset ball out of bounds");
		[self resetBall];
	}
}
//END:code.BalanceBall.MainViewController.reset


- (void)accelerometer:(UIAccelerometer *)accelerometer didAccelerate:(UIAcceleration *)acceleration {
	
	//START:code.BalanceBall.MainViewController.calculatefilters
	CGFloat lowPassFilteredX = (acceleration.x * kFilteringFactor) +
	(filteredAccelX * (1.0 - kFilteringFactor));
	CGFloat lowPassFilteredY = (acceleration.y * kFilteringFactor) +
	(filteredAccelY * (1.0 - kFilteringFactor));
	CGFloat highPassFilteredX = acceleration.x - lowPassFilteredX;
	CGFloat highPassFilteredY = acceleration.y - lowPassFilteredY;
	//END:code.BalanceBall.MainViewController.calculatefilters
	
	//START:code.BalanceBall.MainViewController.applyfilters
	switch (filterPref) {
		case NO_FILTER: {
			filteredAccelX = acceleration.x;
			filteredAccelY = acceleration.y;
			break;
		}
		case LOW_PASS_FILTER: {
			filteredAccelX = lowPassFilteredX;
			filteredAccelY = lowPassFilteredY;
			break;
		}
		case HIGH_PASS_FILTER: {
			filteredAccelX = highPassFilteredX;
			filteredAccelY = highPassFilteredY;
			break;
		}
	}
	
	// apply acceleration to velocity
	NSTimeInterval elapsedTime = acceleration.timestamp - lastAccelTimestamp;
	ballVelocityX = ballVelocityX + (filteredAccelX * MAX_ACCEL_PER_SEC * elapsedTime);
	ballVelocityY = ballVelocityY - (filteredAccelY * MAX_ACCEL_PER_SEC * elapsedTime);
	//END:code.BalanceBall.MainViewController.applyfilters
	
	if (lastAccelTimestamp > 0) {
		// recalc ball position
		ballX += (ballVelocityX * elapsedTime);
		ballY += (ballVelocityY * elapsedTime);
		// update view
		[(MainView*) self.view setBallX:ballX Y:ballY]; 
		// check for ball out of bounds
		[self checkBallInPlay];
	}
	lastAccelTimestamp = acceleration.timestamp;

	/* a 2.0-compatible form of detecting shakes, uncomment to use
	//START:code.BalanceBall.MainViewController.detectshake
	// detect shake
	if ((fabsf (filteredAccelX) > SHAKE_THRESHOLD) ||
		(fabsf (filteredAccelY) > SHAKE_THRESHOLD)) {
		[self resetBall];
	}
	//END:code.BalanceBall.MainViewController.detectshake
	*/
}

	/* book excerpt before filtering is discussed
	 //START:code.BalanceBall.MainViewController.simpleacceleration
- (void)accelerometer:(UIAccelerometer *)accelerometer
		didAccelerate:(UIAcceleration *)acceleration {
	 NSTimeInterval elapsedTime = acceleration.timestamp - lastAccelTimestamp;
	 ballVelocityX = ballVelocityX +
		(acceleration.x * MAX_ACCEL_PER_SEC * elapsedTime);
	 ballVelocityY = ballVelocityY -
		(acceleration.y * MAX_ACCEL_PER_SEC * elapsedTime);
	 if (lastAccelTimestamp > 0) {
	 // recalc ball position
	 ballX += (ballVelocityX * elapsedTime);
	 ballY += (ballVelocityY * elapsedTime);
	 // update view
	 [mainView updateBallX:ballX Y:ballY];
	 // check for ball out of bounds
	 [self checkBallInPlay];
	 }
	 lastAccelTimestamp = acceleration.timestamp;
	 //END:code.BalanceBall.MainViewController.simpleacceleration
}
	 */

// delegate method we defined in MainView
//START:code.BalanceBall.MainViewController.shakemotionbegan
-(void) shakeMotionBegan: (UIEvent*) event {
	NSLog (@"got system shake motion");
	[self resetBall];
}
//END:code.BalanceBall.MainViewController.shakemotionbegan

/*
 // Implement viewDidLoad to do additional setup after loading the view, typically from a nib.
 */
 - (void)viewDidLoad {
	 [super viewDidLoad];
	 // listen for system shakes via UIResponder events on the view
	 // set up ball velocity and location (approximately middle of view)
	 //START:code.BalanceBall.MainViewController.setupball
	 [(MainView*) self.view setBallWidth: BALL_WIDTH height:BALL_HEIGHT];
	 [self resetBall];
	 [(MainView*) self.view setBallX:ballX Y:ballY];
	 //END:code.BalanceBall.MainViewController.setupball
	 //START:code.BalanceBall.MainViewController.useaccelerometer
	 // ask for accelerometer events
	 [[UIAccelerometer sharedAccelerometer] setUpdateInterval: 0.03]; // 30 fps
	 [[UIAccelerometer sharedAccelerometer] setDelegate: self];
	 //END:code.BalanceBall.MainViewController.useaccelerometer
 }

//START:code.BalanceBall.MainViewController.viewdidappear
- (void) viewDidAppear: (BOOL) animated {
	[super viewDidAppear: animated];
	[self.view becomeFirstResponder];
	((MainView*)self.view).shakeDelegate = self;
}
//END:code.BalanceBall.MainViewController.viewdidappear

/*
 // Override to allow orientations other than the default portrait orientation.
 - (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation {
 // Return YES for supported orientations
 return (interfaceOrientation == UIInterfaceOrientationPortrait);
 }
 */


//START:code.BalanceBall.MainViewController.flipsideviewcontrollerdidfinish
- (void)flipsideViewControllerDidFinish:(FlipsideViewController *)controller {
	[self dismissModalViewControllerAnimated:YES];
	// set low-level filter use to switch state
	filterPref = controller.filterControl.selectedSegmentIndex;
}
//END:code.BalanceBall.MainViewController.flipsideviewcontrollerdidfinish


- (IBAction)showInfo {    
	
	FlipsideViewController *controller = [[FlipsideViewController alloc] initWithNibName:@"FlipsideView" bundle:nil];
	controller.delegate = self;
	
	controller.modalTransitionStyle = UIModalTransitionStyleFlipHorizontal;
	[self presentModalViewController:controller animated:YES];
	
	[controller release];
}



/*
 // Override to allow orientations other than the default portrait orientation.
 - (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation {
 // Return YES for supported orientations
 return (interfaceOrientation == UIInterfaceOrientationPortrait);
 }
 */

- (void)didReceiveMemoryWarning {
	// Releases the view if it doesn't have a superview.
    [super didReceiveMemoryWarning];
	
	// Release any cached data, images, etc that aren't in use.
}

- (void)viewDidUnload {
	// Release any retained subviews of the main view.
	// e.g. self.myOutlet = nil;
}


//START:code.BalanceBall.MainViewController.dealloc
- (void)dealloc {
	[[UIAccelerometer sharedAccelerometer] setDelegate: nil];
	[super dealloc];
}
//END:code.BalanceBall.MainViewController.dealloc

@end
