//
//  KMOCROperation.m
//  PDF Reader Pro Edition
//
//  Created by 万军 on 2020/3/23.
//

#import "KMOCROperation.h"
#import "KMGOCRManager.h"
#import <AppKit/AppKit.h>

#define KMOCR_API_URL     @"https://api.convertio.co/convert"

#if VERSION_FREE
#define KMOCR_API_KEY     @"d1a0c1a4cc7fcac5eb08730570217f97"
#else
#define KMOCR_API_KEY     @"d1a0c1a4cc7fcac5eb08730570217f97"
#endif

#define KMTIMER_MAXIMUM_CYCLE 10

@interface KMOCROperation ()

@property (assign, nonatomic, getter = isExecuting)     BOOL executing;
@property (assign, nonatomic, getter = isFinished)      BOOL finished;
@property (assign, nonatomic, getter = isCancelled)     BOOL cancelled;

@property (nonatomic, assign) NSInteger timerCycleCount;
@property (nonatomic, assign) NSInteger imageIndex;
@property (nonatomic, strong) NSURLSessionDataTask *task;
@property (nonatomic, strong) NSString *fileID;
@property (nonatomic, strong) NSString *fileName;
@property (nonatomic, strong) NSImage *orcImage;
@property (nonatomic, strong) NSURL *fileURL;

@end

@implementation KMOCROperation
- (void)dealloc {

}

- (instancetype)initWithRecognitionImage:(NSImage *)image withImageIndex:(NSInteger)imageIndex {
    self = [super init];
    if (self) {
        self.timerCycleCount = 0;
        self.orcImage = image;
        self.imageIndex = imageIndex;
        self.fileName = [self fileNameWithDate];
        self.fileID = nil;
        self.fileURL = nil;
        
        self.queuePriority = NSOperationQueuePriorityNormal;
        
        self.name = self.fileName;
        self.executing = NO;
        self.finished = NO;
    }
    return self;
}

#pragma mark - overwrite
- (void)start {
    if ([self p_checkCancelled]) {
        return;
    }
    
    self.executing  = YES;
    
    [NSThread detachNewThreadSelector:@selector(main) toTarget:self withObject:nil];
}

- (void)main {
    @try {
        if ([self p_checkCancelled]) {
            return;
        }
        [self recognitionImage:_orcImage];
        
        while (self.executing) {
            if ([self p_checkCancelled]) {
                return;
            }
        }
    }
    @catch (NSException * e) {
        NSLog(@"Exception %@", e);
    }
}

- (void)cancel
{
    [super cancel];
    
    if (self.task) {
        [self.task cancel];
        self.task = nil;
    }
    
    if ([self.operationDelegate respondsToSelector:@selector(OCROperation:cancelOCRImageAtIndex:)]) {
        [self.operationDelegate OCROperation:self cancelOCRImageAtIndex:_imageIndex];
    }
    
    if (self.executing) {
        self.executing = NO;
        self.finished = YES;
    } else {
        self.finished = NO;
    }
    self.cancelled = YES;
}

#pragma mark - private methods
- (void)p_done
{
    self.executing  = NO;
    self.finished   = YES;
}

- (BOOL)p_checkCancelled
{
    if (self.cancelled) {
        self.finished = YES;
        return YES;
    }
    return NO;
}

