//
//  FirstViewController.m
//  AudioRecorderPlayer
//
//  Created by Chris Adamson on 5/17/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 "RecordViewController.h"


@implementation RecordViewController


@synthesize filenameField, formatSegments, formatButton, currentTimeLabel,
	recordPauseButton, stopButton,
	leftLevelLabel, leftLevelMeter, rightLevelLabel, rightLevelMeter, 
	pcmSettingsViewController, encodingSettingsViewController;


/*
// The designated initializer. Override to perform setup that is required before the view is loaded.
- (id)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil {
    if (self = [super initWithNibName:nibNameOrNil bundle:nibBundleOrNil]) {
        // Custom initialization
    }
    return self;
}
*/

#pragma mark helper methods

// returns _documentsPath, lazy-instantiating it if necessary
//START:code.RecordViewController.documentspath
- (NSString*) documentsPath {
	if (! _documentsPath) {
		NSArray *searchPaths =
			NSSearchPathForDirectoriesInDomains
			(NSDocumentDirectory, NSUserDomainMask, YES);
		_documentsPath = [searchPaths objectAtIndex: 0];
		[_documentsPath retain];
	}
	return _documentsPath;
}
//END:code.RecordViewController.documentspath


- (void) updateAudioDisplay {
	if (audioRecorder == nil) {
		currentTimeLabel.text = @"--:--";
	} else {
		double currentTime = audioRecorder.currentTime;
		currentTimeLabel.text = [NSString stringWithFormat: @"%02d:%02d",
								 (int) currentTime/60,
								 (int) currentTime%60];		
		//START:code.RecordViewController.setlevelmeters
		[audioRecorder updateMeters];
		[leftLevelMeter setPower: [audioRecorder averagePowerForChannel:0]
				peak: [audioRecorder peakPowerForChannel: 0]];
		if (! rightLevelMeter.hidden) {
			[rightLevelMeter setPower: [audioRecorder averagePowerForChannel:1]
				peak: [audioRecorder peakPowerForChannel: 1]];
		}
//END:code.RecordViewController.setlevelmeters
	}
}


