//
//  ImageManager.m
//
//  Created by Ivan Pusic on 5/4/16.
//  Copyright © 2016 Facebook. All rights reserved.
//

#import "ImageCropPicker.h"

#define ERROR_PICKER_CANCEL_KEY @"picker_cancel"
#define ERROR_PICKER_CANCEL_MSG @"User cancelled image selection"

#define ERROR_CANNOT_SAVE_IMAGE_KEY @"cannot_save_image"
#define ERROR_CANNOT_SAVE_IMAGE_MSG @"Cannot save image. Unable to write to tmp location."

@implementation ImageCropPicker

RCT_EXPORT_MODULE();


- (instancetype)init
{
    if (self = [super init]) {
        self.defaultOptions = @{
                                @"multiple": @NO,
                                @"cropping": @NO,
                                @"maxFiles": @5,
                                @"width": @200,
                                @"height": @200
                                };
    }
    
    return self;
}


RCT_EXPORT_METHOD(openPicker:(NSDictionary *)options
                  resolver:(RCTPromiseResolveBlock)resolve
                  rejecter:(RCTPromiseRejectBlock)reject) {
    
    self.resolve = resolve;
    self.reject = reject;
    self.options = [NSMutableDictionary dictionaryWithDictionary:self.defaultOptions];
    for (NSString *key in options.keyEnumerator) {
        [self.options setValue:options[key] forKey:key];
    }
    
    [PHPhotoLibrary requestAuthorization:^(PHAuthorizationStatus status) {
        dispatch_async(dispatch_get_main_queue(), ^{
            // init picker
            QBImagePickerController *imagePickerController =
            [QBImagePickerController new];
            imagePickerController.delegate = self;
            imagePickerController.allowsMultipleSelection = true;
            imagePickerController.maximumNumberOfSelection = [[self.options objectForKey:@"maxFiles"] intValue];
            imagePickerController.showsNumberOfSelectedAssets = YES;
            imagePickerController.mediaType = QBImagePickerMediaTypeImage;
            
            UIViewController *root = [[[[UIApplication sharedApplication] delegate] window] rootViewController];
            dispatch_async(dispatch_get_main_queue(), ^{
                if (root.presentedViewController) {
                    [root.presentedViewController presentViewController:imagePickerController animated:YES completion:nil];
                } else {
                    [root presentViewController:imagePickerController animated:YES completion:nil];
                }
            });
        });
    }];
}
RCT_EXPORT_METHOD(launchCamera:(RCTResponseSenderBlock)callback)
{
    self.callback = callback;
      self.picker = [[UIImagePickerController alloc] init];
#if TARGET_IPHONE_SIMULATOR
    self.callback(@[@{@"error": @"Camera not available on simulator"}]);
    return;
#else
    self.picker.sourceType = UIImagePickerControllerSourceTypeCamera;
    if ([[self.options objectForKey:@"cameraType"] isEqualToString:@"front"]) {
        self.picker.cameraDevice = UIImagePickerControllerCameraDeviceFront;
    }
    else { // "back"
        self.picker.cameraDevice = UIImagePickerControllerCameraDeviceRear;
    }
#endif
    self.picker.modalPresentationStyle = UIModalPresentationCurrentContext;
      self.picker.mediaTypes = @[(NSString *)kUTTypeImage];
    self.picker.allowsEditing = NO;
    self.picker.modalPresentationStyle = UIModalPresentationCurrentContext;
      self.picker.videoQuality = UIImagePickerControllerQualityTypeHigh;
    self.picker.delegate = self;
    // Check permissions
    void (^showPickerViewController)() = ^void() {
        dispatch_async(dispatch_get_main_queue(), ^{
            UIViewController *root = [[[[UIApplication sharedApplication] delegate] window] rootViewController];
            while (root.presentedViewController != nil) {
                root = root.presentedViewController;
            }
            [root presentViewController:self.picker animated:YES completion:nil];
        });
    };
    
    [self checkCameraPermissions:^(BOOL granted) {
        if (!granted) {
            self.callback(@[@{@"error": @"Camera permissions not granted"}]);
            return;
        }
        
        showPickerViewController();
    }];
    
    
}
- (void)imagePickerController:(UIImagePickerController *)picker didFinishPickingMediaWithInfo:(NSDictionary *)info
{
      NSURL *imageurl  = [info objectForKey: UIImagePickerControllerReferenceURL];
      NSString *tempFileName = [[NSUUID UUID] UUIDString];
      NSString* fileName = [tempFileName stringByAppendingString:@".jpg"];
    
      NSString *path = [[NSTemporaryDirectory()stringByStandardizingPath] stringByAppendingPathComponent:fileName];
   dispatch_block_t dismissCompletionBlock = ^{
       UIImage* originalImage = (UIImage*)[info objectForKey:UIImagePickerControllerOriginalImage];
       originalImage = [self fixOrientation:originalImage];
       NSData *imageData = UIImagePNGRepresentation(originalImage);
       [imageData writeToFile:path atomically:YES];
         NSURL *fileURL = [NSURL fileURLWithPath:path];
       self.callback(@[@{@"imageURl":[fileURL absoluteString]}]);
       
   };
    dispatch_async(dispatch_get_main_queue(), ^{
        [picker dismissViewControllerAnimated:YES completion:dismissCompletionBlock];
    });

    
}
- (UIImage *)fixOrientation:(UIImage *)srcImg {
    if (srcImg.imageOrientation == UIImageOrientationUp) {
        return srcImg;
    }
    
    CGAffineTransform transform = CGAffineTransformIdentity;
    switch (srcImg.imageOrientation) {
        case UIImageOrientationDown:
        case UIImageOrientationDownMirrored:
            transform = CGAffineTransformTranslate(transform, srcImg.size.width, srcImg.size.height);
            transform = CGAffineTransformRotate(transform, M_PI);
            break;
            
        case UIImageOrientationLeft:
        case UIImageOrientationLeftMirrored:
            transform = CGAffineTransformTranslate(transform, srcImg.size.width, 0);
            transform = CGAffineTransformRotate(transform, M_PI_2);
            break;
            
        case UIImageOrientationRight:
        case UIImageOrientationRightMirrored:
            transform = CGAffineTransformTranslate(transform, 0, srcImg.size.height);
            transform = CGAffineTransformRotate(transform, -M_PI_2);
            break;
        case UIImageOrientationUp:
        case UIImageOrientationUpMirrored:
            break;
    }
    
    switch (srcImg.imageOrientation) {
        case UIImageOrientationUpMirrored:
        case UIImageOrientationDownMirrored:
            transform = CGAffineTransformTranslate(transform, srcImg.size.width, 0);
            transform = CGAffineTransformScale(transform, -1, 1);
            break;
            
        case UIImageOrientationLeftMirrored:
        case UIImageOrientationRightMirrored:
            transform = CGAffineTransformTranslate(transform, srcImg.size.height, 0);
            transform = CGAffineTransformScale(transform, -1, 1);
            break;
        case UIImageOrientationUp:
        case UIImageOrientationDown:
        case UIImageOrientationLeft:
        case UIImageOrientationRight:
            break;
    }
    
    CGContextRef ctx = CGBitmapContextCreate(NULL, srcImg.size.width, srcImg.size.height, CGImageGetBitsPerComponent(srcImg.CGImage), 0, CGImageGetColorSpace(srcImg.CGImage), CGImageGetBitmapInfo(srcImg.CGImage));
    CGContextConcatCTM(ctx, transform);
    switch (srcImg.imageOrientation) {
        case UIImageOrientationLeft:
        case UIImageOrientationLeftMirrored:
        case UIImageOrientationRight:
        case UIImageOrientationRightMirrored:
            CGContextDrawImage(ctx, CGRectMake(0,0,srcImg.size.height,srcImg.size.width), srcImg.CGImage);
            break;
            
        default:
            CGContextDrawImage(ctx, CGRectMake(0,0,srcImg.size.width,srcImg.size.height), srcImg.CGImage);
            break;
    }
    
    CGImageRef cgimg = CGBitmapContextCreateImage(ctx);
    UIImage *img = [UIImage imageWithCGImage:cgimg];
    CGContextRelease(ctx);
    CGImageRelease(cgimg);
    return img;
}

