//
//  TDAudioPlayer.m
//  TDAudioPlayer
//
//  Created by Tony DiPasquale on 10/16/13.
//  Copyright (c) 2013 Tony DiPasquale. The MIT License (MIT).
//

#import <AVFoundation/AVFoundation.h>
#import <MediaPlayer/MediaPlayer.h>

#import "TDAudioPlayer.h"

@interface TDAudioPlayer ()

@property (strong, nonatomic) TDAudioInputStreamer *streamer;
@property (strong, nonatomic) NSMutableDictionary *nowPlayingMetaInfo;
@property (assign, nonatomic) TDAudioPlayerState state;

@property (strong, nonatomic) NSTimer *timer;
@property (assign, nonatomic) NSUInteger elapsedTime;

@end

@implementation TDAudioPlayer

+ (instancetype)sharedAudioPlayer
{
    static TDAudioPlayer *player;
    static dispatch_once_t onceToken;
    dispatch_once(&onceToken, ^{
        player = [[TDAudioPlayer alloc] init];
    });
    return player;
}

- (instancetype)init
{
    self = [super init];
    if (!self) return nil;

    [[AVAudioSession sharedInstance] setActive:YES error:nil];
    [[AVAudioSession sharedInstance] setCategory:AVAudioSessionCategoryPlayback error:nil];

    [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(audioSessionDidInterrupt:) name:AVAudioSessionInterruptionNotification object:nil];
    [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(audioSessionDidChangeRoute:) name:AVAudioSessionRouteChangeNotification object:nil];
    [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(audioDidStartPlaying) name:TDAudioStreamDidStartPlayingNotification object:nil];

    return self;
}

#pragma mark - Audio Loading

- (void)loadAudioFromURL:(NSURL *)url
{
    [self loadAudioFromURL:url withMetaData:nil];
}

- (void)loadAudioFromURL:(NSURL *)url withMetaData:(TDAudioMetaInfo *)meta
{
    [self reset];
    self.streamer = [[TDAudioInputStreamer alloc] initWithURL:url];
    [self changeAudioMetaInfo:meta];
}

- (void)loadAudioFromStream:(NSInputStream *)stream
{
    [self loadAudioFromStream:stream withMetaData:nil];
}

- (void)loadAudioFromStream:(NSInputStream *)stream withMetaData:(TDAudioMetaInfo *)meta
{
    [self reset];
    self.streamer = [[TDAudioInputStreamer alloc] initWithInputStream:stream];
    [self changeAudioMetaInfo:meta];
}

- (void)reset
{
    [self.streamer stop];
    self.streamer = nil;

    self.state = TDAudioPlayerStateStopped;
    self.elapsedTime = 0;
    [self clearTimer];
}

#pragma mark - Audio Controls

- (void)play
{
    if (!self.streamer || self.state == TDAudioPlayerStatePlaying) return;
    if (self.state == TDAudioPlayerStateStopped) return [self start];

    [self.streamer resume];
    [self setNowPlayingInfoWithPlaybackRate:@1];
    self.timer = [NSTimer scheduledTimerWithTimeInterval:1.0f target:self selector:@selector(elapseTime) userInfo:nil repeats:YES];
    self.state = TDAudioPlayerStatePlaying;
    [[NSNotificationCenter defaultCenter] postNotificationName:TDAudioPlayerDidPlayNotification object:nil];
}

- (void)start
{
    if (self.state == TDAudioPlayerStateStarting) return;
    [self.streamer start];
    self.state = TDAudioPlayerStateStarting;
    [[NSNotificationCenter defaultCenter] postNotificationName:TDAudioPlayerDidPlayNotification object:nil];
}

- (void)pause
{
    if (!self.streamer || self.state == TDAudioPlayerStatePaused) return;

    [self setNowPlayingInfoWithPlaybackRate:@0.000001f];
    [self clearTimer];

    [self.streamer pause];
    self.state = TDAudioPlayerStatePaused;
    [[NSNotificationCenter defaultCenter] postNotificationName:TDAudioPlayerDidPauseNotification object:nil];
}

- (void)stop
{
    if (!self.streamer || self.state == TDAudioPlayerStateStopped) return;
    [self reset];
    [[NSNotificationCenter defaultCenter] postNotificationName:TDAudioPlayerDidStopNotification object:nil];
}

#pragma mark - Remote Events