// attempts to setup AVAudioRecorder from filename and settings
//START:code.RecordViewController.createavaudiorecorder.url
- (NSError*) createAVAudioRecorder {
	[audioRecorder release];
	audioRecorder = nil;
	
	NSString *destinationString = [[self documentsPath]
			stringByAppendingPathComponent:filenameField.text];
	NSURL *destinationURL = [NSURL fileURLWithPath: destinationString];
//END:code.RecordViewController.createavaudiorecorder.url

//START:code.RecordViewController.createavaudiorecorder.pcmsettings
	NSMutableDictionary *recordSettings =
		[[NSMutableDictionary alloc] initWithCapacity:10];
	if (formatSegments.selectedSegmentIndex == 0) {
		// force pcm settings view to load so its fields can be read
		pcmSettingsViewController.view; // <label id="code.RecordViewController.createavaudiorecorder.pcmsettings.forceviewtoload"/>
		[recordSettings setObject:
		 [NSNumber numberWithInt: kAudioFormatLinearPCM] forKey: AVFormatIDKey];
		float sampleRate =
			[pcmSettingsViewController.sampleRateField.text floatValue];
		[recordSettings setObject:
		 [NSNumber numberWithFloat:sampleRate] forKey: AVSampleRateKey];
		[recordSettings setObject:
			[NSNumber numberWithInt:
				(pcmSettingsViewController.stereoSwitch.on ? 2 : 1)]
			forKey:AVNumberOfChannelsKey];
		int bitDepth =
			[pcmSettingsViewController.sampleDepthField.text intValue];
		[recordSettings setObject:
		 [NSNumber numberWithInt:bitDepth] forKey:AVLinearPCMBitDepthKey];
		[recordSettings setObject:
			[NSNumber numberWithBool:
					pcmSettingsViewController.bigEndianSwitch.on]
				forKey:AVLinearPCMIsBigEndianKey];
		[recordSettings setObject:
			[NSNumber numberWithBool:
					pcmSettingsViewController.floatingSamplesSwitch.on]
				forKey:AVLinearPCMIsFloatKey];
	}
//END:code.RecordViewController.createavaudiorecorder.pcmsettings
//START:code.RecordViewController.createavaudiorecorder.encodingsettings
	else {
		// force pcm settings view to load so its fields can be read
		encodingSettingsViewController.view;
		NSNumber *formatObject;
		switch ([encodingSettingsViewController.formatSegments
				 selectedSegmentIndex]) {
			case (ENCODED_FORMAT_AAC): 
				formatObject =
					[NSNumber numberWithInt: kAudioFormatMPEG4AAC];
				break;
			case (ENCODED_FORMAT_ALAC):
				formatObject =
					[NSNumber numberWithInt: kAudioFormatAppleLossless];
				break;
			case (ENCODED_FORMAT_IMA4):
				formatObject =
					[NSNumber numberWithInt: kAudioFormatAppleIMA4];
				break;
			case (ENCODED_FORMAT_ILBC):
				formatObject =
					[NSNumber numberWithInt: kAudioFormatiLBC];
				break;
			case (ENCODED_FORMAT_ULAW):
				formatObject =
					[NSNumber numberWithInt: kAudioFormatULaw];
				break;
			default:
				formatObject =
					[NSNumber numberWithInt: kAudioFormatLinearPCM];
		}
		[recordSettings setObject:formatObject forKey: AVFormatIDKey];
		float sampleRate =
			[encodingSettingsViewController.sampleRateField.text floatValue];
		[recordSettings setObject: [NSNumber numberWithFloat:sampleRate]
			forKey: AVSampleRateKey];
		[recordSettings setObject:
			[NSNumber numberWithInt:
			 (encodingSettingsViewController.stereoSwitch.on ? 2 : 1)]
			forKey:AVNumberOfChannelsKey];
		int encoderBitrate =
			[encodingSettingsViewController.encoderBitrateField.text intValue];
		[recordSettings setObject:[NSNumber numberWithInt:encoderBitrate]
			forKey:AVEncoderBitRateKey];
		int bitDepthHint =
			[encodingSettingsViewController.encoderBitHint.text intValue];
		[recordSettings setObject:
				[NSNumber numberWithInt:bitDepthHint]
			forKey:AVEncoderBitDepthHintKey];
		int encoderQuality;
		switch ([encodingSettingsViewController.qualitySegments
				 selectedSegmentIndex]) {
			case (0) : encoderQuality = AVAudioQualityMin; break;
			case (1) : encoderQuality = AVAudioQualityLow; break;
			case (2) : encoderQuality = AVAudioQualityMedium; break;
			case (3) : encoderQuality = AVAudioQualityHigh; break;
			case (4) : encoderQuality = AVAudioQualityMax; break;
		}
		[recordSettings setObject:
				[NSNumber numberWithInt: encoderQuality]
			forKey: AVEncoderAudioQualityKey];
	}
//END:code.RecordViewController.createavaudiorecorder.encodingsettings
	
//START:code.RecordViewController.createavaudiorecorder.init
	NSError *recorderSetupError = nil;
	audioRecorder = [[AVAudioRecorder alloc] initWithURL:destinationURL
			settings:recordSettings error:&recorderSetupError];	
	[recordSettings release];
	
	if (recorderSetupError) {
		UIAlertView *cantRecordAlert =
		[[UIAlertView alloc] initWithTitle: @"Can't record"
			message: [recorderSetupError localizedDescription]
			delegate: nil
			cancelButtonTitle:@"OK"
			otherButtonTitles:nil];
		[cantRecordAlert show];
		[cantRecordAlert release];
		return recorderSetupError;
	}
	[audioRecorder prepareToRecord];
	recordPauseButton.enabled = YES;
	audioRecorder.delegate = self;
//END:code.RecordViewController.createavaudiorecorder.init
	
	// hide right level meter and change left to "M" if we're mono
//START:code.RecordViewController.handlemono
	audioRecorder.meteringEnabled = YES;
	if ([[audioRecorder.settings
		  objectForKey:AVNumberOfChannelsKey] intValue] > 1) {
		leftLevelLabel.text = @"L";
		rightLevelLabel.hidden = NO;
		rightLevelMeter.hidden = NO;
	} else  {
		leftLevelLabel.text = @"M";
		rightLevelLabel.hidden = YES;
		rightLevelMeter.hidden = YES;
	}
//END:code.RecordViewController.handlemono
	
	NSLog (@"error: %@", recorderSetupError);
	return recorderSetupError;
}

/* a handy tool for inspecting OSStatus return codes
//START:code.RecordViewController.log4cc
int errorCode = CFSwapInt32HostToBig ([recorderSetupError code]);
NSLog(@"Error: %@ [%4.4s])",
 [recorderSetupError localizedDescription], (char*)&errorCode);
//END:code.RecordViewController.log4cc
 */

//START:code.RecordViewController.alertifnoaudioinput
-(BOOL) alertIfNoAudioInput {
	AVAudioSession *session = [AVAudioSession sharedInstance];
	BOOL audioHWAvailable = session.inputIsAvailable;
	if (! audioHWAvailable) {
		UIAlertView *cantRecordAlert =
		[[UIAlertView alloc] initWithTitle: @"Can't record"
			message: @"No audio input hardware is available"
			delegate: nil
			cancelButtonTitle:@"OK"
			otherButtonTitles:nil];
		[cantRecordAlert show];
		[cantRecordAlert release]; 
	}
	return audioHWAvailable;
}
//END:code.RecordViewController.alertifnoaudioinput

