/*******************************************************
 * Copyright (C) 2018 XMind Ltd. - All Rights Reserved
 *******************************************************/

#import <Cocoa/Cocoa.h>
#import <Foundation/Foundation.h>

#import "./sandbox.h"

NSString* ToNSStringNoCopy(const Nan::Utf8String& value) {
  const char* cstr = *value ? *value : "<string conversion failed>";
  return [[NSString alloc] initWithBytesNoCopy:(void*)cstr
                                        length:value.length()
                                      encoding:NSUTF8StringEncoding
                                  freeWhenDone:false];
}

NSString* ToNSString(const Nan::Utf8String& value) {
  const char* cstr = *value ? *value : "<string conversion failed>";
  return [[NSString alloc] initWithBytes:(void*)cstr
                                  length:value.length()
                                encoding:NSUTF8StringEncoding];
}

NAN_METHOD(SbCreateBookmarkDataForSecurityScopedResource) {
  if (info.Length() != 1 || !info[0]->IsString()) {
    Nan::ThrowTypeError("Invalid arguments");
    return;
  }

  v8::Local<v8::Context> context = info.GetIsolate()->GetCurrentContext();

  Nan::HandleScope scope;

  @autoreleasepool {
    Nan::Utf8String arg0(info[0]);
    NSString * filePath = ToNSStringNoCopy(arg0);

    NSError * error;

    NSURL * url = [NSURL fileURLWithPath:filePath];
    NSData * bookmarkData = [url bookmarkDataWithOptions:NSURLBookmarkCreationWithSecurityScope
                          includingResourceValuesForKeys:nil
                                           relativeToURL:nil
                                                   error:&error];

    if (error != nil) {
      info.GetReturnValue().Set(Nan::New([error description].UTF8String).ToLocalChecked());
      return;
    }

    v8::Local<v8::Object> obj = Nan::New<v8::Object>();
    obj->Set(
      context,
      Nan::New("bookmarkData").ToLocalChecked(),
      Nan::New([bookmarkData base64EncodedStringWithOptions:0].UTF8String).ToLocalChecked()
    );

    info.GetReturnValue().Set(obj);
  }
}

NAN_METHOD(SbResolveBookmarkDataForSecurityScopedResource) {
  if (info.Length() != 1 || !info[0]->IsString()) {
    Nan::ThrowTypeError("Invalid arguments");
    return;
  }

  v8::Local<v8::Context> context = info.GetIsolate()->GetCurrentContext();

  Nan::HandleScope scope;

  @autoreleasepool {
    Nan::Utf8String arg0(info[0]);
    NSString * bookmarkDataStr = ToNSStringNoCopy(arg0);

    NSData * bookmarkData = [[NSData alloc] initWithBase64EncodedString:bookmarkDataStr options:0];

    BOOL isStale = false;
    NSError * error = nil;

    NSURL * url = [NSURL URLByResolvingBookmarkData:bookmarkData
                                            options:NSURLBookmarkResolutionWithSecurityScope
                                      relativeToURL:nil
                                bookmarkDataIsStale:&isStale
                                              error:&error];

    if (error != nil) {
      info.GetReturnValue().Set(Nan::New([error description].UTF8String).ToLocalChecked());
      return;
    }

    NSString * newBookmarkData = nil;
    if (isStale) {
      if ([url startAccessingSecurityScopedResource]) {
        error = nil;
        NSData * newData = [url bookmarkDataWithOptions:NSURLBookmarkCreationWithSecurityScope
                         includingResourceValuesForKeys:nil
                                          relativeToURL:nil
                                                  error:&error];
        [url stopAccessingSecurityScopedResource];

        if (error == nil) {
          newBookmarkData = [newData base64EncodedStringWithOptions:0];
        }
      }
    }

    v8::Local<v8::Object> obj = Nan::New<v8::Object>();
    obj->Set(
      context,
      Nan::New("filePath").ToLocalChecked(),
      Nan::New(url.path.UTF8String).ToLocalChecked()
    );
    obj->Set(
      context,
      Nan::New("newBookmarkData").ToLocalChecked(),
      Nan::New(newBookmarkData == nil ? "" : newBookmarkData.UTF8String).ToLocalChecked()
    );

    info.GetReturnValue().Set(obj);
  }
}

NAN_METHOD(SbStartAccessingSecurityScopedResource) {
  if (info.Length() != 1 || !info[0]->IsString()) {
    Nan::ThrowTypeError("Invalid arguments");
    return;
  }

  Nan::HandleScope scope;

  @autoreleasepool {
    Nan::Utf8String arg0(info[0]);
    NSString * bookmarkDataStr = ToNSStringNoCopy(arg0);
    NSData * bookmarkData = [[NSData alloc] initWithBase64EncodedString:bookmarkDataStr options:0];

    BOOL isStale = false;
    NSError * error = nil;

    NSURL * url = [NSURL URLByResolvingBookmarkData:bookmarkData
                                            options:NSURLBookmarkResolutionWithSecurityScope
                                      relativeToURL:nil
                                bookmarkDataIsStale:&isStale
                                              error:&error];

    if (error != nil) {
      info.GetReturnValue().Set(Nan::New([error description].UTF8String).ToLocalChecked());
    } else {
      BOOL result = [url startAccessingSecurityScopedResource];
      info.GetReturnValue().Set(Nan::New((bool)result));
    }
  }
}

NAN_METHOD(SbStopAccessingSecurityScopedResource) {
  if (info.Length() != 1 || !info[0]->IsString()) {
    Nan::ThrowTypeError("Invalid arguments");
    return;
  }

  Nan::HandleScope scope;

  @autoreleasepool {
    Nan::Utf8String arg0(info[0]);
    NSString * bookmarkDataStr = ToNSStringNoCopy(arg0);
    NSData * bookmarkData = [[NSData alloc] initWithBase64EncodedString:bookmarkDataStr options:0];

    BOOL isStale = false;
    NSError * error = nil;

    NSURL * url = [NSURL URLByResolvingBookmarkData:bookmarkData
                                            options:NSURLBookmarkResolutionWithSecurityScope
                                      relativeToURL:nil
                                bookmarkDataIsStale:&isStale
                                              error:&error];

    [url stopAccessingSecurityScopedResource];
  }
}