- (void)recognitionImage:(NSImage *)image {
    __weak typeof(self)weakSelf = self;
    NSString *binaryImageData = [self base64EncodeImage:image];
    if (!binaryImageData) {
        return;
    }
    NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:[NSURL URLWithString:KMOCR_API_URL]];
    [request setHTTPMethod: @"POST"];
    [request addValue:@"application/json" forHTTPHeaderField:@"Content-Type"];
    NSDictionary *paramsDictionary = @{@"apikey":KMOCR_API_KEY,
                                           @"input":@"base64",
                                           @"file":binaryImageData,
                                           @"outputformat":self.fileType,
                                           @"filename":@"1.pdf",
                                           @"options":@{@"ocr_enabled":@(true),
                                                        @"ocr_settings":@{@"langs":self.selectedLanguages}}};
    NSError *error;
    NSData *requestData = [NSJSONSerialization dataWithJSONObject:paramsDictionary options:0 error:&error];
    [request setHTTPBody:requestData];

    NSURLSession *URLSession = [NSURLSession sharedSession];
    NSURLSessionDataTask *convertTask = [URLSession dataTaskWithRequest:request completionHandler:^ (NSData *data, NSURLResponse *response, NSError *error) {
        if (!data) {
            return;
        }
        if (NSURLErrorCancelled == error.code) {
            return ;
        }
        NSDictionary *dictionary = [NSJSONSerialization JSONObjectWithData:data options:NSJSONReadingMutableLeaves error:nil];
        NSString *statusStr = dictionary[@"status"];
        NSLog(@"dictionary == %@",dictionary);
        
        if ([statusStr isEqualToString:@"ok"]) {
            weakSelf.fileID = dictionary[@"data"][@"id"];
            
            dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(15.0 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
                [weakSelf requestQuery];
            });
        } else {
            [weakSelf cancel];
//            NSError *err = [self errorWithContent:dictionary[@"error"]];
            if ([weakSelf.operationDelegate respondsToSelector:@selector(OCROperation:failureOCRImageAtIndex:error:)]) {
                [weakSelf.operationDelegate OCROperation:weakSelf failureOCRImageAtIndex:weakSelf.imageIndex error:error];
            }
        }
    }];
    [convertTask resume];
}

#pragma mark - Private Methods
- (NSString *)fileNameWithDate
{
    NSDateFormatter *formatter = [[NSDateFormatter alloc ] init];
    [formatter setDateFormat:@"YYYY-MM-dd-hh-mm-ss-SSS"];
    NSString *dateString = [formatter stringFromDate:[NSDate date]];
    NSString *fileName = [NSString stringWithFormat:@"%@ %ld",dateString, (long)_imageIndex];
    return fileName;
}

- (NSData *)compressImageData:(NSData *)imageData toMaxFileSize:(NSInteger)maxFileSize {
    CGFloat compression = 0.9f;
    CGFloat maxCompression = 0.1f;
    NSData *compressImageData = imageData;
    while ([imageData length] > maxFileSize && compression > maxCompression) {
        compression -= 0.1;
        NSBitmapImageRep *imageRep = [NSBitmapImageRep imageRepWithData:imageData];
        compressImageData = [imageRep representationUsingType:NSJPEGFileType
                                                   properties:@{NSImageCompressionFactor:@(compression)}];
    }
    return compressImageData;
}

- (NSString *)base64EncodeImage:(NSImage *)image {
    NSData *data = [image TIFFRepresentation];
    NSBitmapImageRep *imageRep = [NSBitmapImageRep imageRepWithData:data];
    [imageRep setSize:[image size]];
    NSData *imagedata = [imageRep representationUsingType:NSBitmapImageFileTypePNG properties:@{}];
    
    // Resize the image if it exceeds the 4MB API limit
    if ([imagedata length] > 4194304) {
        imagedata = [self compressImageData:imagedata toMaxFileSize:4194304];
    }
    
    NSString *base64String = nil;
    if ([imagedata respondsToSelector:@selector(base64EncodedStringWithOptions:)]) {
        base64String = [imagedata base64EncodedStringWithOptions:NSDataBase64EncodingEndLineWithCarriageReturn];
    } else {
        base64String = [imagedata base64Encoding];
    }
    return base64String;
}

- (NSError *)errorWithContent:(NSString *)content {
    NSError *error;
    NSString *domain = @"com.MyCompany.MyApplication.ErrorDomain";
    NSDictionary *userInfo = @{ NSLocalizedDescriptionKey : content };
    error = [NSError errorWithDomain:domain code:-101 userInfo:userInfo];
    
    return error;
}