- (void)qb_imagePickerController:
(QBImagePickerController *)imagePickerController
          didFinishPickingAssets:(NSArray *)assets {
    
    PHImageManager *manager = [PHImageManager defaultManager];
    
    if ([[[self options] objectForKey:@"multiple"] boolValue]) {
        NSMutableArray *images = [[NSMutableArray alloc] init];
        PHImageRequestOptions* options = [[PHImageRequestOptions alloc] init];
        options.synchronous = YES;
        
        for (PHAsset *asset in assets) {
            [manager
             requestImageDataForAsset:asset
             options:options
             resultHandler:^(NSData *imageData, NSString *dataUTI, UIImageOrientation orientation, NSDictionary *info) {
                 UIImage *image = [UIImage imageWithData:imageData];
                 NSData *data = UIImageJPEGRepresentation(image, 1);
                 
                 NSString *filePath = [self persistFile:data];
                 if (filePath == nil) {
                     self.reject(ERROR_CANNOT_SAVE_IMAGE_KEY, ERROR_CANNOT_SAVE_IMAGE_MSG, nil);
                     return;
                 }
                 
                 [images addObject:@{
                                     @"path": filePath,
                                     @"width": @(asset.pixelWidth),
                                     @"height": @(asset.pixelHeight),
                                     @"mime": @"image/jpeg",
                                     @"size": [NSNumber numberWithUnsignedInteger:data.length]
                                     }];
             }];
        }
        
        self.resolve(images);
        [imagePickerController dismissViewControllerAnimated:YES completion:nil];
    } else {
        PHAsset *asset = [assets objectAtIndex:0];
        
        [manager
         requestImageDataForAsset:asset
         options:nil
         resultHandler:^(NSData *imageData, NSString *dataUTI,
                         UIImageOrientation orientation,
                         NSDictionary *info) {
             
             if ([[[self options] objectForKey:@"cropping"] boolValue]) {
                 UIImage *image = [UIImage imageWithData:imageData];
                 RSKImageCropViewController *imageCropVC = [[RSKImageCropViewController alloc] initWithImage:image cropMode:RSKImageCropModeCustom];
                 
                 imageCropVC.avoidEmptySpaceAroundImage = YES;
                 imageCropVC.dataSource = self;
                 imageCropVC.delegate = self;
                 
                 [imagePickerController dismissViewControllerAnimated:YES completion:^{
                     UIViewController *root = [[[[UIApplication sharedApplication] delegate] window] rootViewController];
                     dispatch_async(dispatch_get_main_queue(), ^{
                         if (root.presentedViewController) {
                             [root.presentedViewController presentViewController:imageCropVC animated:YES completion:nil];
                         } else {
                             [root presentViewController:imageCropVC animated:YES completion:nil];
                         }
                     });
                 }];
             } else {
                 UIImage *image = [UIImage imageWithData:imageData];
                 NSData *data = UIImageJPEGRepresentation(image, 1);
                 NSString *filePath = [self persistFile:data];
                 if (filePath == nil) {
                     self.reject(ERROR_CANNOT_SAVE_IMAGE_KEY, ERROR_CANNOT_SAVE_IMAGE_MSG, nil);
                     return;
                 }
                 
                 self.resolve(@{
                                @"path": filePath,
                                @"width": @(asset.pixelWidth),
                                @"height": @(asset.pixelHeight),
                                @"mime": @"image/jpeg",
                                @"size": [NSNumber numberWithUnsignedInteger:data.length]
                                });
                 [imagePickerController dismissViewControllerAnimated:YES completion:nil];
             }
         }];
    }
}

