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

#import "RNDynamsoft+JSI.h"
#import "RNDynamsoft+Json.h"
#import "DSImageData+HostObject.h"
#import "YeetJSIUtils.h"
#import <DynamsoftCaptureVisionBundle/DynamsoftCaptureVisionBundle.h>

NSArray<NSNumber *> *CGAffineTransformToArray(CGAffineTransform transform) {
    return @[@(transform.a), @(transform.b), @(transform.c), @(transform.d), @(transform.tx), @(transform.ty)];
}

@implementation DSCapturedResultItem (JSI)

- (Object)convertToJSIObject:(Runtime& )runtime {
    Object obj = Object(runtime);
    obj.setProperty(runtime, "type", convertNSNumberToJSINumber(runtime, @(self.type)));
    obj.setProperty(runtime, "targetROIDefName", convertNSStringToJSIString(runtime, self.targetROIDefName));
    obj.setProperty(runtime, "taskName", convertNSStringToJSIString(runtime, self.taskName));
    switch (self.type) {
        case DSCapturedResultItemTypeOriginalImage:
            {
                
            }
            break;
        case DSCapturedResultItemTypeBarcode:
            {
                DSBarcodeResultItem *item = (DSBarcodeResultItem *)self;
                obj.setProperty(runtime, "format", convertNSNumberToJSINumber(runtime, @(item.format)));
                obj.setProperty(runtime, "formatString", convertNSStringToJSIString(runtime, item.formatString));
                obj.setProperty(runtime, "text", convertNSStringToJSIString(runtime, item.text));
                obj.setProperty(runtime, "location", convertNSDictionaryToJSIObject(runtime, [item.location toJson]));
                obj.setProperty(runtime, "confidence", convertNSNumberToJSINumber(runtime, @(item.confidence)));
                obj.setProperty(runtime, "angle", convertNSNumberToJSINumber(runtime, @(item.angle)));
                obj.setProperty(runtime, "moduleSize", convertNSNumberToJSINumber(runtime, @(item.moduleSize)));
                obj.setProperty(runtime, "isDPM", convertNSNumberToJSIBoolean(runtime, @(item.isDPM)));
                obj.setProperty(runtime, "isMirrored", convertNSNumberToJSIBoolean(runtime, @(item.isMirrored)));
            }
            break;
        case DSCapturedResultItemTypeTextLine:
            {
                DSTextLineResultItem *item = (DSTextLineResultItem *)self;
                obj.setProperty(runtime, "text", convertNSStringToJSIString(runtime, item.text));
                obj.setProperty(runtime, "location", convertNSDictionaryToJSIObject(runtime, [item.location toJson]));
                obj.setProperty(runtime, "confidence",convertNSNumberToJSINumber(runtime, @(item.confidence)));
                int count = item.charResult.count;
                if (count > 0) {
                    jsi::Array array = jsi::Array(runtime, count);
                    for (size_t i = 0; i < count; i++) {
                        array.setValueAtIndex(runtime, i, convertNSDictionaryToJSIObject(runtime, [[item.charResult objectAtIndex:i] toJson]));
                    }
                    obj.setProperty(runtime, "charResult", array);
                }
            }
            break;
        case DSCapturedResultItemTypeDetectedQuad:
            {
                DSDetectedQuadResultItem *item = (DSDetectedQuadResultItem *)self;
                obj.setProperty(runtime, "location", convertNSDictionaryToJSIObject(runtime, [item.location toJson]));
                obj.setProperty(runtime, "confidenceAsDocumentBoundary",convertNSNumberToJSINumber(runtime, @(item.confidenceAsDocumentBoundary)));
            }
            break;
        case DSCapturedResultItemTypeDeskewedImage:
            {
                DSDeskewedImageResultItem *item = (DSDeskewedImageResultItem *)self;
                if (item.imageData) {
                    ImageDataHostObject hostObject = [item.imageData hostObject];
                    Object object = Object::createFromHostObject(runtime, std::make_shared<ImageDataHostObject>(hostObject));
                    obj.setProperty(runtime, "imageData", object);
                }
                obj.setProperty(runtime, "sourceDeskewQuad", convertNSDictionaryToJSIObject(runtime, [item.sourceDeskewQuad toJson]));
                obj.setProperty(runtime, "crossVerificationStatus", convertNSNumberToJSINumber(runtime, @(item.crossVerificationStatus)));
                obj.setProperty(runtime, "originalToLocalMatrix", convertNSArrayToJSIArray(runtime, CGAffineTransformToArray(item.originalToLocalMatrix)));
            }
            break;
        case DSCapturedResultItemTypeEnhancedImage:
        {
            DSEnhancedImageResultItem *item = (DSEnhancedImageResultItem *)self;
            if (item.imageData) {
                ImageDataHostObject hostObject = [item.imageData hostObject];
                Object object = Object::createFromHostObject(runtime, std::make_shared<ImageDataHostObject>(hostObject));
                obj.setProperty(runtime, "imageData", object);
            }
            obj.setProperty(runtime, "originalToLocalMatrix", convertNSArrayToJSIArray(runtime, CGAffineTransformToArray(item.originalToLocalMatrix)));
            break;
        }
        case DSCapturedResultItemTypeParsedResult:
            {
                DSParsedResultItem *item = (DSParsedResultItem *)self;
                NSDictionary<NSString *, NSString *> *parsedFields = item.parsedFields;
                NSMutableDictionary *mutableDictionary = [NSMutableDictionary dictionaryWithDictionary:parsedFields];
                for (NSString *key in [parsedFields allKeys]) {
                    [mutableDictionary setValue:@{ @"value":[parsedFields valueForKey:key],
                                                   @"mappingStatus":@([item getFieldMappingStatus:key]),
                                                   @"validationStatus":@([item getFieldValidationStatus:key]) } forKey:key];
                }
                obj.setProperty(runtime, "parsedFields", convertNSDictionaryToJSIObject(runtime, [mutableDictionary copy]));
                obj.setProperty(runtime, "jsonString", convertNSStringToJSIString(runtime, item.jsonString));
                obj.setProperty(runtime, "codeType", convertNSStringToJSIString(runtime, item.codeType));
            }
            break;
    }
    return obj;
}

