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

#import "RNDynamsoftImageEditorViewManager.h"
#import "RNDynamsoftImageEditorView.h"
#import <DynamsoftCaptureVisionBundle/DynamsoftCaptureVisionBundle.h>
#import <React/RCTBridge+Private.h>
#import "DSImageData+HostObject.h"
#import <React/RCTUIManager.h>
#import "RNDynamsoft+Json.h"

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

@implementation RNDynamsoftImageEditorViewManager

RCT_EXPORT_MODULE(DynamsoftImageEditorView)

- (UIView *)view {
    return [[RNDynamsoftImageEditorView alloc] init];
}

RCT_EXPORT_METHOD(setQuads:(nonnull NSNumber *)tag quadArray:(NSArray *)array layerId:(NSInteger)layerId) {
    [self trySetQuadsWithTag:tag quadArray:array layerId:layerId attempt:1];
}

- (void)trySetQuadsWithTag:(nonnull NSNumber *)tag quadArray:(NSArray *)array layerId:(NSInteger)layerId attempt:(NSInteger)attempt {
    const NSInteger maxAttempts = 10;
    const NSTimeInterval retryDelay = 0.1; // 100ms

    [self.bridge.uiManager addUIBlock:
     ^(__unused RCTUIManager *uiManager, NSDictionary<NSNumber *, UIView *> *viewRegistry) {
         id view = viewRegistry[tag];
         if (!view || ![view isKindOfClass:[RNDynamsoftImageEditorView class]]) {
             if (attempt < maxAttempts) {
                 dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(retryDelay * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
                     [self trySetQuadsWithTag:tag quadArray:array layerId:layerId attempt:attempt + 1];
                 });
             } else {
                 RCTLogError(@"Cannot find RNDynamsoftImageEditorView with tag #%@ after %ld attempts", tag, (long)attempt);
             }
         } else {
             if (array && array.count > 0) {
                 NSMutableArray<DSQuadDrawingItem *> *items = [NSMutableArray array];
                 for (id dic in array) {
                     if ([dic isKindOfClass:[NSDictionary class]]) {
                         DSQuadrilateral *quad = [DSQuadrilateral quadrilateralWithDictionary:dic];
                         [items addObject:[[DSQuadDrawingItem alloc] initWithQuadrilateral:quad]];
                     }
                 }
                 DSDrawingLayer *layer = [((RNDynamsoftImageEditorView *)view).editorView getDrawingLayer:layerId];
                 layer.drawingItems = items;
             }
         }
     }];
}

RCT_EXPORT_METHOD(getSelectedQuad:(nonnull NSNumber *)tag
                  resolver:(RCTPromiseResolveBlock)resolve
                  rejecter:(RCTPromiseRejectBlock)reject)
{
    [self.bridge.uiManager addUIBlock:
     ^(__unused RCTUIManager *uiManager, NSDictionary<NSNumber *, UIView *> *viewRegistry) {
         id view = viewRegistry[tag];
         if (!view || ![view isKindOfClass:[RNDynamsoftImageEditorView class]]) {
             RCTLogError(@"Cannot find RNDynamsoftImageEditorView with tag #%@", tag);
             resolve(nil);
         } else {
             DSDrawingItem *item = [((RNDynamsoftImageEditorView *)view).editorView getSelectedDrawingItem];
             if (item && item.mediaType == DSDrawingItemMediaTypeQuadrilateral) {
                 DSQuadDrawingItem *quadItem = (DSQuadDrawingItem *)item;
                 if (quadItem) {
                     resolve([quadItem.quad toJson]);
                 } else {
                     resolve(nil);
                 }
             }
         }
     }];
}

- (void)trySetImageDataWithTag:(nonnull NSNumber *)tag data:(DSImageData *)data attempt:(NSInteger)attempt {
    const NSInteger maxAttempts = 10;
    const NSTimeInterval retryDelay = 0.1; // 100ms

    [self.bridge.uiManager addUIBlock:
     ^(__unused RCTUIManager *uiManager, NSDictionary<NSNumber *, UIView *> *viewRegistry) {
         id view = viewRegistry[tag];
         if (!view || ![view isKindOfClass:[RNDynamsoftImageEditorView class]]) {
             if (attempt < maxAttempts) {
                 dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(retryDelay * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
                     [self trySetImageDataWithTag:tag data:data attempt:1];
                 });
             } else {
                 RCTLogError(@"Cannot find RNDynamsoftImageEditorView with tag #%@ after %ld attempts", tag, (long)attempt);
             }
         } else {
             ((RNDynamsoftImageEditorView *)view).editorView.imageData = data;
         }
     }];
}

#pragma mark - Global JSI
// --- RCT_EXPORT_BLOCKING_SYNCHRONOUS_METHOD(install) ---
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;
}

// --- Helper Function (Assumed to be accessible, copied here for completeness) ---
static void installHostFunction(
    jsi::Runtime &jsiRuntime,
    const char* name,
    size_t argCount,
    HostFunctionType hostFunction) {
    
    auto func = Function::createFromHostFunction(
        jsiRuntime,
        PropNameID::forAscii(jsiRuntime, name),
        argCount,
        std::move(hostFunction)
    );
    jsiRuntime.global().setProperty(jsiRuntime, name, std::move(func));
}

// --- JSI Installation Logic ---
static void install(jsi::Runtime &jsiRuntime, RNDynamsoftImageEditorViewManager *rnEditorViewManager) {

    // 1. editorView_setOriginalImage(viewTag, imageDataHostObject)
    installHostFunction(jsiRuntime, "editorView_setOriginalImage", 2,
        [rnEditorViewManager](Runtime &runtime, const Value &thisValue, const Value *arguments, size_t count) -> Value {
            
            // 🔍 Argument Validation & Type Check (Optimized)
            if (count != 2 || !arguments[0].isNumber() || !arguments[1].isObject()) {
                // Should potentially throw an exception for incorrect usage,
                // but returning Value(false) keeps it consistent with original code style.
                return Value(false);
            }

            // Extract View Tag (JS number)
            double tag = arguments[0].getNumber();
            NSNumber *viewTag = @(tag); // Convert to NSNumber for Objective-C method

            // Extract Image Data Host Object (Requires runtime for getObject)
            Object obj = arguments[1].getObject(runtime);
            
            // Check if it's the specific Host Object type (Requires runtime for isHostObject)
            if (obj.isHostObject<ImageDataHostObject>(runtime)) {
                // Safely convert Host Object to DSImageData
                ImageDataHostObject hostObject = ImageDataHostObject::getHOFromObject(runtime, obj);
                DSImageData *data = [DSImageData imageDataFromHostObject:hostObject];
                
                // Call the Objective-C View Manager method
                [rnEditorViewManager trySetImageDataWithTag:viewTag data:data attempt:1];
                
                return Value(true);
            }
            
            return Value(false);
        }
    );
}

@end
