#import "ShareViewController.h"
#import <React/RCTBundleURLProvider.h>
#import <React/RCTRootView.h>
#import "PerformRequests.h"
#import "SessionManager.h"

NSExtensionContext* extensionContext;

@implementation ShareViewController
+ (BOOL)requiresMainQueueSetup
{
  return YES;
}

@synthesize bridge = _bridge;

- (UIView*) shareView {
  NSURL *jsCodeLocation;
  
  jsCodeLocation = [[RCTBundleURLProvider sharedSettings] jsBundleURLForBundleRoot:@"share.ios" fallbackResource:nil];
  
  RCTRootView *rootView = [[RCTRootView alloc] initWithBundleURL:jsCodeLocation
                                                      moduleName:@"MattermostShare"
                                               initialProperties:nil
                                                   launchOptions:nil];
  rootView.backgroundColor = nil;
  return rootView;
}

RCT_EXPORT_MODULE(MattermostShare);

- (void)viewDidLoad {
  [super viewDidLoad];
  extensionContext = self.extensionContext;
  UIView *rootView = [self shareView];
  if (rootView.backgroundColor == nil) {
    rootView.backgroundColor = [[UIColor alloc] initWithRed:1 green:1 blue:1 alpha:0.1];
  }
  
  self.view = rootView;
}

RCT_REMAP_METHOD(getOrientation,
                  resolver:(RCTPromiseResolveBlock)resolve
                  rejecter:(RCTPromiseRejectBlock)reject) {
  if([UIScreen mainScreen].bounds.size.width < [UIScreen mainScreen].bounds.size.height) {
    resolve(@"PORTRAIT");
  } else {
    resolve(@"LANDSCAPE");
  }
}

RCT_EXPORT_METHOD(close:(NSDictionary *)data appGroupId:(NSString *)appGroupId) {
  if (data != nil) {
    NSString *requestId = [data objectForKey:@"requestId"];
    NSString *useBackgroundUpload = [data objectForKey:@"useBackgroundUpload"];
    NSString *certificateName = [data objectForKey:@"certificate"];
    BOOL tryToUploadInTheBackgound = useBackgroundUpload ? [useBackgroundUpload boolValue] : NO;

    if (tryToUploadInTheBackgound) {
      NSString *requestWithGroup = [NSString stringWithFormat:@"%@|%@", requestId, appGroupId];
      [SessionManager sharedSession].closeExtension = ^{
         [extensionContext completeRequestReturningItems:nil
                                        completionHandler:nil];
         NSLog(@"Extension closed");
      };

      [SessionManager sharedSession].sendShareEvent = ^(NSString* eventName) {
        NSLog(@"Send Share Extension Event to JS");
        [_bridge enqueueJSCall:@"RCTDeviceEventEmitter"
                        method:@"emit"
                          args:@[eventName]
                    completion:nil];
      };

      [[SessionManager sharedSession] setRequestWithGroup:requestWithGroup certificateName:certificateName];
      [[SessionManager sharedSession] setDataForRequest:data forRequestWithGroup:requestWithGroup];
      [[SessionManager sharedSession] createPostForRequest:requestWithGroup];
    } else {
      NSDictionary *post = [data objectForKey:@"post"];
      NSArray *files = [data objectForKey:@"files"];
      PerformRequests *request = [[PerformRequests alloc] initWithPost:post withFiles:files forRequestId:requestId inAppGroupId:appGroupId inContext:extensionContext];
      [request createPost];
    }
  } else {
    [extensionContext completeRequestReturningItems:nil
                                completionHandler:nil];
    NSLog(@"Extension closed");
  }
}

RCT_REMAP_METHOD(data,
                 appGroupId: (NSString *)appGroupId
                 resolver:(RCTPromiseResolveBlock)resolve
                 rejecter:(RCTPromiseRejectBlock)reject)
{
  [self extractDataFromContext: extensionContext withAppGroup: appGroupId andCallback:^(NSArray* items ,NSError* err) {
    if (err) {
      reject(@"data", @"Failed to extract attachment content", err);
      return;
    }
    resolve(items);
  }];
}

