/*
 * 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 NSData (DSNumber)

- (nullable NSArray<NSNumber *> *)toNumberArray {
    if (!self) return nil;
    
    const uint8_t *bytes = (const uint8_t *)self.bytes;
    NSUInteger length = self.length;
    
    NSMutableArray *result = [NSMutableArray arrayWithCapacity:length];
    for (NSUInteger i = 0; i < length; i++) {
        [result addObject:@(bytes[i])];
    }
    
    return result;
}

@end

@implementation DSCapturedResultItem (Json)

- (nullable NSDictionary *)toJson {
    NSDictionary *dictionary;
    switch (self.type) {
        case DSCapturedResultItemTypeOriginalImage:
            return nil;
            break;
        case DSCapturedResultItemTypeBarcode:
            {
                DSBarcodeResultItem *item = (DSBarcodeResultItem *)self;
                NSMutableDictionary *mutableDictionary = [NSMutableDictionary dictionaryWithDictionary:@{
                    @"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)
                }];
                if (item.details) {
                    if (item.format == DSBarcodeFormatQRCode) {
                        DSQRCodeDetails *qrDetails = (DSQRCodeDetails *)item.details;
                        NSMutableDictionary *qrDict = [NSMutableDictionary dictionaryWithDictionary:@{
                            @"rows": @(qrDetails.rows),
                            @"columns": @(qrDetails.columns),
                            @"errorCorrectionLevel": @(qrDetails.errorCorrectionLevel),
                            @"version": @(qrDetails.version),
                            @"model": @(qrDetails.model),
                            @"mode": @(qrDetails.mode),
                            @"page": @(qrDetails.page),
                            @"totalPage": @(qrDetails.totalPage),
                            @"parityData": @(qrDetails.parityData),
                            @"dataMaskPattern": @(qrDetails.dataMaskPattern)
                        }];
                        
                        if (qrDetails.codewords) {
                            qrDict[@"codewords"] = [qrDetails.codewords toNumberArray];
                        }
                        
                        mutableDictionary[@"qrCodeDetails"] = qrDict;
                    } else if (item.format == DSBarcodeFormatAztec) {
                        DSAztecDetails *aztecDetails = (DSAztecDetails *)item.details;
                        NSDictionary *aztecDict = @{
                            @"rows": @(aztecDetails.rows),
                            @"columns": @(aztecDetails.columns),
                            @"layerNumber": @(aztecDetails.layerNumber)
                        };
                        mutableDictionary[@"aztecDetails"] = aztecDict;
                    } else if (item.format == DSBarcodeFormatDataMatrix) {
                        DSDataMatrixDetails *dmDetails = (DSDataMatrixDetails *)item.details;
                        NSDictionary *dmDict = @{
                            @"rows": @(dmDetails.rows),
                            @"columns": @(dmDetails.columns),
                            @"dataRegionRows": @(dmDetails.dataRegionRows),
                            @"dataRegionColumns": @(dmDetails.dataRegionColumns),
                            @"dataRegionNumber": @(dmDetails.dataRegionNumber)
                        };
                        mutableDictionary[@"dataMatrixDetails"] = dmDict;
                    } else if ((item.format & DSBarcodeFormatOneD) != 0) {
                        DSOneDCodeDetails *onedDetails = (DSOneDCodeDetails *)item.details;
                        NSMutableDictionary *onedDict = [NSMutableDictionary dictionary];
                        
                        if (onedDetails.startCharsBytes) {
                            onedDict[@"startCharsBytes"] = [onedDetails.startCharsBytes toNumberArray];
                        }
                        if (onedDetails.stopCharsBytes) {
                            onedDict[@"stopCharsBytes"] = [onedDetails.stopCharsBytes toNumberArray];
                        }
                        if (onedDetails.checkDigitBytes) {
                            onedDict[@"checkDigitBytes"] = [onedDetails.checkDigitBytes toNumberArray];
                        }
                        
                        onedDict[@"startPatternRange_lower"] = @(onedDetails.startPatternRange.minimum);
                        onedDict[@"middlePatternRange_lower"] = @(onedDetails.middlePatternRange.minimum);
                        onedDict[@"endPatternRange_lower"] = @(onedDetails.endPatternRange.minimum);
                        onedDict[@"startPatternRange_upper"] = @(onedDetails.startPatternRange.maximum);
                        onedDict[@"middlePatternRange_upper"] = @(onedDetails.middlePatternRange.maximum);
                        onedDict[@"endPatternRange_upper"] = @(onedDetails.endPatternRange.maximum);
                        
                        mutableDictionary[@"oneDCodeDetails"] = onedDict;
                    } else if (item.format == DSBarcodeFormatPDF417) {
                        DSPDF417Details *pdf417Details = (DSPDF417Details *)item.details;
                        NSMutableDictionary *pdf417Dict = [NSMutableDictionary dictionaryWithDictionary:@{
                            @"rows": @(pdf417Details.rows),
                            @"columns": @(pdf417Details.columns),
                            @"errorCorrectionLevel": @(pdf417Details.errorCorrectionLevel),
                            @"hasLeftRowIndicator": @(pdf417Details.hasLeftRowIndicator),
                            @"hasRightRowIndicator": @(pdf417Details.hasRightRowIndicator)
                        }];
                        
                        if (pdf417Details.codewords) {
                            pdf417Dict[@"codewords"] = pdf417Details.codewords;
                        }
                        
                        mutableDictionary[@"pdf417Details"] = pdf417Dict;
                    }
                }
                dictionary = [mutableDictionary copy];
            }
            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,
                                @"rawText":item.rawText,
                                @"location":[item.location toJson],
                                @"confidence":@(item.confidence),
                                @"characterResults":charResult,
                                @"specificationName": item.specificationName
                };
            }
            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
