//
// Copyright 2019 Signal Messenger, LLC
// SPDX-License-Identifier: AGPL-3.0-only
//

#import "SDSCrossProcess.h"
#import "DarwinNotificationCenter.h"
#import <SignalCoreKit/OWSAsserts.h>
#import <SignalServiceKit/SignalServiceKit-Swift.h>

NS_ASSUME_NONNULL_BEGIN

// NOTE: CFNotificationCenterGetDarwinNotifyCenter() might offer a better / equivalent solution,
//       but it wasn't working for me and so I moved on.

static pid_t localPid(void)
{
    static dispatch_once_t onceToken;
    static pid_t pid;
    dispatch_once(&onceToken, ^{ pid = getpid(); });
    return pid;
}

#pragma mark -

@interface SDSCrossProcess ()

@property (nonatomic) int notifyToken;

@end

#pragma mark -

@implementation SDSCrossProcess

- (id)init
{
    if (self = [super init]) {
        self.notifyToken = DarwinNotificationInvalidObserver;

        [self start];
    }
    return self;
}

- (void)dealloc
{
    [self stop];
}

- (void)start
{
    [self stop];

    __weak SDSCrossProcess *weakSelf = self;
    self.notifyToken =
        [DarwinNotificationCenter addObserverForName:DarwinNotificationName.sdsCrossProcess
                                               queue:dispatch_get_main_queue()
                                          usingBlock:^(int token) { [weakSelf handleNotification:token]; }];
}

- (void)handleNotification:(int)token
{
    OWSAssertIsOnMainThread();

    uint64_t fromPid = [DarwinNotificationCenter getStateForObserver:token];
    BOOL isLocal = fromPid == (uint64_t)localPid();
    if (isLocal) {
        return;
    }

    if (self.callback) {
        self.callback();
    }
}

- (void)stop
{
    if ([DarwinNotificationCenter isValidObserver:self.notifyToken]) {
        [DarwinNotificationCenter removeObserver:self.notifyToken];
    }
    self.notifyToken = DarwinNotificationInvalidObserver;
}

- (void)notifyChangedAsync
{
    dispatch_async(dispatch_get_main_queue(), ^{ [self notifyChanged]; });
}

- (void)notifyChanged
{
    OWSAssertIsOnMainThread();

    if (![DarwinNotificationCenter isValidObserver:self.notifyToken]) {
        [self start];
    }

    [DarwinNotificationCenter setState:(uint64_t)localPid() forObserver:self.notifyToken];
    [DarwinNotificationCenter postNotificationName:DarwinNotificationName.sdsCrossProcess];
}

@end

NS_ASSUME_NONNULL_END