typedef void (^ProviderCallback)(NSString *content, NSString *contentType, BOOL owner, NSError *err);

- (void)extractDataFromContext:(NSExtensionContext *)context withAppGroup:(NSString *) appGroupId andCallback:(void(^)(NSArray *items ,NSError *err))callback {
  @try {
    NSExtensionItem *item = [context.inputItems firstObject];
    NSArray *attachments = item.attachments;
    NSMutableArray *items = [[NSMutableArray alloc] init];
    
    __block int attachmentIdx = 0;
    __block ProviderCallback providerCb = nil;
    __block __weak ProviderCallback weakProviderCb = nil;
    providerCb = ^ void (NSString *content, NSString *contentType, BOOL owner, NSError *err) {
      if (err) {
        callback(nil, err);
        return;
      }
      
      if (content != nil) {
        [items addObject:@{
                           @"type": contentType,
                           @"value": content,
                           @"owner": [NSNumber numberWithBool:owner],
                           }];
      }
      
      ++attachmentIdx;
      if (attachmentIdx == [attachments count]) {
        callback(items, nil);
      } else {
        [self extractDataFromProvider:attachments[attachmentIdx] withAppGroup:appGroupId andCallback: weakProviderCb];
      }
    };
    weakProviderCb = providerCb;
    [self extractDataFromProvider:attachments[0] withAppGroup:appGroupId andCallback: providerCb];
  }
  @catch (NSException *exc) {
    NSError *error = [NSError errorWithDomain:@"fiftythree.paste" code:1 userInfo:@{
                                                                                    @"reason": [exc description]
                                                                                    }];
    callback(nil, error);
  }
}

