/*
 * Copyright © Dynamsoft Corporation. All rights reserved.
 */

#import "RNDynamsoft+Json.h"

NSArray<NSNumber *> *CGAffineTransformToArray(CGAffineTransform transform) {
    return @[@(transform.a), @(transform.b), @(transform.c), @(transform.d), @(transform.tx), @(transform.ty)];
}

void runOnMainThread(void (^block)(void))
{
    if ([NSThread isMainThread]) {
        block();
    } else {
        dispatch_async(dispatch_get_main_queue(), block);
    }
}

@implementation DSQuadrilateral (Json)

+ (nullable DSQuadrilateral *)quadrilateralWithDictionary:(NSDictionary *)dictionary {
    if (dictionary) {
        NSArray *array = [dictionary valueForKey:@"points"];
        NSMutableArray *points = [NSMutableArray array];
        for (id object in array) {
            if ([object isKindOfClass:NSDictionary.class]) {
                NSNumber *x = [object valueForKey:@"x"];
                NSNumber *y = [object valueForKey:@"y"];
                CGPoint point = CGPointMake(x.floatValue, y.floatValue);
                [points addObject:[NSValue valueWithCGPoint:point]];
            } else {
                return nil;
            }
        }
        return [[DSQuadrilateral alloc] initWithPointArray:[points copy]];
    } else {
        return nil;
    }
}

- (NSDictionary *)toJson {
    NSMutableArray *array = [NSMutableArray array];
    for (NSValue *value in self.points) {
        CGPoint point = value.CGPointValue;
        [array addObject:@{@"x":@(point.x), @"y":@(point.y)}];
    }
    NSDictionary *dictionary = @{@"points":[array copy]};
    return dictionary;
}

@end

@implementation DSCharacterResult (Json)

- (NSDictionary *)toJson {
    unichar h = self.characterH;
    unichar m = self.characterM;
    unichar l = self.characterL;
    return @{ @"characterH":[NSString stringWithCharacters:&h length:1],
              @"characterM":[NSString stringWithCharacters:&m length:1],
              @"characterL":[NSString stringWithCharacters:&l length:1],
              @"location":self.location ? [self.location toJson] : [NSNull null],
              @"characterHConfidence":@(self.characterHConfidence),
              @"characterMConfidence":@(self.characterMConfidence),
              @"characterLConfidence":@(self.characterLConfidence)
           };
}

@end

@implementation DSSimplifiedCaptureVisionSettings (Json)

- (void)updateWithDicitionary:(NSDictionary *)dictionary {
    NSNumber *outputOriginalImage = [dictionary valueForKey:@"outputOriginalImage"];
    if (outputOriginalImage) {
        self.outputOriginalImage = outputOriginalImage.boolValue;
    }
    NSDictionary *roi = [dictionary valueForKey:@"roi"];
    if (roi) {
        self.roi = [DSQuadrilateral quadrilateralWithDictionary:roi];
    }
    NSNumber *roiMeasuredInPercentage = [dictionary valueForKey:@"roiMeasuredInPercentage"];
    if (roiMeasuredInPercentage) {
        self.roiMeasuredInPercentage = roiMeasuredInPercentage.boolValue;
    }
    NSNumber *maxParallelTasks = [dictionary valueForKey:@"maxParallelTasks"];
    if (maxParallelTasks) {
        self.maxParallelTasks = maxParallelTasks.integerValue;
    }
    NSNumber *timeout = [dictionary valueForKey:@"timeout"];
    if (timeout) {
        self.timeout = timeout.integerValue;
    }
    NSNumber *minImageCaptureInterval = [dictionary valueForKey:@"minImageCaptureInterval"];
    if (minImageCaptureInterval) {
        self.minImageCaptureInterval = minImageCaptureInterval.integerValue;
    }
    NSDictionary *barcodeSettings = [dictionary valueForKey:@"barcodeSettings"];
    if (barcodeSettings) {
        [self.barcodeSettings updateWithDicitionary:barcodeSettings];
    }
    NSDictionary *labelSettings = [dictionary valueForKey:@"labelSettings"];
    if (labelSettings) {
        [self.labelSettings updateWithDicitionary:labelSettings];
    }
    NSDictionary *documentSettings = [dictionary valueForKey:@"documentSettings"];
    if (documentSettings) {
        [self.documentSettings updateWithDicitionary:documentSettings];
    }
}

