//
//  IBTAudioController.m
//  Cruiser
//
//  Created by Xummer on 4/17/15.
//  Copyright (c) 2015 Xummer. All rights reserved.
//

#import "IBTAudioController.h"

@import AVFoundation;
@import AudioToolbox;

@interface IBTAudioController () <AVAudioSessionDelegate>

@end

@implementation IBTAudioController

static IBTAudioController *sharedInstance = nil;

+ (IBTAudioController *)sharedController {
    
    @synchronized(self) {
        if (nil == sharedInstance) {
            sharedInstance = [[self alloc] init];
        }
    }
    return sharedInstance;
}

+ (void)unload {
    if (sharedInstance) {
        sharedInstance = nil;
    }
}

- (id)init{
    self = [super init];
    if (self) {
        NSNotificationCenter *notiCenter = [NSNotificationCenter defaultCenter];
        [notiCenter addObserver: self
                       selector: @selector(proximityStateDidChange:)
                           name: UIDeviceProximityStateDidChangeNotification
                         object: nil];
        
    }
    return self;
}

- (void)dealloc {
    //    AVAudioSession *mySession = [AVAudioSession sharedInstance];
    
    //    [mySession setDelegate:nil];
    NSNotificationCenter *notiCenter = [NSNotificationCenter defaultCenter];
    [notiCenter removeObserver: self
                          name: AVAudioSessionInterruptionNotification
                        object: nil];
    [notiCenter removeObserver: self
                          name: AVAudioSessionRouteChangeNotification
                        object: nil];
    
    [self deactivate];
    [notiCenter removeObserver: self
                          name: UIDeviceProximityStateDidChangeNotification
                        object: nil];
}

//the callback define for audio route change
void propChangeListenerCallback (
                                 void                      *inUserData,
                                 AudioSessionPropertyID    inPropertyID,
                                 UInt32                    inPropertyValueSize,
                                 const void                *inPropertyValue
                                 );

- (void)setup {
    
    AVAudioSession *mySession = [AVAudioSession sharedInstance];
    
    // Assign the Playback category to the audio session.
    //    OSStatus err;
    
    NSNotificationCenter *notiCenter = [NSNotificationCenter defaultCenter];
    [notiCenter addObserver: self
                   selector: @selector(handleInterruption:)
                       name: AVAudioSessionInterruptionNotification
                     object: mySession];
    
    [notiCenter addObserver: self
                   selector: @selector(handleRouteChange:)
                       name: AVAudioSessionRouteChangeNotification
                     object: nil];
    
    //    // listen for audio route changes
    //    err = AudioSessionAddPropertyListener(
    //                                          kAudioSessionProperty_AudioRouteChange,
    //                                          propChangeListenerCallback,
    //                                          self);
    //    if(kAudioSessionNoError != err){
    //        NSLog(@"unable to register property listener for AudioRouteChange.");
    //        return NO;
    //    }
    //
    //    // listen for audio input availability changes
    //    err = AudioSessionAddPropertyListener(
    //                                          kAudioSessionProperty_AudioInputAvailable,
    //                                          propChangeListenerCallback,
    //                                          self);
    //    if(kAudioSessionNoError != err){
    //        NSLog(@"unable to register property listener for AudioInputAvailable.");
    //        return NO;
    //    }
    
    
    
    //    if(![self activate]){
    //        NSLog(@"unable to activate AudioSession.");
    //        return NO;
    //    }
}

- (BOOL)setAVAudioSessionCategory:(NSString*)category
{
    AVAudioSession *mySession = [AVAudioSession sharedInstance];
    
    // Assign the Playback category to the audio session.
    NSError *audioSessionError = nil;
    [mySession setCategory: category
                     error: &audioSessionError];
    
    if (audioSessionError != nil) {
        NSLog (@"Error setting audio session category.");
        return NO;
    }
    else {
        return YES;
    }
}

- (BOOL)switchToPlaybackMode {
    
    return [self setAVAudioSessionCategory:AVAudioSessionCategoryPlayback];
}

- (BOOL)switchToPlayAndRecordMode {
    
    return [self setAVAudioSessionCategory:AVAudioSessionCategoryPlayAndRecord];
}

- (void)reset {
    
}

- (BOOL)activate {
    
    UInt32 sessionCategory = kAudioSessionCategory_MediaPlayback;
    AudioSessionSetProperty(kAudioSessionProperty_AudioCategory,
                            sizeof(sessionCategory),
                            &sessionCategory);
    
    UInt32 audioRouteOverride = kAudioSessionOverrideAudioRoute_Speaker;
    AudioSessionSetProperty (kAudioSessionProperty_OverrideAudioRoute,
                             sizeof (audioRouteOverride),
                             &audioRouteOverride);
    
    AVAudioSession *audioSession = [AVAudioSession sharedInstance];
    
    //默认情况下扬声器播放
    [audioSession setCategory:AVAudioSessionCategoryPlayback error:nil];
    
    NSError *audioSessionError = nil;
    
    [audioSession setActive:YES error:&audioSessionError];
    
    if (audioSessionError != nil) {
        NSLog (@"Error activating audio session during initial setup.");
        return NO;
    }
    else {
        return YES;
    }
}

