#import <Foundation/Foundation.h>
#import <React/RCTConvert.h>
#import <bbs-signatures/bls12381g2_key_pair.h>
#import <bbs-signatures/bbs_key_pair.h>
#import <bbs-signatures/bbs_signature.h>
#import <bbs-signatures/bbs_signature_proof.h>
#import "RnBbsSignatures.h"
#import "RnConvert.h"
#import "RnOperation.h"

@implementation RnBbsSignatures

RCT_EXPORT_MODULE()

//TODO check heap allocations are all free'd

RCT_EXPORT_METHOD(generateBls12381G2KeyPair:(NSDictionary *)request
                  withResolver:(RCTPromiseResolveBlock)resolve
                  withRejecter:(RCTPromiseRejectBlock)reject)
{
    RnOperation<NSDictionary*, NSDictionary*> *operation = [RnOperation new:^NSDictionary*(NSDictionary* request, NSError** error) {
        NSData *seed = nil;
        if ([request valueForKey:@"seed"] != nil) {
            seed = [RnConvert dataFromByteArray:[RCTConvert NSArray:request[@"seed"]]];
        }
        
        Bls12381G2KeyPair *keyPair = [[Bls12381G2KeyPair alloc] initWithSeed:seed
                                                                   withError:error];
        
        return [NSDictionary dictionaryWithObjects:@[[RnConvert byteArrayFromData:keyPair.publicKey],
                                                     [RnConvert byteArrayFromData:keyPair.secretKey]]
                                           forKeys:@[@"publicKey",
                                                     @"secretKey"]];
    }];
    
    [operation exec:request
       withResolver:resolve
       withRejecter:reject];
}

RCT_EXPORT_METHOD(bls12381G2PublicKeyToBbsPublicKey:(NSDictionary *)request
                  withResolver:(RCTPromiseResolveBlock)resolve
                  withRejecter:(RCTPromiseRejectBlock)reject)
{
    RnOperation<NSDictionary*, NSArray*> *operation = [RnOperation new:^NSArray*(NSDictionary* request, NSError** error) {
        NSInteger *messageCount = [RCTConvert NSInteger:request[@"messageCount"]];
        NSData *publicKey = [RnConvert dataFromByteArray:[RCTConvert NSArray:request[@"publicKey"]]];
        
        Bls12381G2KeyPair *blsKeyPair = [[Bls12381G2KeyPair alloc] initWithPublicKey:publicKey];
        
        BbsKeyPair *keyPair = [[BbsKeyPair alloc] initWithBls12381G2KeyPair:blsKeyPair
                                                               messageCount:messageCount
                                                                  withError:error];
        
        return [RnConvert byteArrayFromData:keyPair.publicKey];
    }];
    
    [operation exec:request
       withResolver:resolve
       withRejecter:reject];
}

RCT_EXPORT_METHOD(sign:(NSDictionary *)request
                  withResolver:(RCTPromiseResolveBlock)resolve
                  withRejecter:(RCTPromiseRejectBlock)reject)
{
    RnOperation<NSDictionary*, NSArray*> *operation = [RnOperation new:^NSArray*(NSDictionary* request, NSError** error) {
        NSData *publicKey = [RnConvert dataFromByteArray:[RCTConvert NSArray:request[@"publicKey"]]];
        NSData *secretKey = [RnConvert dataFromByteArray:[RCTConvert NSArray:request[@"secretKey"]]];
        NSInteger *messageCount = [RCTConvert NSInteger:request[@"messageCount"]];
        NSArray *messages = [RnConvert dataArrayFromArrayOfByteArrays:[RCTConvert NSArray:request[@"messages"]]];
        
        BbsKeyPair *keyPair = [[BbsKeyPair alloc] initWithData:publicKey
                                                  messageCount:messageCount
                                                  andSecretKey:secretKey];
        
        BbsSignature *signature = [[BbsSignature alloc] sign:keyPair
                                                    messages:messages
                                                   withError:error];
        
        return [RnConvert byteArrayFromData:signature.value];
    }];
    
    [operation exec:request
       withResolver:resolve
       withRejecter:reject];
}

RCT_EXPORT_METHOD(blsSign:(NSDictionary *)request
                  withResolver:(RCTPromiseResolveBlock)resolve
                  withRejecter:(RCTPromiseRejectBlock)reject)
{
    RnOperation<NSDictionary*, NSArray*> *operation = [RnOperation new:^NSArray*(NSDictionary* request, NSError** error) {
        NSData *secretKey = [RnConvert dataFromByteArray:[RCTConvert NSArray:request[@"secretKey"]]];
        NSArray *messages = [RnConvert dataArrayFromArrayOfByteArrays:[RCTConvert NSArray:request[@"messages"]]];
        Bls12381G2KeyPair *blsKeyPair = [[Bls12381G2KeyPair alloc] initWithSecretKey:secretKey withError:error];
        
        BbsKeyPair *keyPair = [[BbsKeyPair alloc] initWithBls12381G2KeyPair:blsKeyPair
                                                               messageCount:[messages count]
                                                                  withError:error];
        
        BbsSignature *signature = [[BbsSignature alloc] sign:keyPair
                                                    messages:messages
                                                   withError:error];
        
        return [RnConvert byteArrayFromData:signature.value];
    }];
    
    [operation exec:request
       withResolver:resolve
       withRejecter:reject];
}

