/*
 * Copyright (c) 2017 Apple Inc. All Rights Reserved.
 *
 * @APPLE_LICENSE_HEADER_START@
 *
 * This file contains Original Code and/or Modifications of Original Code
 * as defined in and that are subject to the Apple Public Source License
 * Version 2.0 (the 'License'). You may not use this file except in
 * compliance with the License. Please obtain a copy of the License at
 * http://www.opensource.apple.com/apsl/ and read it before using this
 * file.
 *
 * The Original Code and all software distributed under the License are
 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
 * Please see the License for the specific language governing rights and
 * limitations under the License.
 *
 * @APPLE_LICENSE_HEADER_END@
 */

#if __OBJC2__

#import <Foundation/NSXPCConnection_Private.h>
#import <xpc/xpc.h>

#import <Security/SecItemPriv.h>

#import "keychain/ot/OTControl.h"
#import "keychain/ot/OTControlProtocol.h"
#import "keychain/ot/OctagonControlServer.h"

#include <security_utilities/debugging.h>

@interface OTControl ()
@property NSXPCConnection *connection;
@end

@implementation OTControl

- (instancetype)initWithConnection:(NSXPCConnection*)connection {
    if(self = [super init]) {
        _connection = connection;
    }
    return self;
}

- (void)restore:(NSString *)contextID dsid:(NSString *)dsid secret:(NSData*)secret escrowRecordID:(NSString*)escrowRecordID
         reply:(void (^)(NSData* signingKeyData, NSData* encryptionKeyData, NSError* _Nullable error))reply
{
    [[self.connection remoteObjectProxyWithErrorHandler: ^(NSError* error) {
        reply(nil, nil, error);
    }] restore:contextID dsid:dsid secret:secret escrowRecordID:escrowRecordID reply:^(NSData* signingKeyData, NSData* encryptionKeyData, NSError *error) {
        reply(signingKeyData, encryptionKeyData, error);
    }];

}

-(void)reset:(void (^)(BOOL result, NSError* _Nullable error))reply
{
    [[self.connection remoteObjectProxyWithErrorHandler: ^(NSError* error) {
        reply(NO, error);
    }] reset:^(BOOL result, NSError * _Nullable error) {
        reply(result, error);
    }];
}

- (void)signingKey:(void (^)(NSData* result, NSError* _Nullable error))reply
{
    [[self.connection remoteObjectProxyWithErrorHandler: ^(NSError* error) {
        reply(nil, error);
    }] octagonSigningPublicKey:^(NSData *signingKey, NSError * _Nullable error) {
        reply(signingKey, error);
    }];

}

- (void)encryptionKey:(void (^)(NSData* result, NSError* _Nullable error))reply
{
    [[self.connection remoteObjectProxyWithErrorHandler: ^(NSError* error) {
        reply(nil, error);
    }] octagonEncryptionPublicKey:^(NSData *encryptionKey, NSError * _Nullable error) {
        reply(encryptionKey, error);
    }];

}

- (void)listOfRecords:(void (^)(NSArray* list, NSError* _Nullable error))reply
{
    [[self.connection remoteObjectProxyWithErrorHandler: ^(NSError* error) {
        reply(nil, error);
    }] listOfEligibleBottledPeerRecords:^(NSArray *list, NSError * _Nullable error) {
        reply(list, error);
    }];
    
}

- (void)signIn:(NSString*)dsid reply:(void (^)(BOOL result, NSError * _Nullable error))reply{
    [[self.connection remoteObjectProxyWithErrorHandler: ^(NSError* error) {
        reply(NO, error);
    }] signIn:dsid reply:^(BOOL result, NSError * _Nullable error) {
        reply(result, error);
    }];
}

- (void)signOut:(void (^)(BOOL result, NSError * _Nullable error))reply
{
    [[self.connection remoteObjectProxyWithErrorHandler: ^(NSError* error) {
        reply(NO, error);
    }] signOut:^(BOOL result, NSError * _Nullable error) {
        reply(result, error);
    }];
    
}


- (void)preflightBottledPeer:(NSString*)contextID
                        dsid:(NSString*)dsid
                       reply:(void (^)(NSData* _Nullable entropy,
                                       NSString* _Nullable bottleID,
                                       NSData* _Nullable signingPublicKey,
                                       NSError* _Nullable error))reply
{
    [[self.connection remoteObjectProxyWithErrorHandler: ^(NSError* error) {
        reply(nil, nil, nil, error);
    }] preflightBottledPeer:contextID dsid:dsid reply:^(NSData* _Nullable entropy,
                                                        NSString* _Nullable bottleID,
                                                        NSData* _Nullable signingPublicKey,
                                                        NSError* _Nullable error) {
        reply(entropy, bottleID, signingPublicKey, error);
    }];
}

- (void)launchBottledPeer:(NSString*)contextID
                 bottleID:(NSString*)bottleID
                    reply:(void (^ _Nullable)(NSError* _Nullable))reply
{
    [[self.connection remoteObjectProxyWithErrorHandler: ^(NSError* error) {
        reply(error);
    }] launchBottledPeer:contextID bottleID:bottleID reply:^(NSError * _Nullable error) {
        reply(error);
    }];
}

- (void)scrubBottledPeer:(NSString*)contextID
                bottleID:(NSString*)bottleID
                   reply:(void (^ _Nullable)(NSError* _Nullable))reply
{
    [[self.connection remoteObjectProxyWithErrorHandler: ^(NSError* error) {
        reply(error);
    }] scrubBottledPeer:contextID bottleID:bottleID reply:reply];
}

+ (OTControl*)controlObject:(NSError* __autoreleasing *)error {

    NSXPCConnection* connection = [[NSXPCConnection alloc] initWithMachServiceName:@(kSecuritydOctagonServiceName) options:0];
    
    if (connection == nil) {
        if(error) {
            *error =  [NSError errorWithDomain:@"securityd" code:-1 userInfo:@{NSLocalizedDescriptionKey: @"Couldn't create connection (no reason given)"}];
        }
        return nil;
    }
    
    NSXPCInterface *interface = OTSetupControlProtocol([NSXPCInterface interfaceWithProtocol:@protocol(OTControlProtocol)]);
    connection.remoteObjectInterface = interface;
    [connection resume];
    
    OTControl* c = [[OTControl alloc] initWithConnection:connection];
    return c;
}

@end

#endif // __OBJC2__
