/*
 * Copyright © Dynamsoft Corporation. All rights reserved.
 */

#import "RNDynamsoftImageSourceAdapter.h"
#import <React/RCTBridge+Private.h>
#import <React/RCTUtils.h>
#import <jsi/jsi.h>
#import <sys/utsname.h>
#import "YeetJSIUtils.h"
#import <DynamsoftCaptureVisionBundle/DynamsoftCaptureVisionBundle.h>
#import "DSImageData+HostObject.h"

using namespace facebook::jsi;
using namespace facebook;
using namespace std;

@interface RNDynamsoftImageSourceAdapter ()

@end

@implementation RNDynamsoftImageSourceAdapter

RCT_EXPORT_MODULE(DynamsoftImageSourceAdapterModule)

- (instancetype)init
{
    self = [super init];
    if (self) {
        
    }
    return self;
}
 
RCT_EXPORT_BLOCKING_SYNCHRONOUS_METHOD(createDefaultNativeISA) {
    DSImageSourceAdapter *adapter = [[DSImageSourceAdapter alloc] init];
    NSString *key = [NSNumber numberWithUnsignedInteger:adapter.hash].stringValue;
    [self.isaDictionary setValue:adapter forKey:key];
    return key;
}

RCT_EXPORT_METHOD(destroy:(NSString *)key) {
    [self.isaDictionary removeObjectForKey:key];
}

RCT_EXPORT_METHOD(clearBuffer:(NSString *)key) {
    [[self.isaDictionary valueForKey:key] clearBuffer];
}

RCT_EXPORT_METHOD(getImageCount:(NSString *)key
                  resolver:(RCTPromiseResolveBlock)resolve
                  rejecter:(RCTPromiseRejectBlock)reject)
{
    resolve(@([self.isaDictionary valueForKey:key].imageCount));
}

RCT_EXPORT_METHOD(isBufferEmpty:(NSString *)key
                  resolver:(RCTPromiseResolveBlock)resolve
                  rejecter:(RCTPromiseRejectBlock)reject)
{
    resolve(@([self.isaDictionary valueForKey:key].isBufferEmpty));
}

RCT_EXPORT_METHOD(getColourChannelUsageType:(NSString *)key
                  resolver:(RCTPromiseResolveBlock)resolve
                  rejecter:(RCTPromiseRejectBlock)reject)
{
    resolve(@([self.isaDictionary valueForKey:key].colourChannelUsageType));
}

RCT_EXPORT_METHOD(setColourChannelUsageType:(NSString *)key
                  type:(NSInteger)type
                  resolver:(RCTPromiseResolveBlock)resolve
                  rejecter:(RCTPromiseRejectBlock)reject)
{
    [self.isaDictionary valueForKey:key].colourChannelUsageType = (DSColourChannelUsageType)type;
    resolve(@(true));
}

RCT_EXPORT_METHOD(getMaximumImageCount:(NSString *)key
                  resolver:(RCTPromiseResolveBlock)resolve
                  rejecter:(RCTPromiseRejectBlock)reject)
{
    resolve(@([self.isaDictionary valueForKey:key].maxImageCount));
}

RCT_EXPORT_METHOD(setMaximumImageCount:(NSString *)key
                  count:(NSInteger)count
                  resolver:(RCTPromiseResolveBlock)resolve
                  rejecter:(RCTPromiseRejectBlock)reject)
{
    [self.isaDictionary valueForKey:key].maxImageCount = count;
    resolve(@(true));
}

RCT_EXPORT_METHOD(setNextImageToReturn:(NSString *)key
                  imageId:(NSInteger)imageId
                  resolver:(RCTPromiseResolveBlock)resolve
                  rejecter:(RCTPromiseRejectBlock)reject)
{
    [[self.isaDictionary valueForKey:key] setNextImageToReturn:imageId keepInBuffer:true];
    resolve(@(true));
}

RCT_EXPORT_METHOD(startFetching:(NSString *)key) {
    [[self.isaDictionary valueForKey:key] startFetching];
}

RCT_EXPORT_METHOD(stopFetching:(NSString *)key) {
    [[self.isaDictionary valueForKey:key] stopFetching];
}

RCT_EXPORT_BLOCKING_SYNCHRONOUS_METHOD(hasImage:(NSString *)key imageId:(NSInteger)imageId)  {
    return @([[self.isaDictionary valueForKey:key] hasImage:imageId]);
}