RCT_EXPORT_METHOD(verify:(NSDictionary *)request
                  withResolver:(RCTPromiseResolveBlock)resolve
                  withRejecter:(RCTPromiseRejectBlock)reject)
{
    RnOperation<NSDictionary*, NSNumber*> *operation = [RnOperation new:^NSNumber*(NSDictionary* request, NSError** error) {
        NSData *publicKey = [RnConvert dataFromByteArray:[RCTConvert NSArray:request[@"publicKey"]]];
        NSInteger *messageCount = [RCTConvert NSInteger:request[@"messageCount"]];
        NSArray *messages = [RnConvert dataArrayFromArrayOfByteArrays:[RCTConvert NSArray:request[@"messages"]]];
        NSData *signatureBytes = [RnConvert dataFromByteArray:[RCTConvert NSArray:request[@"signature"]]];
        
        BbsKeyPair *keyPair = [[BbsKeyPair alloc] initWithData:publicKey
                                                  messageCount:messageCount];
        
        BbsSignature *signature = [[BbsSignature alloc] initWithBytes:signatureBytes
                                                            withError:error];
        
        return [[NSNumber alloc] initWithBool:[signature verify:keyPair
                                                       messages:messages
                                                      withError:error]];
    }];
    
    [operation exec:request
       withResolver:resolve
       withRejecter:reject];
}

RCT_EXPORT_METHOD(blsVerify:(NSDictionary *)request
                  withResolver:(RCTPromiseResolveBlock)resolve
                  withRejecter:(RCTPromiseRejectBlock)reject)
{
    RnOperation<NSDictionary*, NSNumber*> *operation = [RnOperation new:^NSNumber*(NSDictionary* request, NSError** error) {
        NSData *publicKey = [RnConvert dataFromByteArray:[RCTConvert NSArray:request[@"publicKey"]]];
        NSArray *messages = [RnConvert dataArrayFromArrayOfByteArrays:[RCTConvert NSArray:request[@"messages"]]];
        NSData *signatureBytes = [RnConvert dataFromByteArray:[RCTConvert NSArray:request[@"signature"]]];
        
        Bls12381G2KeyPair *blsKeyPair = [[Bls12381G2KeyPair alloc] initWithPublicKey:publicKey];
        
        BbsKeyPair *keyPair = [[BbsKeyPair alloc] initWithBls12381G2KeyPair:blsKeyPair
                                                               messageCount:[messages count]
                                                                  withError:error];
        BbsSignature *signature = [[BbsSignature alloc] initWithBytes:signatureBytes
                                                            withError:error];
        
        return [[NSNumber alloc] initWithBool:[signature verify:keyPair
                                                       messages:messages
                                                      withError:error]];
    }];
    
    [operation exec:request
       withResolver:resolve
       withRejecter:reject];
}

RCT_EXPORT_METHOD(createProof:(NSDictionary *)request
                  withResolver:(RCTPromiseResolveBlock)resolve
                  withRejecter:(RCTPromiseRejectBlock)reject)
{
    RnOperation<NSDictionary*, NSArray*> *operation = [RnOperation new:^NSArray*(NSDictionary* request, NSError** error) {
        NSData *nonce = [RnConvert dataFromByteArray:[RCTConvert NSArray:request[@"nonce"]]];
        NSArray *revealed = [RCTConvert NSArray:request[@"revealed"]];
        NSData *publicKey = [RnConvert dataFromByteArray:[RCTConvert NSArray:request[@"publicKey"]]];
        NSData *signatureBytes = [RnConvert dataFromByteArray:[RCTConvert NSArray:request[@"signature"]]];
        NSArray *messages = [RnConvert dataArrayFromArrayOfByteArrays:[RCTConvert NSArray:request[@"messages"]]];
        
        BbsKeyPair *keyPair = [[BbsKeyPair alloc] initWithData:publicKey
                                                  messageCount:[messages count]];
        
        BbsSignature *signature = [[BbsSignature alloc] initWithBytes:signatureBytes
                                                            withError:error];
        
        BbsSignatureProof *proof = [[BbsSignatureProof alloc] createProof:signature
                                                                  keyPair:keyPair
                                                                    nonce:nonce
                                                                 messages:messages
                                                                 revealed:revealed
                                                                withError:error];
        
        return [RnConvert byteArrayFromData:proof.value];
    }];
    
    [operation exec:request
       withResolver:resolve
       withRejecter:reject];
}