- (NSObject *)toJson {
    if (self) {
        NSDictionary *dictionary = @{@"outputOriginalImage":@(self.outputOriginalImage),
                                     @"roi":self.roi ? [self.roi toJson] : [NSNull null],
                                     @"roiMeasuredInPercentage":@(self.roiMeasuredInPercentage),
                                     @"maxParallelTasks":@(self.maxParallelTasks),
                                     @"timeout":@(self.timeout),
                                     @"minImageCaptureInterval":@(self.minImageCaptureInterval),
                                     @"barcodeSettings": [self.barcodeSettings toJson],
                                     @"labelSettings":[self.labelSettings toJson],
                                     @"documentSettings":[self.documentSettings toJson] };
        return dictionary;
    } else {
        return [NSNull null];
    }
}

@end

@implementation DSSimplifiedBarcodeReaderSettings (Json)

- (void)updateWithDicitionary:(NSDictionary *)dictionary {
    NSString *_barcodeFormatIdsNumberString = [dictionary valueForKey:@"_barcodeFormatIdsNumberString"];
    if (_barcodeFormatIdsNumberString) {
        NSUInteger unsignedIntegerValue = strtoull([_barcodeFormatIdsNumberString UTF8String], NULL, 10);
        self.barcodeFormatIds = unsignedIntegerValue;
    }
    NSNumber *expectedBarcodesCount = [dictionary valueForKey:@"expectedBarcodesCount"];
    if (expectedBarcodesCount) {
        self.expectedBarcodesCount = expectedBarcodesCount.integerValue;
    }
    NSNumber *minResultConfidence = [dictionary valueForKey:@"minResultConfidence"];
    if (minResultConfidence) {
        self.minResultConfidence = minResultConfidence.integerValue;
    }
    NSNumber *minBarcodeTextLength = [dictionary valueForKey:@"minBarcodeTextLength"];
    if (minBarcodeTextLength) {
        self.minBarcodeTextLength = minBarcodeTextLength.integerValue;
    }
    NSString *barcodeTextRegExPattern = [dictionary valueForKey:@"barcodeTextRegExPattern"];
    if (barcodeTextRegExPattern) {
        self.barcodeTextRegExPattern = barcodeTextRegExPattern;
    }
    NSNumber *maxThreadsInOneTask = [dictionary valueForKey:@"maxThreadsInOneTask"];
    if (maxThreadsInOneTask) {
        self.maxThreadsInOneTask = maxThreadsInOneTask.integerValue;
    }
    NSNumber *scaleDownThreshold = [dictionary valueForKey:@"scaleDownThreshold"];
    if (scaleDownThreshold) {
        self.scaleDownThreshold = scaleDownThreshold.integerValue;
    }
    NSArray *localizationModes = [dictionary valueForKey:@"localizationModes"];
    if (localizationModes) {
        self.localizationModes = localizationModes;
    }
    NSArray *deblurModes = [dictionary valueForKey:@"deblurModes"];
    if (deblurModes) {
        self.deblurModes = deblurModes;
    }
    NSArray *grayscaleTransformationModes = [dictionary valueForKey:@"grayscaleTransformationModes"];
    if (grayscaleTransformationModes) {
        self.grayscaleTransformationModes = grayscaleTransformationModes;
    }
    NSArray *grayscaleEnhancementModes = [dictionary valueForKey:@"grayscaleEnhancementModes"];
    if (grayscaleEnhancementModes) {
        self.grayscaleEnhancementModes = grayscaleEnhancementModes;
    }
}

