/*
 * This file is part of mpv.
 *
 * mpv is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2.1 of the License, or (at your option) any later version.
 *
 * mpv is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with mpv.  If not, see <http://www.gnu.org/licenses/>.
 */

#include "player/client.h"
#import "macosx_touchbar.h"

@implementation TouchBar

@synthesize app = _app;
@synthesize touchbarItems = _touchbar_items;
@synthesize duration = _duration;
@synthesize position = _position;
@synthesize pause = _pause;

- (id)init
{
    if (self = [super init]) {
        self.touchbarItems = @{
            seekBar: [NSMutableDictionary dictionaryWithDictionary:@{
                @"type": @"slider",
                @"name": @"Seek Bar",
                @"cmd":  @"seek %f absolute-percent"
            }],
            play: [NSMutableDictionary dictionaryWithDictionary:@{
                @"type":     @"button",
                @"name":     @"Play Button",
                @"cmd":      @"cycle pause",
                @"image":    [NSImage imageNamed:NSImageNameTouchBarPauseTemplate],
                @"imageAlt": [NSImage imageNamed:NSImageNameTouchBarPlayTemplate]
            }],
            previousItem: [NSMutableDictionary dictionaryWithDictionary:@{
                @"type":  @"button",
                @"name":  @"Previous Playlist Item",
                @"cmd":   @"playlist-prev",
                @"image": [NSImage imageNamed:NSImageNameTouchBarGoBackTemplate]
            }],
            nextItem: [NSMutableDictionary dictionaryWithDictionary:@{
                @"type":  @"button",
                @"name":  @"Next Playlist Item",
                @"cmd":   @"playlist-next",
                @"image": [NSImage imageNamed:NSImageNameTouchBarGoForwardTemplate]
            }],
            previousChapter: [NSMutableDictionary dictionaryWithDictionary:@{
                @"type":  @"button",
                @"name":  @"Previous Chapter",
                @"cmd":   @"add chapter -1",
                @"image": [NSImage imageNamed:NSImageNameTouchBarSkipBackTemplate]
            }],
            nextChapter: [NSMutableDictionary dictionaryWithDictionary:@{
                @"type":  @"button",
                @"name":  @"Next Chapter",
                @"cmd":   @"add chapter 1",
                @"image": [NSImage imageNamed:NSImageNameTouchBarSkipAheadTemplate]
            }],
            cycleAudio: [NSMutableDictionary dictionaryWithDictionary:@{
                @"type":  @"button",
                @"name":  @"Cycle Audio",
                @"cmd":   @"cycle audio",
                @"image": [NSImage imageNamed:NSImageNameTouchBarAudioInputTemplate]
            }],
            cycleSubtitle: [NSMutableDictionary dictionaryWithDictionary:@{
                @"type":  @"button",
                @"name":  @"Cycle Subtitle",
                @"cmd":   @"cycle sub",
                @"image": [NSImage imageNamed:NSImageNameTouchBarComposeTemplate]
            }],
            currentPosition: [NSMutableDictionary dictionaryWithDictionary:@{
                @"type": @"text",
                @"name": @"Current Position"
            }],
            timeLeft: [NSMutableDictionary dictionaryWithDictionary:@{
                @"type": @"text",
                @"name": @"Time Left"
            }]
        };

        [self addObserver:self forKeyPath:@"visible" options:0 context:nil];
    }
    return self;
}