RCT_EXPORT_METHOD(blsCreateProof:(NSDictionary *)request
                  withResolver:(RCTPromiseResolveBlock)resolve
                  withRejecter:(RCTPromiseRejectBlock)reject)
{
    RnOperation<NSDictionary*, NSArray*> *operation = [RnOperation new:^NSArray*(NSDictionary* request, NSError** error) {
        NSData *nonce = [RnConvert dataFromByteArray:[RCTConvert NSArray:request[@"nonce"]]];
        NSArray *revealed = [RCTConvert NSArray:request[@"revealed"]];
        NSData *publicKey = [RnConvert dataFromByteArray:[RCTConvert NSArray:request[@"publicKey"]]];
        NSData *signatureBytes = [RnConvert dataFromByteArray:[RCTConvert NSArray:request[@"signature"]]];
        NSArray *messages = [RnConvert dataArrayFromArrayOfByteArrays:[RCTConvert NSArray:request[@"messages"]]];
        
        Bls12381G2KeyPair *blsKeyPair = [[Bls12381G2KeyPair alloc] initWithPublicKey:publicKey];
        
        BbsKeyPair *keyPair = [[BbsKeyPair alloc] initWithBls12381G2KeyPair:blsKeyPair
                                                               messageCount:[messages count]
                                                                  withError:error];
        
        BbsSignature *signature = [[BbsSignature alloc] initWithBytes:signatureBytes
                                                            withError:error];
        
        BbsSignatureProof *proof = [[BbsSignatureProof alloc] createProof:signature
                                                                  keyPair:keyPair
                                                                    nonce:nonce
                                                                 messages:messages
                                                                 revealed:revealed
                                                                withError:error];
        
        return [RnConvert byteArrayFromData:proof.value];
    }];
    
    [operation exec:request
       withResolver:resolve
       withRejecter:reject];
}

RCT_EXPORT_METHOD(verifyProof:(NSDictionary *)request
                  withResolver:(RCTPromiseResolveBlock)resolve
                  withRejecter:(RCTPromiseRejectBlock)reject)
{
    RnOperation<NSDictionary*, NSNumber*> *operation = [RnOperation new:^NSNumber*(NSDictionary* request, NSError** error) {
        NSData *nonce = [RnConvert dataFromByteArray:[RCTConvert NSArray:request[@"nonce"]]];
        NSData *publicKey = [RnConvert dataFromByteArray:[RCTConvert NSArray:request[@"publicKey"]]];
        NSData *proofBytes = [RnConvert dataFromByteArray:[RCTConvert NSArray:request[@"proof"]]];
        NSArray *messages = [RnConvert dataArrayFromArrayOfByteArrays:[RCTConvert NSArray:request[@"messages"]]];
        
        BbsKeyPair *keyPair = [[BbsKeyPair alloc] initWithData:publicKey
                                                  messageCount:[messages count]];
        
        BbsSignatureProof *proof = [[BbsSignatureProof alloc] initWithBytes:proofBytes withError:error];
        
        return [[NSNumber alloc] initWithBool:[proof verifyProof:keyPair
                                                        messages:messages
                                                           nonce:nonce
                                                       withError:error]];
    }];
    
    [operation exec:request
       withResolver:resolve
       withRejecter:reject];
}

RCT_EXPORT_METHOD(blsVerifyProof:(NSDictionary *)request
                  withResolver:(RCTPromiseResolveBlock)resolve
                  withRejecter:(RCTPromiseRejectBlock)reject)
{
    RnOperation<NSDictionary*, NSNumber*> *operation = [RnOperation new:^NSNumber*(NSDictionary* request, NSError** error) {
        NSData *nonce = [RnConvert dataFromByteArray:[RCTConvert NSArray:request[@"nonce"]]];
        NSData *publicKey = [RnConvert dataFromByteArray:[RCTConvert NSArray:request[@"publicKey"]]];
        NSData *proofBytes = [RnConvert dataFromByteArray:[RCTConvert NSArray:request[@"proof"]]];
        NSArray *messages = [RnConvert dataArrayFromArrayOfByteArrays:[RCTConvert NSArray:request[@"messages"]]];
        
        Bls12381G2KeyPair *blsKeyPair = [[Bls12381G2KeyPair alloc] initWithPublicKey:publicKey];
        
        BbsSignatureProof *proof = [[BbsSignatureProof alloc] initWithBytes:proofBytes
                                                                  withError:error];
        
        return [[NSNumber alloc] initWithBool:[proof blsVerifyProof:blsKeyPair
                                                           messages:messages
                                                              nonce:nonce
                                                          withError:error]];
    }];
    
    [operation exec:request
       withResolver:resolve
       withRejecter:reject];
}


@end