- (NSObject *)toJson {
    if (self) {
        NSDictionary *dictionary = @{@"_barcodeFormatIdsNumberString":@(self.barcodeFormatIds).stringValue,
                                     @"expectedBarcodesCount":@(self.expectedBarcodesCount),
                                     @"minResultConfidence":@(self.minResultConfidence),
                                     @"minBarcodeTextLength":@(self.minBarcodeTextLength),
                                     @"barcodeTextRegExPattern":self.barcodeTextRegExPattern ? self.barcodeTextRegExPattern : [NSNull null],
                                     @"maxThreadsInOneTask":@(self.maxThreadsInOneTask),
                                     @"scaleDownThreshold":@(self.scaleDownThreshold),
                                     @"localizationModes":self.localizationModes ? self.localizationModes : [NSNull null],
                                     @"deblurModes":self.deblurModes ? self.deblurModes : [NSNull null],
                                     @"grayscaleTransformationModes":self.grayscaleTransformationModes ? self.grayscaleTransformationModes : [NSNull null],
                                     @"grayscaleEnhancementModes":self.grayscaleEnhancementModes ? self.grayscaleEnhancementModes : [NSNull null]
        };
        return dictionary;
    } else {
        return [NSNull null];
    }
}

@end

@implementation DSSimplifiedLabelRecognizerSettings (Json)

- (void)updateWithDicitionary:(NSDictionary *)dictionary {
    NSArray *grayscaleTransformationModes = [dictionary valueForKey:@"grayscaleTransformationModes"];
    if (grayscaleTransformationModes) {
        self.grayscaleTransformationModes = grayscaleTransformationModes;
    }
    NSArray *grayscaleEnhancementModes = [dictionary valueForKey:@"grayscaleEnhancementModes"];
    if (grayscaleEnhancementModes) {
        self.grayscaleEnhancementModes = grayscaleEnhancementModes;
    }
    NSString *characterModelName = [dictionary valueForKey:@"characterModelName"];
    if (characterModelName) {
        self.characterModelName = characterModelName;
    }
    NSString *lineStringRegExPattern = [dictionary valueForKey:@"lineStringRegExPattern"];
    if (lineStringRegExPattern) {
        self.lineStringRegExPattern = lineStringRegExPattern;
    }
    NSNumber *maxThreadsInOneTask = [dictionary valueForKey:@"maxThreadsInOneTask"];
    if (maxThreadsInOneTask) {
        self.maxThreadsInOneTask = maxThreadsInOneTask.integerValue;
    }
    NSNumber *scaleDownThreshold = [dictionary valueForKey:@"scaleDownThreshold"];
    if (scaleDownThreshold) {
        self.scaleDownThreshold = scaleDownThreshold.integerValue;
    }
}

- (NSObject *)toJson {
    if (self) {
        NSDictionary *dictionary = @{@"grayscaleTransformationModes":self.grayscaleTransformationModes ? self.grayscaleTransformationModes : [NSNull null],
                                     @"grayscaleEnhancementModes":self.grayscaleEnhancementModes ? self.grayscaleEnhancementModes : [NSNull null],
                                     @"characterModelName":self.characterModelName ? self.characterModelName : [NSNull null],
                                     @"lineStringRegExPattern":self.lineStringRegExPattern ? self.lineStringRegExPattern : [NSNull null],
                                     @"maxThreadsInOneTask":@(self.maxThreadsInOneTask),
                                     @"scaleDownThreshold":@(self.scaleDownThreshold)
        };
        return dictionary;
    } else {
        return [NSNull null];
    }
}

@end

@implementation DSSimplifiedDocumentNormalizerSettings (Json)

