/* Copyright (c) 2013, Felix Paul Kühne and VideoLAN
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without 
 * modification, are permitted provided that the following conditions are met:
 *
 * 1. Redistributions of source code must retain the above copyright notice, 
 *    this list of conditions and the following disclaimer.
 *
 * 2. Redistributions in binary form must reproduce the above copyright notice,
 *    this list of conditions and the following disclaimer in the documentation
 *    and/or other materials provided with the distribution.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 
 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
 * POSSIBILITY OF SUCH DAMAGE. */

#import "VDLPlaybackViewController.h"
#import <AVFoundation/AVFoundation.h>

@interface VDLPlaybackViewController () <UIGestureRecognizerDelegate, UIActionSheetDelegate, VLCMediaPlayerDelegate>
{
    VLCMediaPlayer *_mediaplayer;
    BOOL _setPosition;
    BOOL _displayRemainingTime;
    int _currentAspectRatioMask;
    NSArray *_aspectRatios;
    UIActionSheet *_audiotrackActionSheet;
    UIActionSheet *_subtitleActionSheet;
    NSURL *_url;
    NSTimer *_idleTimer;
}

@end

@implementation VDLPlaybackViewController

- (void)viewDidLoad
{
    [super viewDidLoad];

    /* fix-up UI */
    self.wantsFullScreenLayout = YES;
    [[UIApplication sharedApplication] setStatusBarStyle:UIStatusBarStyleLightContent];

    /* we want to influence the system volume */
    [[AVAudioSession sharedInstance] setDelegate:self];

    /* populate array of supported aspect ratios (there are more!) */
    _aspectRatios = @[@"DEFAULT", @"FILL_TO_SCREEN", @"4:3", @"16:9", @"16:10", @"2.21:1"];

    /* fix-up the UI */
    CGRect rect = self.toolbar.frame;
    rect.size.height += 20.;
    self.toolbar.frame = rect;
    [self.timeDisplay setTitle:@"" forState:UIControlStateNormal];

    /* this looks a bit weird, but let's try to support iOS 5 */
    UISlider *volumeSlider = nil;
    for (id aView in self.volumeView.subviews){
        if ([[[aView class] description] isEqualToString:@"MPVolumeSlider"]){
            volumeSlider = (UISlider *)aView;
            break;
        }
    }
    [volumeSlider addTarget:self
                     action:@selector(volumeSliderAction:)
           forControlEvents:UIControlEventValueChanged];

    /* setup gesture recognizer to toggle controls' visibility */
    _movieView.userInteractionEnabled = NO;
    UITapGestureRecognizer *tapOnVideoRecognizer = [[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(toggleControlsVisible)];
    tapOnVideoRecognizer.delegate = self;
    [self.view addGestureRecognizer:tapOnVideoRecognizer];
}

- (void)playMediaFromURL:(NSURL*)theURL
{
    _url = theURL;
}

- (IBAction)playandPause:(id)sender
{
    if (_mediaplayer.isPlaying)
        [_mediaplayer pause];

    [_mediaplayer play];
}

- (IBAction)closePlayback:(id)sender
{
    [self.navigationController dismissViewControllerAnimated:YES completion:nil];
}

- (void)viewWillAppear:(BOOL)animated
{
    [super viewWillAppear:animated];

    [self.navigationController setNavigationBarHidden:YES animated:YES];

    /* setup the media player instance, give it a delegate and something to draw into */
    _mediaplayer = [[VLCMediaPlayer alloc] init];
    _mediaplayer.delegate = self;
    _mediaplayer.drawable = self.movieView;

    /* enable debug logging from libvlc here */
    _mediaplayer.libraryInstance.debugLogging = YES;

    /* listen for notifications from the player */
    [_mediaplayer addObserver:self forKeyPath:@"time" options:0 context:nil];
    [_mediaplayer addObserver:self forKeyPath:@"remainingTime" options:0 context:nil];

    /* create a media object and give it to the player */
    _mediaplayer.media = [VLCMedia mediaWithURL:_url];

    [_mediaplayer play];

    if (self.controllerPanel.hidden)
        [self toggleControlsVisible];

    [self _resetIdleTimer];
}


- (void)viewWillDisappear:(BOOL)animated
{
    [super viewWillDisappear:animated];

    if (_mediaplayer) {
        @try {
            [_mediaplayer removeObserver:self forKeyPath:@"time"];
            [_mediaplayer removeObserver:self forKeyPath:@"remainingTime"];
        }
        @catch (NSException *exception) {
            NSLog(@"we weren't an observer yet");
        }

        if (_mediaplayer.media)
            [_mediaplayer stop];

        if (_mediaplayer)
            _mediaplayer = nil;
    }

    if (_idleTimer) {
        [_idleTimer invalidate];
        _idleTimer = nil;
    }

    [self.navigationController setNavigationBarHidden:NO animated:YES];
    [[UIApplication sharedApplication] setStatusBarHidden:NO withAnimation:UIStatusBarAnimationFade];
}

- (UIResponder *)nextResponder
{
    [self _resetIdleTimer];
    return [super nextResponder];
}

- (IBAction)positionSliderAction:(UISlider *)sender
{
    [self _resetIdleTimer];

    /* we need to limit the number of events sent by the slider, since otherwise, the user
     * wouldn't see the I-frames when seeking on current mobile devices. This isn't a problem
     * within the Simulator, but especially on older ARMv7 devices, it's clearly noticeable. */
    [self performSelector:@selector(_setPositionForReal) withObject:nil afterDelay:0.3];
    _setPosition = NO;
}

- (void)_setPositionForReal
{
    if (!_setPosition) {
        _mediaplayer.position = _positionSlider.value;
        _setPosition = YES;
    }
}

- (IBAction)positionSliderDrag:(id)sender
{
    [self _resetIdleTimer];
}

- (IBAction)volumeSliderAction:(id)sender
{
    [self _resetIdleTimer];
}

- (void)mediaPlayerStateChanged:(NSNotification *)aNotification
{
    VLCMediaPlayerState currentState = _mediaplayer.state;

    if (currentState == VLCMediaPlayerStateBuffering) {
        NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
        [_mediaplayer performSelector:@selector(setTextRendererFont:) withObject:[defaults objectForKey:kVLCSettingSubtitlesFont]];
        [_mediaplayer performSelector:@selector(setTextRendererFontSize:) withObject:[defaults objectForKey:kVLCSettingSubtitlesFontSize]];
        [_mediaplayer performSelector:@selector(setTextRendererFontColor:) withObject:[defaults objectForKey:kVLCSettingSubtitlesFontColor]];
        [_mediaplayer performSelector:@selector(setTextRendererFontForceBold:) withObject:[defaults objectForKey:kVLCSettingSubtitlesBoldFont]];
    }

    /* distruct view controller on error */
    if (currentState == VLCMediaPlayerStateError)
        [self performSelector:@selector(closePlayback:) withObject:nil afterDelay:2.];

    /* or if playback ended */
    if (currentState == VLCMediaPlayerStateEnded || currentState == VLCMediaPlayerStateStopped)
        [self performSelector:@selector(closePlayback:) withObject:nil afterDelay:2.];

    [self.playPauseButton setTitle:[_mediaplayer isPlaying]? @"Pause" : @"Play" forState:UIControlStateNormal];
}

- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context
{
    self.positionSlider.value = [_mediaplayer position];

    if (_displayRemainingTime)
        [self.timeDisplay setTitle:[[_mediaplayer remainingTime] stringValue] forState:UIControlStateNormal];
    else
        [self.timeDisplay setTitle:[[_mediaplayer time] stringValue] forState:UIControlStateNormal];
}

- (IBAction)toggleTimeDisplay:(id)sender
{
    [self _resetIdleTimer];
    _displayRemainingTime = !_displayRemainingTime;
}

- (void)toggleControlsVisible
{
    BOOL controlsHidden = !self.controllerPanel.hidden;
    self.controllerPanel.hidden = controlsHidden;
    self.toolbar.hidden = controlsHidden;
    [[UIApplication sharedApplication] setStatusBarHidden:controlsHidden withAnimation:UIStatusBarAnimationFade];
}

- (void)_resetIdleTimer
{
    if (!_idleTimer)
        _idleTimer = [NSTimer scheduledTimerWithTimeInterval:5.
                                                      target:self
                                                    selector:@selector(idleTimerExceeded)
                                                    userInfo:nil
                                                     repeats:NO];
    else {
        if (fabs([_idleTimer.fireDate timeIntervalSinceNow]) < 5.)
            [_idleTimer setFireDate:[NSDate dateWithTimeIntervalSinceNow:5.]];
    }
}

- (void)idleTimerExceeded
{
    _idleTimer = nil;

    if (!self.controllerPanel.hidden)
        [self toggleControlsVisible];
}

- (IBAction)switchVideoDimensions:(id)sender
{
    [self _resetIdleTimer];

    NSUInteger count = [_aspectRatios count];

    if (_currentAspectRatioMask + 1 > count - 1) {
        _mediaplayer.videoAspectRatio = NULL;
        _mediaplayer.videoCropGeometry = NULL;
        _currentAspectRatioMask = 0;
        NSLog(@"crop disabled");
    } else {
        _currentAspectRatioMask++;

        if ([_aspectRatios[_currentAspectRatioMask] isEqualToString:@"FILL_TO_SCREEN"]) {
            UIScreen *screen = [UIScreen mainScreen];
            float f_ar = screen.bounds.size.width / screen.bounds.size.height;

            if (f_ar == (float)(640./1136.)) // iPhone 5 aka 16:9.01
                _mediaplayer.videoCropGeometry = "16:9";
            else if (f_ar == (float)(2./3.)) // all other iPhones
                _mediaplayer.videoCropGeometry = "16:10"; // libvlc doesn't support 2:3 crop
            else if (f_ar == .75) // all iPads
                _mediaplayer.videoCropGeometry = "4:3";
            else if (f_ar == .5625) // AirPlay
                _mediaplayer.videoCropGeometry = "16:9";
            else
                NSLog(@"unknown screen format %f, can't crop", f_ar);

            NSLog(@"FILL_TO_SCREEN");
            return;
        }

        _mediaplayer.videoCropGeometry = NULL;
        _mediaplayer.videoAspectRatio = (char *)[_aspectRatios[_currentAspectRatioMask] UTF8String];
        NSLog(@"crop switched to %@", _aspectRatios[_currentAspectRatioMask]);
    }
}

- (IBAction)switchAudioTrack:(id)sender
{
    _audiotrackActionSheet = [[UIActionSheet alloc] initWithTitle:@"audio track selector" delegate:self cancelButtonTitle:nil destructiveButtonTitle:nil otherButtonTitles: nil];
    NSArray *audioTracks = [_mediaplayer audioTrackNames];
    NSArray *audioTrackIndexes = [_mediaplayer audioTrackIndexes];

    NSUInteger count = [audioTracks count];
    for (NSUInteger i = 0; i < count; i++) {
        NSString *indexIndicator = ([audioTrackIndexes[i] intValue] == [_mediaplayer currentAudioTrackIndex])? @"\u2713": @"";
        NSString *buttonTitle = [NSString stringWithFormat:@"%@ %@", indexIndicator, audioTracks[i]];
        [_audiotrackActionSheet addButtonWithTitle:buttonTitle];
    }

    [_audiotrackActionSheet addButtonWithTitle:@"Cancel"];
    [_audiotrackActionSheet setCancelButtonIndex:[_audiotrackActionSheet numberOfButtons] - 1];
    [_audiotrackActionSheet showInView:self.audioSwitcherButton];
}

- (IBAction)switchSubtitleTrack:(id)sender
{
    NSArray *spuTracks = [_mediaplayer videoSubTitlesNames];
    NSArray *spuTrackIndexes = [_mediaplayer videoSubTitlesIndexes];

    NSUInteger count = [spuTracks count];
    if (count <= 1)
        return;
    _subtitleActionSheet = [[UIActionSheet alloc] initWithTitle:@"subtitle track selector" delegate:self cancelButtonTitle:nil destructiveButtonTitle:nil otherButtonTitles: nil];

    for (NSUInteger i = 0; i < count; i++) {
        NSString *indexIndicator = ([spuTrackIndexes[i] intValue] == [_mediaplayer currentVideoSubTitleIndex])? @"\u2713": @"";
        NSString *buttonTitle = [NSString stringWithFormat:@"%@ %@", indexIndicator, spuTracks[i]];
        [_subtitleActionSheet addButtonWithTitle:buttonTitle];
    }

    [_subtitleActionSheet addButtonWithTitle:@"Cancel"];
    [_subtitleActionSheet setCancelButtonIndex:[_subtitleActionSheet numberOfButtons] - 1];
    [_subtitleActionSheet showInView: self.subtitleSwitcherButton];
}

- (void)actionSheet:(UIActionSheet *)actionSheet clickedButtonAtIndex:(NSInteger)buttonIndex {
    if (buttonIndex == [actionSheet cancelButtonIndex])
        return;

    NSArray *indexArray;
    if (actionSheet == _subtitleActionSheet) {
        indexArray = _mediaplayer.videoSubTitlesIndexes;
        if (buttonIndex <= indexArray.count) {
            _mediaplayer.currentVideoSubTitleIndex = [indexArray[buttonIndex] intValue];
        }
    } else if (actionSheet == _audiotrackActionSheet) {
        indexArray = _mediaplayer.audioTrackIndexes;
        if (buttonIndex <= indexArray.count) {
            _mediaplayer.currentAudioTrackIndex = [indexArray[buttonIndex] intValue];
        }
    }
}

- (void)didReceiveMemoryWarning
{
    [super didReceiveMemoryWarning];
    // Dispose of any resources that can be recreated.
}

@end