- (void)extractDataFromProvider:(NSItemProvider *)provider withAppGroup:(NSString *) appGroupId andCallback:(void(^)(NSString* content, NSString* contentType, BOOL owner, NSError *err))callback {
  if([provider hasItemConformingToTypeIdentifier:@"public.movie"]) {
    [provider loadItemForTypeIdentifier:@"public.movie" options:nil completionHandler:^(id<NSSecureCoding, NSObject> item, NSError *error) {
      @try {
        if ([item isKindOfClass: NSURL.class]) {
          NSURL *url = (NSURL *)item;
          return callback([url absoluteString], @"public.movie", NO, nil);
        }
        return callback(nil, nil, NO, nil);
      }
      @catch(NSException *exc) {
        NSError *error = [NSError errorWithDomain:@"fiftythree.paste" code:2 userInfo:@{
                                                                                        @"reason": [exc description]
                                                                                        }];
        callback(nil, nil, NO, error);
      }
    }];
    return;
  }
  if([provider hasItemConformingToTypeIdentifier:@"public.image"]) {
    [provider loadItemForTypeIdentifier:@"public.image" options:nil completionHandler:^(id<NSSecureCoding, NSObject> item, NSError *error) {
      if (error) {
        callback(nil, nil, NO, error);
        return;
      }
      
      @try {
        if ([item isKindOfClass: NSURL.class]) {
          NSURL *url = (NSURL *)item;
          return callback([url absoluteString], @"public.image", NO, nil);
        } else if ([item isKindOfClass: UIImage.class]) {
          UIImage *image = (UIImage *)item;
          NSString *fileName = [NSString stringWithFormat:@"%@.jpg", [[NSUUID UUID] UUIDString]];
          NSURL *tempContainerURL = [[SessionManager sharedSession] tempContainerURL:appGroupId];
          if (tempContainerURL == nil){
            return callback(nil, nil, NO, nil);
          }
          
          NSURL *tempFileURL = [tempContainerURL URLByAppendingPathComponent: fileName];
          BOOL created = [UIImageJPEGRepresentation(image, 0.8) writeToFile:[tempFileURL path] atomically:YES];
          if (created) {
            return callback([tempFileURL absoluteString], @"public.image", YES, nil);
          } else {
            return callback(nil, nil, NO, nil);
          }
        } else if ([item isKindOfClass: NSData.class]) {
          NSString *fileName = [NSString stringWithFormat:@"%@.jpg", [[NSUUID UUID] UUIDString]];
          NSData *data = (NSData *)item;
          UIImage *image = [UIImage imageWithData:data];
          NSURL *tempContainerURL = [[SessionManager sharedSession] tempContainerURL:appGroupId];
          if (tempContainerURL == nil){
            return callback(nil, nil, NO, nil);
          }
          NSURL *tempFileURL = [tempContainerURL URLByAppendingPathComponent: fileName];
          BOOL created = [UIImageJPEGRepresentation(image, 0.8) writeToFile:[tempFileURL path] atomically:YES];
          if (created) {
            return callback([tempFileURL absoluteString], @"public.image", YES, nil);
          } else {
            return callback(nil, nil, NO, nil);
          }
        } else {
          // Do nothing, some type we don't support.
          return callback(nil, nil, NO, nil);
        }
      }
      @catch(NSException *exc) {
        NSError *error = [NSError errorWithDomain:@"fiftythree.paste" code:2 userInfo:@{
                                                                                        @"reason": [exc description]
                                                                                        }];
        callback(nil, nil, NO, error);
      }
    }];
    return;
  }
  
  if([provider hasItemConformingToTypeIdentifier:@"public.file-url"]) {
    [provider loadItemForTypeIdentifier:@"public.file-url" options:nil completionHandler:^(id<NSSecureCoding, NSObject> item, NSError *error) {
      if (error) {
        callback(nil, nil, NO, error);
        return;
      }
      
      if ([item isKindOfClass:NSURL.class]) {
        return callback([(NSURL *)item absoluteString], @"public.file-url", NO, nil);
      } else if ([item isKindOfClass:NSString.class]) {
        return callback((NSString *)item, @"public.file-url", NO, nil);
      }
      callback(nil, nil, NO, nil);
    }];
    return;
  }
  
  if([provider hasItemConformingToTypeIdentifier:@"public.url"]) {
    [provider loadItemForTypeIdentifier:@"public.url" options:nil completionHandler:^(id<NSSecureCoding, NSObject> item, NSError *error) {
      if (error) {
        callback(nil, nil, NO, error);
        return;
      }
      
      if ([item isKindOfClass:NSURL.class]) {
        return callback([(NSURL *)item absoluteString], @"public.url", NO, nil);
      } else if ([item isKindOfClass:NSString.class]) {
        return callback((NSString *)item, @"public.url", NO, nil);
      }
    }];
    return;
  }
  
  if([provider hasItemConformingToTypeIdentifier:@"public.plain-text"]) {
    [provider loadItemForTypeIdentifier:@"public.plain-text" options:nil completionHandler:^(id<NSSecureCoding, NSObject> item, NSError *error) {
      if (error) {
        callback(nil, nil, NO, error);
        return;
      }
      
      if ([item isKindOfClass:NSString.class]) {
        return callback((NSString *)item, @"public.plain-text", NO, nil);
      } else if ([item isKindOfClass:NSAttributedString.class]) {
        NSAttributedString *str = (NSAttributedString *)item;
        return callback([str string], @"public.plain-text", NO, nil);
      } else if ([item isKindOfClass:NSData.class]) {
        NSString *str = [[NSString alloc] initWithData:(NSData *)item encoding:NSUTF8StringEncoding];
        if (str) {
          return callback(str, @"public.plain-text", NO, nil);
        } else {
          return callback(nil, nil, NO, nil);
        }
      } else {
        return callback(nil, nil, NO, nil);
      }
    }];
    return;
  }
  
  callback(nil, nil, NO, nil);
}
@end