- (void)updateWithDicitionary:(NSDictionary *)dictionary {
    NSArray *grayscaleTransformationModes = [dictionary valueForKey:@"grayscaleTransformationModes"];
    if (grayscaleTransformationModes) {
        self.grayscaleTransformationModes = grayscaleTransformationModes;
    }
    NSArray *grayscaleEnhancementModes = [dictionary valueForKey:@"grayscaleEnhancementModes"];
    if (grayscaleEnhancementModes) {
        self.grayscaleEnhancementModes = grayscaleEnhancementModes;
    }
    NSNumber *colourMode = [dictionary valueForKey:@"colourMode"];
    if (colourMode) {
        self.colourMode = (DSImageColourMode)colourMode.integerValue;
    }
    NSArray *pageSize = [dictionary valueForKey:@"pageSize"];
    if (pageSize) {
        NSNumber *width = pageSize.firstObject;
        NSNumber *height = pageSize.lastObject;
        if (width && height) {
            self.pageSize = CGSizeMake(width.integerValue, height.integerValue);
        }
    }
    NSNumber *brightness = [dictionary valueForKey:@"brightness"];
    if (brightness) {
        self.brightness = brightness.integerValue;
    }
    NSNumber *contrast = [dictionary valueForKey:@"contrast"];
    if (contrast) {
        self.contrast = contrast.integerValue;
    }
    NSNumber *maxThreadsInOneTask = [dictionary valueForKey:@"maxThreadsInOneTask"];
    if (maxThreadsInOneTask) {
        self.maxThreadsInOneTask = maxThreadsInOneTask.integerValue;
    }
    NSNumber *scaleDownThreshold = [dictionary valueForKey:@"scaleDownThreshold"];
    if (scaleDownThreshold) {
        self.scaleDownThreshold = scaleDownThreshold.integerValue;
    }
    NSNumber *minQuadrilateralAreaRatio = [dictionary valueForKey:@"minQuadrilateralAreaRatio"];
    if (minQuadrilateralAreaRatio) {
        self.minQuadrilateralAreaRatio = minQuadrilateralAreaRatio.integerValue;
    }
    NSNumber *expectedDocumentsCount = [dictionary valueForKey:@"expectedDocumentsCount"];
    if (expectedDocumentsCount) {
        self.expectedDocumentsCount = expectedDocumentsCount.integerValue;
    }
}

- (NSObject *)toJson {
    if (self) {
        NSDictionary *dictionary = @{@"grayscaleTransformationModes":self.grayscaleTransformationModes ? self.grayscaleTransformationModes : [NSNull null],
                                     @"grayscaleEnhancementModes":self.grayscaleEnhancementModes ? self.grayscaleEnhancementModes : [NSNull null],
                                     @"colourMode":@(self.colourMode),
                                     @"pageSize":@[@(self.pageSize.width), @(self.pageSize.height)],
                                     @"brightness":@(self.brightness),
                                     @"contrast":@(self.contrast),
                                     @"maxThreadsInOneTask":@(self.maxThreadsInOneTask),
                                     @"scaleDownThreshold":@(self.scaleDownThreshold),
                                     @"minQuadrilateralAreaRatio":@(self.minQuadrilateralAreaRatio),
                                     @"expectedDocumentsCount":@(self.expectedDocumentsCount) };
        return dictionary;
    } else {
        return [NSNull null];
    }
}

@end

@implementation DSCapturedResultItem (Json)