- (nullable NSTouchBarItem *)touchBar:(NSTouchBar *)touchBar
                makeItemForIdentifier:(NSTouchBarItemIdentifier)identifier
{
    if ([self.touchbarItems[identifier][@"type"] isEqualToString:@"slider"]) {
        NSCustomTouchBarItem *tbItem = [[NSCustomTouchBarItem alloc] initWithIdentifier:identifier];
        NSSlider *slider = [NSSlider sliderWithTarget:self action:@selector(seekbarChanged:)];
        slider.minValue = 0.0f;
        slider.maxValue = 100.0f;
        tbItem.view = slider;
        tbItem.customizationLabel = self.touchbarItems[identifier][@"name"];
        [self.touchbarItems[identifier] setObject:slider forKey:@"view"];
        [self.touchbarItems[identifier] setObject:tbItem forKey:@"tbItem"];
        [tbItem addObserver:self forKeyPath:@"visible" options:0 context:nil];
        return tbItem;
    } else if ([self.touchbarItems[identifier][@"type"] isEqualToString:@"button"]) {
        NSCustomTouchBarItem *tbItem = [[NSCustomTouchBarItem alloc] initWithIdentifier:identifier];
        NSImage *tbImage = self.touchbarItems[identifier][@"image"];
        NSButton *tbButton = [NSButton buttonWithImage:tbImage target:self action:@selector(buttonAction:)];
        tbItem.view = tbButton;
        tbItem.customizationLabel = self.touchbarItems[identifier][@"name"];
        [self.touchbarItems[identifier] setObject:tbButton forKey:@"view"];
        [self.touchbarItems[identifier] setObject:tbItem forKey:@"tbItem"];
        [tbItem addObserver:self forKeyPath:@"visible" options:0 context:nil];
        return tbItem;
    } else if ([self.touchbarItems[identifier][@"type"] isEqualToString:@"text"]) {
        NSCustomTouchBarItem *tbItem = [[NSCustomTouchBarItem alloc] initWithIdentifier:identifier];
        NSTextField *tbText = [NSTextField labelWithString:@"0:00"];
        tbText.alignment = NSTextAlignmentCenter;
        tbItem.view = tbText;
        tbItem.customizationLabel = self.touchbarItems[identifier][@"name"];
        [self.touchbarItems[identifier] setObject:tbText forKey:@"view"];
        [self.touchbarItems[identifier] setObject:tbItem forKey:@"tbItem"];
        [tbItem addObserver:self forKeyPath:@"visible" options:0 context:nil];
        return tbItem;
    }

    return nil;
}

- (void)observeValueForKeyPath:(NSString *)keyPath
                      ofObject:(id)object
                        change:(NSDictionary<NSKeyValueChangeKey,id> *)change
                       context:(void *)context {
    if ([keyPath isEqualToString:@"visible"]) {
        NSNumber *visible = [object valueForKey:@"visible"];
        if (visible.boolValue) {
            [self updateTouchBarTimeItems];
            [self updatePlayButton];
        }
    }
}

- (void)updateTouchBarTimeItems
{
    if (!self.isVisible)
        return;

    [self updateSlider];
    [self updateTimeLeft];
    [self updateCurrentPosition];
}

- (void)updateSlider
{
    NSCustomTouchBarItem *tbItem = self.touchbarItems[seekBar][@"tbItem"];
    if (!tbItem.visible)
        return;

    NSSlider *seekSlider = self.touchbarItems[seekBar][@"view"];

    if (self.duration <= 0) {
        seekSlider.enabled = NO;
        seekSlider.doubleValue = 0;
    } else {
        seekSlider.enabled = YES;
        if (!seekSlider.highlighted)
            seekSlider.doubleValue = (self.position/self.duration)*100;
    }
}

- (void)updateTimeLeft
{
    NSCustomTouchBarItem *tbItem = self.touchbarItems[timeLeft][@"tbItem"];
    if (!tbItem.visible)
        return;

    NSTextField *timeLeftItem = self.touchbarItems[timeLeft][@"view"];

    [self removeConstraintForIdentifier:timeLeft];
    if (self.duration <= 0) {
        timeLeftItem.stringValue = @"";
    } else {
        int left = (int)(floor(self.duration)-floor(self.position));
        NSString *leftFormat = [self formatTime:left];
        NSString *durFormat = [self formatTime:self.duration];
        timeLeftItem.stringValue = [NSString stringWithFormat:@"-%@", leftFormat];
        [self applyConstraintFromString:[NSString stringWithFormat:@"-%@", durFormat]
                          forIdentifier:timeLeft];
    }
}

- (void)updateCurrentPosition
{
    NSCustomTouchBarItem *tbItem = self.touchbarItems[currentPosition][@"tbItem"];
    if (!tbItem.visible)
        return;

    NSTextField *curPosItem = self.touchbarItems[currentPosition][@"view"];
    NSString *posFormat = [self formatTime:(int)floor(self.position)];
    curPosItem.stringValue = posFormat;

    [self removeConstraintForIdentifier:currentPosition];
    if (self.duration <= 0) {
        [self applyConstraintFromString:[self formatTime:self.position]
                          forIdentifier:currentPosition];
    } else {
        NSString *durFormat = [self formatTime:self.duration];
        [self applyConstraintFromString:durFormat forIdentifier:currentPosition];
    }
}