#pragma mark - Global
RCT_EXPORT_BLOCKING_SYNCHRONOUS_METHOD(install)
{
    RCTBridge* bridge = [RCTBridge currentBridge];
    RCTCxxBridge* cxxBridge = (RCTCxxBridge*)bridge;
    if (cxxBridge == nil) {
        return @false;
    }
    auto jsiRuntime = (jsi::Runtime*) cxxBridge.runtime;
    if (jsiRuntime == nil) {
        return @false;
    }
    
    install(*(facebook::jsi::Runtime *)jsiRuntime, self);
    return @true;
}

static void install(jsi::Runtime &jsiRuntime, RNDynamsoftImageSourceAdapter *rnAdapter) {
    const char* getImageDataName = "isaGetImageData";
    auto getImageDataFunc = Function::createFromHostFunction(jsiRuntime,
                                                          PropNameID::forAscii(jsiRuntime,
                                                                               getImageDataName),
                                                          1,
                                                          [rnAdapter](Runtime &runtime,
                                                                   const Value &thisValue,
                                                                   const Value *arguments,
                                                                   size_t count) -> Value {
        NSString *key = convertJSIStringToNSString(runtime, arguments[0].getString(runtime));
        ImageDataHostObject hostObject = [[rnAdapter imageDataFromISA:key] hostObject];
        Object obj = Object::createFromHostObject(runtime, std::make_shared<ImageDataHostObject>(hostObject));
        return obj;
    });
    jsiRuntime.global().setProperty(jsiRuntime, getImageDataName, std::move(getImageDataFunc));
    
    const char* addImageToBufferName = "isaAddImageToBuffer";
    auto addImageToBufferFunc = Function::createFromHostFunction(jsiRuntime,
                                                    PropNameID::forAscii(jsiRuntime,
                                                                         addImageToBufferName),
                                                    2,
                                                    [rnAdapter](Runtime &runtime,
                                                             const Value &thisValue,
                                                             const Value *arguments,
                                                             size_t count) -> Value {
        NSString *key = convertJSIStringToNSString(runtime, arguments[0].getString(runtime));
        Object obj = arguments[1].getObject(runtime);
        if (obj.isHostObject<ImageDataHostObject>(runtime)) {
            ImageDataHostObject hostObject = ImageDataHostObject::getHOFromObject(runtime, std::move(obj));
            DSImageData *data = [DSImageData imageDataFromHostObject:hostObject];
            [rnAdapter addImage:data toISA:key];
            return Value(true);
        }
        return Value(false);
    });
    jsiRuntime.global().setProperty(jsiRuntime, addImageToBufferName, std::move(addImageToBufferFunc));
    
    const char* imageDataToBase64Name = "imageDataToBase64";
    auto imageDataToBase64Func = Function::createFromHostFunction(jsiRuntime,
                                                          PropNameID::forAscii(jsiRuntime,
                                                                               imageDataToBase64Name),
                                                          1,
                                                          [rnAdapter](Runtime &runtime,
                                                                   const Value &thisValue,
                                                                   const Value *arguments,
                                                                   size_t count) -> Value {
        Object obj = arguments[0].getObject(runtime);
        if (obj.isHostObject<ImageDataHostObject>(runtime)) {
            ImageDataHostObject hostObject = ImageDataHostObject::getHOFromObject(runtime, std::move(obj));
            DSImageData *imageData = [DSImageData imageDataFromHostObject:hostObject];
            NSError *error;
            UIImage *image = [imageData toUIImage:&error];
            if (!error && image) {
                NSData *data = UIImageJPEGRepresentation(image, 1.0);
                NSString *base64String = [data base64EncodedStringWithOptions:NSDataBase64Encoding64CharacterLineLength];
                return convertNSStringToJSIString(runtime, base64String);
            }
        }
        return Value(false);
    });
    jsiRuntime.global().setProperty(jsiRuntime, imageDataToBase64Name, std::move(imageDataToBase64Func));
}

#pragma mark - Private
- (NSMutableDictionary<NSString *,DSImageSourceAdapter *> *)isaDictionary {
    if (!_isaDictionary) {
        _isaDictionary = [[NSMutableDictionary alloc] init];
    }
    return _isaDictionary;
}

- (void)addImage:(DSImageData *)data toISA:(NSString *)key {
    [[self.isaDictionary valueForKey:key] addImageToBuffer:data];
}

- (DSImageData *)imageDataFromISA:(NSString *)key {
    return [[self.isaDictionary valueForKey:key] getImage];
}

@end