- (BOOL)deactivate {
    AVAudioSession *mySession = [AVAudioSession sharedInstance];
    NSError *audioSessionError = nil;
    // Deactivate the audio session
    
    [mySession setActive:NO withOptions:AVAudioSessionSetActiveOptionNotifyOthersOnDeactivation error:&audioSessionError];
    
    if (audioSessionError != nil) {
        NSLog (@"Error activating audio session during initial setup.");
        return NO;
    }
    else {
        return YES;
    }
}

#pragma mark -
#pragma mark -
- (void)handleInterruption:(NSNotification*)notification{
    NSInteger reason = 0;
    NSString* reasonStr= @"";
    if ([notification.name isEqualToString:AVAudioSessionInterruptionNotification]) {
        //Posted when an audio interruption occurs.
        reason = [[[notification userInfo] objectForKey:AVAudioSessionInterruptionTypeKey] integerValue];
        if (reason == AVAudioSessionInterruptionTypeBegan) {
            //       Audio has stopped, already inactive
            //       Change state of UI, etc., to reflect non-playing state
            //            if(soundSessionIO_.isProcessingSound)[soundSessionIO_ stopSoundProcessing:nil];
            
        }
        
        if (reason == AVAudioSessionInterruptionTypeEnded) {
            //       Make session active
            //       Update user interface
            //       AVAudioSessionInterruptionOptionShouldResume option
            reasonStr = @"AVAudioSessionInterruptionTypeEnded";
            NSNumber* seccondReason = [[notification userInfo] objectForKey:AVAudioSessionInterruptionOptionKey] ;
            switch ([seccondReason integerValue]) {
                case AVAudioSessionInterruptionOptionShouldResume:
                    //          Indicates that the audio session is active and immediately ready to be used. Your app can resume the audio operation that was interrupted.
                    break;
                default:
                    break;
            }
        }
        
        
        if ([notification.name isEqualToString:@"AVAudioSessionDidBeginInterruptionNotification"]) {
            //            if (soundSessionIO_.isProcessingSound) {
            //
            //            }
            //      Posted after an interruption in your audio session occurs.
            //      This notification is posted on the main thread of your app. There is no userInfo dictionary.
        }
        if ([notification.name isEqualToString:@"AVAudioSessionDidEndInterruptionNotification"]) {
            //      Posted after an interruption in your audio session ends.
            //      This notification is posted on the main thread of your app. There is no userInfo dictionary.
        }
        if ([notification.name isEqualToString:@"AVAudioSessionInputDidBecomeAvailableNotification"]) {
            //      Posted when an input to the audio session becomes available.
            //      This notification is posted on the main thread of your app. There is no userInfo dictionary.
        }
        if ([notification.name isEqualToString:@"AVAudioSessionInputDidBecomeUnavailableNotification"]) {
            //      Posted when an input to the audio session becomes unavailable.
            //      This notification is posted on the main thread of your app. There is no userInfo dictionary.
        }
        
    };
    NSLog(@"handleInterruption: %@ reason %@",[notification name],reasonStr);
}

#pragma mark -
#pragma mark -
- (void)handleRouteChange:(NSNotification*)notification{
    AVAudioSession *session = [ AVAudioSession sharedInstance ];
    NSString* seccReason = @"";
    NSInteger reason = [[[notification userInfo] objectForKey:AVAudioSessionRouteChangeReasonKey] integerValue];
    //  AVAudioSessionRouteDescription* prevRoute = [[notification userInfo] objectForKey:AVAudioSessionRouteChangePreviousRouteKey];
    switch (reason) {
        case AVAudioSessionRouteChangeReasonNoSuitableRouteForCategory:
            seccReason = @"The route changed because no suitable route is now available for the specified category.";
            break;
        case AVAudioSessionRouteChangeReasonWakeFromSleep:
            seccReason = @"The route changed when the device woke up from sleep.";
            break;
        case AVAudioSessionRouteChangeReasonOverride:
            seccReason = @"The output route was overridden by the app.";
            break;
        case AVAudioSessionRouteChangeReasonCategoryChange:
            seccReason = @"The category of the session object changed.";
            UInt32 newCategory;
            UInt32 size = sizeof(newCategory);
            if (AudioSessionGetProperty(kAudioSessionProperty_AudioCategory, &size, &newCategory) == noErr &&
                newCategory != kAudioSessionCategory_PlayAndRecord && newCategory != kAudioSessionCategory_RecordAudio) {
                
            }
            NSLog(@"Audio category %u %d %d %d %d %d %d\n", (unsigned int)newCategory, kAudioSessionCategory_AmbientSound,
                  kAudioSessionCategory_SoloAmbientSound, kAudioSessionCategory_MediaPlayback, kAudioSessionCategory_RecordAudio, kAudioSessionCategory_PlayAndRecord
                  , kAudioSessionCategory_AudioProcessing);
            break;
        case AVAudioSessionRouteChangeReasonOldDeviceUnavailable:
            seccReason = @"The previous audio output path is no longer available.";
            break;
        case AVAudioSessionRouteChangeReasonNewDeviceAvailable:
            seccReason = @"A preferred new audio output path is now available.";
            break;
        case AVAudioSessionRouteChangeReasonUnknown:
        default:
            seccReason = @"The reason for the change is unknown.";
            break;
    }
    AVAudioSessionPortDescription *input = [[session.currentRoute.inputs count]?session.currentRoute.inputs:nil objectAtIndex:0];
    if (input.portType == AVAudioSessionPortHeadsetMic) {
        
    }
}