- (void)updatePlayButton
{
    NSCustomTouchBarItem *tbItem = self.touchbarItems[play][@"tbItem"];
    if (!self.isVisible || !tbItem.visible)
        return;

    NSButton *playButton = self.touchbarItems[play][@"view"];
    if (self.pause) {
        playButton.image = self.touchbarItems[play][@"imageAlt"];
    } else {
        playButton.image = self.touchbarItems[play][@"image"];
    }
}

- (void)buttonAction:(NSButton *)sender
{
    NSString *identifier = [self getIdentifierFromView:sender];
    [self.app queueCommand:(char *)[self.touchbarItems[identifier][@"cmd"] UTF8String]];
}

- (void)seekbarChanged:(NSSlider *)slider
{
    NSString *identifier = [self getIdentifierFromView:slider];
    NSString *seek = [NSString stringWithFormat:
        self.touchbarItems[identifier][@"cmd"], slider.doubleValue];
    [self.app queueCommand:(char *)[seek UTF8String]];
}

- (NSString *)formatTime:(int)time
{
    int seconds = time % 60;
    int minutes = (time / 60) % 60;
    int hours = time / (60 * 60);

    NSString *stime = hours > 0 ? [NSString stringWithFormat:@"%d:", hours] : @"";
    stime = (stime.length > 0 || minutes > 9) ?
        [NSString stringWithFormat:@"%@%02d:", stime, minutes] :
        [NSString stringWithFormat:@"%d:", minutes];
    stime = [NSString stringWithFormat:@"%@%02d", stime, seconds];

    return stime;
}

- (void)removeConstraintForIdentifier:(NSTouchBarItemIdentifier)identifier
{
    NSTextField *field = self.touchbarItems[identifier][@"view"];
    [field removeConstraint:self.touchbarItems[identifier][@"constrain"]];
}

- (void)applyConstraintFromString:(NSString *)string
                    forIdentifier:(NSTouchBarItemIdentifier)identifier
{
    NSTextField *field = self.touchbarItems[identifier][@"view"];
    if (field) {
        NSString *fString = [[string componentsSeparatedByCharactersInSet:
            [NSCharacterSet decimalDigitCharacterSet]] componentsJoinedByString:@"0"];
        NSTextField *textField = [NSTextField labelWithString:fString];
        NSSize size = [textField frame].size;

        NSLayoutConstraint *con =
            [NSLayoutConstraint constraintWithItem:field
                                         attribute:NSLayoutAttributeWidth
                                         relatedBy:NSLayoutRelationEqual
                                            toItem:nil
                                         attribute:NSLayoutAttributeNotAnAttribute
                                        multiplier:1.0
                                          constant:(int)ceil(size.width*1.1)];
        [field addConstraint:con];
        [self.touchbarItems[identifier] setObject:con forKey:@"constrain"];
    }
}

- (NSString *)getIdentifierFromView:(id)view
{
    NSString *identifier;
    for (identifier in self.touchbarItems)
        if([self.touchbarItems[identifier][@"view"] isEqual:view])
            break;
    return identifier;
}

- (void)processEvent:(struct mpv_event *)event
{
    switch (event->event_id) {
    case MPV_EVENT_END_FILE: {
        self.position = 0;
        self.duration = 0;
        break;
    }
    case MPV_EVENT_PROPERTY_CHANGE: {
        [self handlePropertyChange:(mpv_event_property *)event->data];
        break;
    }
    }
}

- (void)handlePropertyChange:(struct mpv_event_property *)property
{
    NSString *name = [NSString stringWithUTF8String:property->name];
    mpv_format format = property->format;

    if ([name isEqualToString:@"time-pos"] && format == MPV_FORMAT_DOUBLE) {
        double newPosition = *(double *)property->data;
        newPosition = newPosition < 0 ? 0 : newPosition;
        if ((int)(floor(newPosition) - floor(self.position)) != 0) {
            self.position = newPosition;
            [self updateTouchBarTimeItems];
        }
    } else if ([name isEqualToString:@"duration"] && format == MPV_FORMAT_DOUBLE) {
        self.duration = *(double *)property->data;
        [self updateTouchBarTimeItems];
    } else if ([name isEqualToString:@"pause"] && format == MPV_FORMAT_FLAG) {
        self.pause = *(int *)property->data;
        [self updatePlayButton];
    }
}

@end