#pragma mark - NSTimer
- (void)requestQuery {
    __weak typeof(self)weakSelf = self;
    NSString *urlString = [NSString stringWithFormat:@"%@/%@/status",KMOCR_API_URL,_fileID];
    NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:[NSURL URLWithString:urlString]];
    [request setHTTPMethod: @"GET"];
    
    NSURLSession *URLSession = [NSURLSession sharedSession];
    self.task = [URLSession dataTaskWithRequest:request completionHandler:^ (NSData *data, NSURLResponse *response, NSError *error) {
        if (NSURLErrorCancelled == error.code) {
            return ;
        }
        NSDictionary *dictionary = [NSJSONSerialization JSONObjectWithData:data options:NSJSONReadingMutableLeaves error:nil];
        NSString *statusStr = dictionary[@"status"];
        NSLog(@"dictionary == %@",dictionary);
        weakSelf.fileID = dictionary[@"data"][@"id"];
        if ([statusStr isEqualToString:@"ok"]) {
            if (weakSelf.task) {
                [weakSelf.task cancel];
                weakSelf.task = nil;
            }
            NSDictionary *fileData = dictionary[@"data"];
            if ([fileData[@"step_percent"] intValue] == 100) {
                weakSelf.fileURL = [NSURL URLWithString:fileData[@"output"][@"url"]];
                [weakSelf getResultFileContent];
            } else {
                if (weakSelf.timerCycleCount >= KMTIMER_MAXIMUM_CYCLE) {
                    [weakSelf cancel];
//                    NSError *err = [self errorWithContent:@"Network Timeout"];
                    if ([weakSelf.operationDelegate respondsToSelector:@selector(OCROperation:failureOCRImageAtIndex:error:)]) {
                        [weakSelf.operationDelegate OCROperation:weakSelf failureOCRImageAtIndex:weakSelf.imageIndex error:error];
                    }
                } else {
                    weakSelf.timerCycleCount++;
                    dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(5.0 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
                        [weakSelf requestQuery];
                    });
                }
            }
        } else {
            [weakSelf cancel];
//            NSError *err = [self errorWithContent:dictionary[@"error"]];
            if ([weakSelf.operationDelegate respondsToSelector:@selector(OCROperation:failureOCRImageAtIndex:error:)]) {
                [weakSelf.operationDelegate OCROperation:weakSelf failureOCRImageAtIndex:weakSelf.imageIndex error:error];
            }
        }
    }];
    [self.task resume];
}

#pragma mark - Get Result File Content
- (void)getResultFileContent {
    __weak typeof(self)weakSelf = self;
    NSString *urlString = [NSString stringWithFormat:@"%@/%@/dl",KMOCR_API_URL,_fileID];
    NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:[NSURL URLWithString:urlString]];
    [request setHTTPMethod: @"GET"];
    
    NSURLSession *URLSession = [NSURLSession sharedSession];
    NSURLSessionDataTask *fileContentTask = [URLSession dataTaskWithRequest:request completionHandler:^ (NSData *data, NSURLResponse *response, NSError *error) {
        if (NSURLErrorCancelled == error.code) {
            return ;
        }
        NSData *fileData;
        NSError *err;
        NSDictionary *dictionary = [NSJSONSerialization JSONObjectWithData:data options:NSJSONReadingMutableLeaves error:nil];
        NSLog(@"dictionary == %@",dictionary);
        NSString *statusStr = dictionary[@"status"];
        if ([statusStr isEqualToString:@"ok"]) {
            fileData = [self responseDataResults:dictionary];
        } else {
        //            err = [self errorWithContent:dictionary[@"error"]];
        }
        
        dispatch_async(dispatch_get_main_queue(), ^{
            if (error || !fileData) {
                [self cancel];
                if ([weakSelf.operationDelegate respondsToSelector:@selector(OCROperation:failureOCRImageAtIndex:error:)]) {
                    [weakSelf.operationDelegate OCROperation:self failureOCRImageAtIndex:weakSelf.imageIndex error:error];
                }
            } else {
                NSArray *arr = @[fileData];
                NSLog(@"self.imageIndex == %ld",(long)self.imageIndex);
                if ([weakSelf.operationDelegate respondsToSelector:@selector(OCROperation:finishOCRImageAtIndex:results:)]) {
                    [weakSelf.operationDelegate OCROperation:weakSelf finishOCRImageAtIndex:weakSelf.imageIndex results:arr];
                }
                [self p_done];
            }
        });
//        if ([statusStr isEqualToString:@"ok"]) {
//            [self p_done];
//        } else {
//            [self cancel];
//        }
    }];
    [fileContentTask resume];
}

- (NSData *)responseDataResults:(NSDictionary *)dictionary {
    NSDictionary *responses = [dictionary isKindOfClass:[NSDictionary class]] ? [dictionary objectForKey:@"data"] : nil;
    NSString *stringBase64 = responses[@"content"];
    NSData *data = [[NSData alloc] initWithBase64EncodedString:stringBase64 options:0];
    [data writeToFile:self.filePath atomically:YES];
    return data;
}
@end