/* this start method quoted earlier in the chapter does not have the
	check for audio input hardware that's added later
//START:code.RecordViewController.startrecordingwithouthwcheck
-(void) startRecording {
	[audioRecorder record];
	recordPauseButton.selected = YES;
	formatButton.enabled = NO;
}

-(void) pauseRecording {
	[audioRecorder pause];
	recordPauseButton.selected = NO;
}

-(void) stopRecording {
	[audioRecorder stop];
}
//END:code.RecordViewController.startrecordingwithouthwcheck
*/


-(void) startRecording {
	if (! [self alertIfNoAudioInput]) 
		return;
	[audioRecorder record];
	recordPauseButton.selected = YES;
	formatButton.enabled = NO;
}

-(void) pauseRecording {
	[audioRecorder pause];
	recordPauseButton.selected = NO;
}

-(void) stopRecording {
	[audioRecorder stop];
}
// stop's GUI cleanup in audioRecorderDidFinishRecording:successfully


#pragma mark UITextField delegates
//START:code.RecordViewController.textfielddidendediting
- (void)textFieldDidEndEditing:(UITextField *)textField {
	[textField resignFirstResponder];
	// verify that there's a legitimate filename extension
	if ([[textField.text pathExtension] length] == 0)
		textField.text =
		[NSString stringWithFormat: @"%@.caf", textField.text];
	[self createAVAudioRecorder];
}
//END:code.RecordViewController.textfielddidendediting

- (BOOL)textFieldShouldReturn:(UITextField *)textField {
	[textField resignFirstResponder];
	return YES;
}

#pragma mark AVAudioRecorderDelegate methods
//START:code.RecordViewController.delegate.finish
- (void)audioRecorderDidFinishRecording:(AVAudioRecorder *)recorder
		successfully:(BOOL)flag {
	NSLog (@"audioRecorderDidFinishRecording:successfully:");
	recordPauseButton.selected = NO;
	recordPauseButton.enabled = NO;
	formatButton.enabled = YES;
	[audioRecorder release];
	audioRecorder = nil;
	filenameField.text = @"";	
}

- (void)audioRecorderEncodeErrorDidOccur:(AVAudioRecorder *)recorder
		error:(NSError *)error {
	recordPauseButton.selected = NO;
	recordPauseButton.enabled = NO;
	[audioRecorder release];
	audioRecorder = nil;
	filenameField.text = @"";	
}
//END:code.RecordViewController.delegate.finish

- (void)audioRecorderBeginInterruption:(AVAudioRecorder *)recorder {
	NSLog (@"begin interruption!");
	recordPauseButton.selected = NO;
}

- (void)audioRecorderEndInterruption:(AVAudioRecorder *)recorder {
	NSLog (@"end interruption!");
}


#pragma mark button handlers
//START:code.RecordViewController.handleformatbuttontapped
-(IBAction) handleFormatButtonTapped {
	if (formatSegments.selectedSegmentIndex == 0) {
		[self presentModalViewController:pcmSettingsViewController
			animated:YES];
	} else {
		[self presentModalViewController:encodingSettingsViewController
			animated:YES];
	}
}
//END:code.RecordViewController.handleformatbuttontapped

-(IBAction) handleFormatSegmentChange {
	[self createAVAudioRecorder];
}

//START:code.RecordViewController.handlebuttons
-(IBAction) handleRecordPauseTapped {
	NSLog (@"handleRecordPauseTapped");
	if (audioRecorder.recording) {
		[self pauseRecording];
	} else {
		[self startRecording];
	}
}

-(IBAction) handleStopTapped {
	NSLog (@"handleStopTapped");
	if (audioRecorder.recording) {
		[self stopRecording];
	}
}
//END:code.RecordViewController.handlebuttons


# pragma mark VC lifecycle methods
/*
// Implement loadView to create a view hierarchy programmatically, without using a nib.
 */
- (void)loadView {
	[super loadView];
}

/*
// Implement viewDidLoad to do additional setup after loading the view, typically from a nib.
 */
- (void)viewDidLoad {
    [super viewDidLoad];
	// check for input h/w
	[self alertIfNoAudioInput];
	
	// setup clock
	currentTimeUpdateTimer = [NSTimer scheduledTimerWithTimeInterval:0.1
		target:self selector:@selector(updateAudioDisplay)
		userInfo:NULL repeats:YES];
}

/*
// 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;
}


- (void)dealloc {
    [super dealloc];
	[filenameField release];
	[formatSegments release];
	[formatButton release];
	[currentTimeLabel release];
	[recordPauseButton release];
	[stopButton release];
	[leftLevelLabel release];
	[leftLevelMeter release]; 
	[rightLevelLabel release];
	[rightLevelMeter release];  
	[pcmSettingsViewController release];
	[encodingSettingsViewController release];
	
}

@end