- (void)handleRemoteControlEvent:(UIEvent *)event
{
    if (event.type != UIEventTypeRemoteControl) return;

    switch (event.subtype) {
        case UIEventSubtypeRemoteControlPause:
            [self pause];
            break;

        case UIEventSubtypeRemoteControlPlay:
            [self play];
            break;

        case UIEventSubtypeRemoteControlStop:
            [self stop];
            break;

        case UIEventSubtypeRemoteControlTogglePlayPause:
            if (self.state == TDAudioPlayerStatePlaying) {
                [self pause];
            } else {
                [self play];
            }
            break;

        case UIEventSubtypeRemoteControlNextTrack:
            [[NSNotificationCenter defaultCenter] postNotificationName:TDAudioPlayerNextTrackRequestNotification object:nil];
            break;

        case UIEventSubtypeRemoteControlPreviousTrack:
            [[NSNotificationCenter defaultCenter] postNotificationName:TDAudioPlayerPreviousTrackRequestNotification object:nil];
            break;

        default:
            break;
    }
}

#pragma mark - Timer Helpers

- (void)elapseTime
{
    self.elapsedTime++;
}

- (void)clearTimer
{
    [self.timer invalidate];
    self.timer = nil;
}

#pragma mark - Now Playing Info Helpers

- (void)changeAudioMetaInfo:(TDAudioMetaInfo *)meta
{
    [self setNowPlayingInfoWithMetaInfo:meta];

    if (!meta)
        return [[NSNotificationCenter defaultCenter] postNotificationName:TDAudioPlayerDidChangeAudioNotification object:nil];

    [[NSNotificationCenter defaultCenter] postNotificationName:TDAudioPlayerDidChangeAudioNotification object:nil userInfo:@{@"meta": meta}];
}

- (void)setNowPlayingInfoWithMetaInfo:(TDAudioMetaInfo *)info
{
    if (!info) {
        [MPNowPlayingInfoCenter defaultCenter].nowPlayingInfo = @{};
        return;
    }

    if (!self.nowPlayingMetaInfo)
        self.nowPlayingMetaInfo = [NSMutableDictionary dictionary];
    else
        [self.nowPlayingMetaInfo removeAllObjects];

    if (info.title) self.nowPlayingMetaInfo[MPMediaItemPropertyTitle] = info.title;
    if (info.artist) self.nowPlayingMetaInfo[MPMediaItemPropertyArtist] = info.artist;

    if (info.albumArtLarge) {
        MPMediaItemArtwork *artwork = [[MPMediaItemArtwork alloc] initWithImage:[UIImage imageWithData:[NSData dataWithContentsOfURL:[NSURL URLWithString:info.albumArtLarge]]]];
        if (artwork) self.nowPlayingMetaInfo[MPMediaItemPropertyArtwork] = artwork;
    }

    if (info.duration) self.nowPlayingMetaInfo[MPMediaItemPropertyPlaybackDuration] = info.duration;

    [self setNowPlayingInfoWithPlaybackRate:@0];
}

- (void)setNowPlayingInfoWithPlaybackRate:(NSNumber *)rate
{
    if (!self.nowPlayingMetaInfo) return;

    self.nowPlayingMetaInfo[MPNowPlayingInfoPropertyPlaybackRate] = rate;
    self.nowPlayingMetaInfo[MPNowPlayingInfoPropertyElapsedPlaybackTime] = @(self.elapsedTime);

    [MPNowPlayingInfoCenter defaultCenter].nowPlayingInfo = self.nowPlayingMetaInfo;
}

#pragma mark - Notification Handlers

- (void)audioSessionDidInterrupt:(NSNotification *)notification
{
    NSUInteger type = [notification.userInfo[AVAudioSessionInterruptionTypeKey] unsignedIntegerValue];

    if (type == AVAudioSessionInterruptionTypeBegan)
        [self pause];
}

- (void)audioSessionDidChangeRoute:(NSNotification *)notification
{
    NSUInteger reason = [notification.userInfo[AVAudioSessionRouteChangeReasonKey] unsignedIntegerValue];

    if (reason == AVAudioSessionRouteChangeReasonOldDeviceUnavailable)
        [self pause];
}

- (void)audioDidStartPlaying
{
    [self setNowPlayingInfoWithPlaybackRate:@1];
    self.timer = [NSTimer scheduledTimerWithTimeInterval:1.0f target:self selector:@selector(elapseTime) userInfo:nil repeats:YES];
    self.state = TDAudioPlayerStatePlaying;
}

#pragma mark - Cleanup

- (void)dealloc
{
    [[NSNotificationCenter defaultCenter] removeObserver:self];
}

@end