#pragma mark -
#pragma mark UIDeviceProximityStateDidChangeNotification


- (void)proximityStateDidChange:(NSNotification *)notification;
{
    UIDevice *device = [notification object];
    if ([device proximityState]){
        [[AVAudioSession sharedInstance] setCategory:AVAudioSessionCategoryPlayAndRecord error:nil];
    }
    else{
        [[AVAudioSession sharedInstance] setCategory:AVAudioSessionCategoryPlayback error:nil];
    }
}





#pragma mark -
#pragma mark Audio Route Change Listener
// Audio session callback function for responding to audio route changes. If playing back audio and
//   the user unplugs a headset or headphones, or removes the device from a dock connector for hardware
//   that supports audio playback, this callback detects that and stops playback.
//
// Refer to AudioSessionPropertyListener in Audio Session Services Reference.
void propChangeListenerCallback (
                                 void                      *inUserData,
                                 AudioSessionPropertyID    inPropertyID,
                                 UInt32                    inPropertyValueSize,
                                 const void                *inPropertyValue
                                 ) {
    
    //    AudioController *controller = (AudioController*) inUserData;
    
    // Ensure that this callback was invoked because of an audio route change
    if (inPropertyID == kAudioSessionProperty_AudioRouteChange) {
        CFDictionaryRef routeChangeDictionary = inPropertyValue;
        CFNumberRef routeChangeReasonRef = CFDictionaryGetValue(routeChangeDictionary,
                                                                CFSTR(kAudioSession_AudioRouteChangeKey_Reason));
        
        SInt32 routeChangeReason;
        
        CFNumberGetValue(routeChangeReasonRef, kCFNumberSInt32Type, &routeChangeReason);
        if (routeChangeReason == kAudioSessionRouteChangeReason_OldDeviceUnavailable) {
            NSLog(@"kAudioSessionRouteChangeReason_OldDeviceUnavailable.\n");
            //occurs when headset is unplugged
        }
        else if(routeChangeReason == kAudioSessionRouteChangeReason_NewDeviceAvailable) {
            //occurs when headset is plugged
            NSLog(@"kAudioSessionRouteChangeReason_NewDeviceAvailable.\n");
        }
        else if (routeChangeReason == kAudioSessionRouteChangeReason_NoSuitableRouteForCategory) {
            NSLog(@"kAudioSessionRouteChangeReason_NoSuitableRouteForCategory.\n");
            
        }
        else if(routeChangeReason == kAudioSessionRouteChangeReason_CategoryChange) {
            NSLog(@"kAudioSessionRouteChangeReason_CategoryChange.\n");
            UInt32 newCategory;
            UInt32 size = sizeof(newCategory);
            if (AudioSessionGetProperty(kAudioSessionProperty_AudioCategory, &size, &newCategory) == noErr &&
                newCategory != kAudioSessionCategory_PlayAndRecord && newCategory != kAudioSessionCategory_RecordAudio) {
                
            }
            NSLog(@"Audio category %u %d %d %d %d %d %d\n", (unsigned int)newCategory, kAudioSessionCategory_AmbientSound,
                  kAudioSessionCategory_SoloAmbientSound, kAudioSessionCategory_MediaPlayback, kAudioSessionCategory_RecordAudio, kAudioSessionCategory_PlayAndRecord
                  , kAudioSessionCategory_AudioProcessing);
        }
    }
    else if(inPropertyID == kAudioSessionProperty_AudioInputAvailable) {
        //may resume the audio record if the input become available
    }
}

@end