- (nullable NSDictionary *)toJson {
    NSDictionary *dictionary;
    switch (self.type) {
        case DSCapturedResultItemTypeOriginalImage:
            return nil;
            break;
        case DSCapturedResultItemTypeBarcode:
            {
                DSBarcodeResultItem *item = (DSBarcodeResultItem *)self;
                dictionary = @{ @"type":@(item.type),
                                @"targetROIDefName":item.targetROIDefName,
                                @"taskName":item.taskName,
                                @"_formatNumberString": [NSNumber numberWithUnsignedInteger:item.format].stringValue,
                                @"formatString":item.formatString,
                                @"text":item.text,
                                @"location":[item.location toJson],
                                @"confidence":@(item.confidence),
                                @"angle":@(item.angle),
                                @"moduleSize":@(item.moduleSize),
                                @"isDPM":@(item.isDPM),
                                @"isMirrored":@(item.isMirrored)
                };
            }
            break;
        case DSCapturedResultItemTypeTextLine:
            {
                DSTextLineResultItem *item = (DSTextLineResultItem *)self;
                NSArray<DSCharacterResult *> *charResultArray = item.charResult;
                id charResult;
                if (charResultArray.count > 0) {
                    NSMutableArray<NSDictionary *> *mutableArray = [NSMutableArray array];
                    for (DSCharacterResult *result in charResultArray) {
                        [mutableArray addObject:[result toJson]];
                    }
                    charResult = [mutableArray copy];
                } else {
                    charResult = [NSNull null];
                }
                dictionary = @{ @"type":@(item.type),
                                @"targetROIDefName":item.targetROIDefName,
                                @"taskName":item.taskName,
                                @"text":item.text,
                                @"location":[item.location toJson],
                                @"confidence":@(item.confidence),
                                @"charResult":charResult
                };
            }
            break;
        case DSCapturedResultItemTypeDetectedQuad:
            {
                DSDetectedQuadResultItem *item = (DSDetectedQuadResultItem *)self;
                dictionary = @{ @"type":@(item.type),
                                @"targetROIDefName":item.targetROIDefName,
                                @"taskName":item.taskName,
                                @"location":[item.location toJson],
                                @"confidenceAsDocumentBoundary":@(item.confidenceAsDocumentBoundary),
                                @"crossVerificationStatus":@(item.crossVerificationStatus)
                };
            }
            break;
        case DSCapturedResultItemTypeDeskewedImage:
            {
                DSDeskewedImageResultItem *item = (DSDeskewedImageResultItem *)self;
                dictionary = @{ @"type":@(item.type),
                                @"targetROIDefName":item.targetROIDefName,
                                @"taskName":item.taskName,
                                @"sourceDeskewQuad":[item.sourceDeskewQuad toJson],
                                @"crossVerificationStatus":@(item.crossVerificationStatus),
                                @"originalToLocalMatrix":CGAffineTransformToArray(item.originalToLocalMatrix)
                };
            }
            break;
        case DSCapturedResultItemTypeEnhancedImage: {
            DSEnhancedImageResultItem *item = (DSEnhancedImageResultItem *)self;
            dictionary = @{ @"type":@(item.type),
                            @"targetROIDefName":item.targetROIDefName,
                            @"taskName":item.taskName,
                            @"originalToLocalMatrix":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];
                }
                dictionary = @{ @"type":@(item.type),
                                @"targetROIDefName":item.targetROIDefName,
                                @"taskName":item.taskName,
                                @"jsonString":item.jsonString,
                                @"codeType":item.codeType,
                                @"parsedFields":[mutableDictionary copy]
                };
            }
            break;
    }
    return dictionary;
}

@end

@implementation DSCapturedResult (Json)

- (NSObject *)toJson {
    if (self) {
        id items;
        if (self.items.count > 0) {
            NSMutableArray *array = [NSMutableArray array];
            for (DSCapturedResultItem *item in self.items) {
                NSDictionary *obj = [item toJson];
                if (obj) {
                    [array addObject:obj];
                }
            }
            items = [array copy];
        } else {
            items = [NSNull null];
        }
        NSDictionary *dictionary = @{@"originalImageHashId":self.originalImageHashId,
                                     @"items":items,
                                     @"rotationTransformMatrix":CGAffineTransformToArray(self.rotationTransformMatrix),
                                     @"errorCode":@(self.errorCode),
                                     @"errorMessage":self.errorMessage ? self.errorMessage : [NSNull null]
        };
        return dictionary;
    } else {
        return [NSNull null];
    }
}

@end

@implementation DSDecodedBarcodesResult (Json)

- (NSObject *)toJson {
    if (self) {
        id items;
        if (self.items.count > 0) {
            NSMutableArray *array = [NSMutableArray array];
            for (DSBarcodeResultItem *item in self.items) {
                NSDictionary *obj = [item toJson];
                if (obj) {
                    [array addObject:obj];
                }
            }
            items = [array copy];
        } else {
            items = [NSNull null];
        }
        NSDictionary *dictionary = @{@"originalImageHashId":self.originalImageHashId,
                                     @"items":items,
                                     @"rotationTransformMatrix":CGAffineTransformToArray(self.rotationTransformMatrix),
                                     @"errorCode":@(self.errorCode),
                                     @"errorMessage":self.errorMessage ? self.errorMessage : [NSNull null]
        };
        return dictionary;
    } else {
        return [NSNull null];
    }
}