- (void)qb_imagePickerControllerDidCancel:(QBImagePickerController *)imagePickerController {
    self.reject(ERROR_PICKER_CANCEL_KEY, ERROR_PICKER_CANCEL_MSG, nil);
    [imagePickerController dismissViewControllerAnimated:YES completion:nil];
}

#pragma mark - CustomCropModeDelegates

// Returns a custom rect for the mask.
- (CGRect)imageCropViewControllerCustomMaskRect:
(RSKImageCropViewController *)controller {
    CGSize maskSize = CGSizeMake(
                                 [[self.options objectForKey:@"width"] intValue],
                                 [[self.options objectForKey:@"height"] intValue]);
    
    CGFloat viewWidth = CGRectGetWidth(controller.view.frame);
    CGFloat viewHeight = CGRectGetHeight(controller.view.frame);
    
    CGRect maskRect = CGRectMake((viewWidth - maskSize.width) * 0.5f,
                                 (viewHeight - maskSize.height) * 0.5f,
                                 maskSize.width, maskSize.height);
    
    return maskRect;
}

// if provided width or height is bigger than screen w/h,
// then we should scale draw area
- (CGRect) scaleRect:(RSKImageCropViewController *)controller {
    CGRect rect = controller.maskRect;
    CGFloat viewWidth = CGRectGetWidth(controller.view.frame);
    CGFloat viewHeight = CGRectGetHeight(controller.view.frame);
    
    if (rect.size.width > viewWidth) {
        float scaleFactor = viewWidth / rect.size.width;
        rect.size.width *= scaleFactor;
        rect.size.height *= scaleFactor;
        rect.origin.x = 0;
        rect.origin.y = viewHeight / 2 * 0.5f;
    } else if (rect.size.height > viewHeight) {
        float scaleFactor = viewHeight / rect.size.height;
        rect.size.width *= scaleFactor;
        rect.size.height *= scaleFactor;
        rect.origin.x = viewWidth / 2 * 0.5f;
        rect.origin.y = 0;
    }
    
    return rect;
}