@end

@implementation DSCapturedResult (JSI)

- (Object)convertToJSIObject:(Runtime& )runtime {
    Object obj = Object(runtime);
    obj.setProperty(runtime, "originalImageHashId", convertNSStringToJSIString(runtime, self.originalImageHashId));
    obj.setProperty(runtime, "errorMessage", convertNSStringToJSIString(runtime, self.errorMessage));
    obj.setProperty(runtime, "errorCode", convertNSNumberToJSINumber(runtime, @(self.errorCode)));
    obj.setProperty(runtime, "rotationTransformMatrix", convertNSArrayToJSIArray(runtime, CGAffineTransformToArray(self.rotationTransformMatrix)));
    int count = (int)self.items.count;
    if (count > 0) {
        jsi::Array array = jsi::Array(runtime, count);
        for (size_t i = 0; i < count; i++) {
            array.setValueAtIndex(runtime, i, [[self.items objectAtIndex:i] convertToJSIObject:runtime]);
        }
        obj.setProperty(runtime, "items", array);
    }
    return obj;
}

@end

DSQuadrilateral * convertJSIValueToDSQuadrilateral(
                               jsi::Runtime &runtime,
                               const jsi::Object &value)
{
    if (value.hasProperty(runtime, "points") && value.getProperty(runtime, "points").isObject() && value.getProperty(runtime, "points").getObject(runtime).isArray(runtime)) {
        auto jsiPoints = value.getProperty(runtime, "points").getObject(runtime).getArray(runtime);
        size_t size = jsiPoints.size(runtime);
        NSMutableArray *points = [NSMutableArray array];
        for (size_t i = 0; i < size; i++) {
            auto jsiPoint =  jsiPoints.getValueAtIndex(runtime, i).getObject(runtime);
            if (jsiPoint.hasProperty(runtime, "x") && jsiPoint.hasProperty(runtime, "y")) {
                double x = jsiPoint.getProperty(runtime, "x").getNumber();
                double y = jsiPoint.getProperty(runtime, "y").getNumber();
                CGPoint point = CGPointMake(x, y);
                [points addObject:[NSValue valueWithCGPoint:point]];
            }
        }
        if (points.count == 4) {
            return [[DSQuadrilateral alloc] initWithPointArray:[points copy]];
        }
    }
    return nil;
}