@end

@implementation DSProcessedDocumentResult (Json)

- (NSObject *)toJson {
    if (self) {
        id detectedQuadResultItems;
        if (self.detectedQuadResultItems.count > 0) {
            NSMutableArray *array = [NSMutableArray array];
            for (DSDetectedQuadResultItem *item in self.detectedQuadResultItems) {
                NSDictionary *obj = [item toJson];
                if (obj) {
                    [array addObject:obj];
                }
            }
            detectedQuadResultItems = [array copy];
        } else {
            detectedQuadResultItems = [NSNull null];
        }
        id deskewedImageResultItems;
        if (self.deskewedImageResultItems.count > 0) {
            NSMutableArray *array = [NSMutableArray array];
            for (DSDeskewedImageResultItem *item in self.deskewedImageResultItems) {
                NSDictionary *obj = [item toJson];
                if (obj) {
                    [array addObject:obj];
                }
            }
            deskewedImageResultItems = [array copy];
        } else {
            deskewedImageResultItems = [NSNull null];
        }
        id enhancedImageResultItems;
        if (self.enhancedImageResultItems.count > 0) {
            NSMutableArray *array = [NSMutableArray array];
            for (DSEnhancedImageResultItem *item in self.enhancedImageResultItems) {
                NSDictionary *obj = [item toJson];
                if (obj) {
                    [array addObject:obj];
                }
            }
            enhancedImageResultItems = [array copy];
        } else {
            enhancedImageResultItems = [NSNull null];
        }
        
        NSDictionary *dictionary = @{@"originalImageHashId":self.originalImageHashId,
                                     @"detectedQuadResultItems":detectedQuadResultItems,
                                     @"deskewedImageResultItems":deskewedImageResultItems,
                                     @"enhancedImageResultItems":enhancedImageResultItems,
                                     @"rotationTransformMatrix":CGAffineTransformToArray(self.rotationTransformMatrix),
                                     @"errorCode":@(self.errorCode),
                                     @"errorMessage":self.errorMessage ? self.errorMessage : [NSNull null]
        };
        return dictionary;
    } else {
        return [NSNull null];
    }
}

@end

@implementation DSRecognizedTextLinesResult (Json)

- (NSObject *)toJson {
    if (self) {
        id items;
        if (self.items.count > 0) {
            NSMutableArray *array = [NSMutableArray array];
            for (DSTextLineResultItem *item in self.items) {
                NSDictionary *obj = [item toJson];
                if (obj) {
                    [array addObject:obj];
                }
            }
            items = [array copy];
        } else {
            items = [NSNull null];
        }
        NSDictionary *dictionary = @{@"originalImageHashId":self.originalImageHashId,
                                     @"items":items,
                                     @"rotationTransformMatrix":CGAffineTransformToArray(self.rotationTransformMatrix),
                                     @"errorCode":@(self.errorCode),
                                     @"errorMessage":self.errorMessage ? self.errorMessage : [NSNull null]
        };
        return dictionary;
    } else {
        return [NSNull null];
    }
}

@end

@implementation DSParsedResult (Json)

- (NSObject *)toJson {
    if (self) {
        id items;
        if (self.items.count > 0) {
            NSMutableArray *array = [NSMutableArray array];
            for (DSParsedResultItem *item in self.items) {
                NSDictionary *obj = [item toJson];
                if (obj) {
                    [array addObject:obj];
                }
            }
            items = [array copy];
        } else {
            items = [NSNull null];
        }
        NSDictionary *dictionary = @{@"originalImageHashId":self.originalImageHashId,
                                     @"items":items,
                                     @"errorCode":@(self.errorCode),
                                     @"errorMessage":self.errorMessage ? self.errorMessage : [NSNull null]
        };
        return dictionary;
    } else {
        return [NSNull null];
    }
}

@end