// Returns a custom path for the mask.
- (UIBezierPath *)imageCropViewControllerCustomMaskPath:
(RSKImageCropViewController *)controller {
    CGRect rect = [self scaleRect:controller];
    UIBezierPath *path = [UIBezierPath bezierPathWithRoundedRect:rect
                                               byRoundingCorners:UIRectCornerAllCorners
                                                     cornerRadii:CGSizeMake(0, 0)];
    return path;
}

// Returns a custom rect in which the image can be moved.
- (CGRect)imageCropViewControllerCustomMovementRect:
(RSKImageCropViewController *)controller {
    return [self scaleRect:controller];
}

#pragma mark - CropFinishDelegate

// Crop image has been canceled.
- (void)imageCropViewControllerDidCancelCrop:
(RSKImageCropViewController *)controller {
    self.reject(ERROR_PICKER_CANCEL_KEY, ERROR_PICKER_CANCEL_MSG, nil);
    [controller dismissViewControllerAnimated:YES completion:nil];
}

// The original image has been cropped.
- (void)imageCropViewController:(RSKImageCropViewController *)controller
                   didCropImage:(UIImage *)croppedImage
                  usingCropRect:(CGRect)cropRect {
    
    // we have correct rect, but not correct dimensions
    // so resize image
    CGSize resizedImageSize = CGSizeMake([[[self options] objectForKey:@"width"] intValue], [[[self options] objectForKey:@"height"] intValue]);
    UIImage *resizedImage = [croppedImage resizedImageToFitInSize:resizedImageSize scaleIfSmaller:YES];
    NSData *data = UIImageJPEGRepresentation(resizedImage, 1);
    
    NSString *filePath = [self persistFile:data];
    if (filePath == nil) {
        self.reject(ERROR_CANNOT_SAVE_IMAGE_KEY, ERROR_CANNOT_SAVE_IMAGE_MSG, nil);
        return;
    }
    
    NSDictionary *image = @{
                            @"path": filePath,
                            @"width": @(resizedImage.size.width),
                            @"height": @(resizedImage.size.height),
                            @"mime": @"image/jpeg",
                            @"size": [NSNumber numberWithUnsignedInteger:data.length]
                            };
    
    self.resolve(image);
    [controller dismissViewControllerAnimated:YES completion:nil];
}

// at the moment it is not possible to upload image by reading PHAsset
// we are saving image and saving it to the tmp location where we are allowed to access image later
- (NSString*) persistFile:(NSData*)data {
    // create temp file
    NSString *filePath = [NSTemporaryDirectory() stringByAppendingString:[[NSUUID UUID] UUIDString]];
    filePath = [filePath stringByAppendingString:@".jpg"];
    
    // save cropped file
    BOOL status = [data writeToFile:filePath atomically:YES];
    if (!status) {
        return nil;
    }
    
    return filePath;
}

// The original image has been cropped. Additionally provides a rotation angle
// used to produce image.
- (void)imageCropViewController:(RSKImageCropViewController *)controller
                   didCropImage:(UIImage *)croppedImage
                  usingCropRect:(CGRect)cropRect
                  rotationAngle:(CGFloat)rotationAngle {
    [self imageCropViewController:controller didCropImage:croppedImage usingCropRect:cropRect];
}

- (void)checkCameraPermissions:(void(^)(BOOL granted))callback
{
    AVAuthorizationStatus status = [AVCaptureDevice authorizationStatusForMediaType:AVMediaTypeVideo];
    if (status == AVAuthorizationStatusAuthorized) {
        callback(YES);
        return;
    } else if (status == AVAuthorizationStatusNotDetermined){
        [AVCaptureDevice requestAccessForMediaType:AVMediaTypeVideo completionHandler:^(BOOL granted) {
            callback(granted);
            return;
        }];
    } else {
        callback(NO);
    }
}

@end
