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

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

#include "./tokenizer.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];
}

static NSLinguisticTagger* _tagger = NULL;
static NSLinguisticTagger* getTagger() {
  if (_tagger == NULL) {
    _tagger = [[NSLinguisticTagger alloc]
      initWithTagSchemes: @[NSLinguisticTagSchemeLemma]
                 options: NSLinguisticTaggerOmitWhitespace | NSLinguisticTaggerJoinNames | NSLinguisticTaggerOmitPunctuation];
  }
  return _tagger;
}

NAN_METHOD(Tokenize) {
  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 * input = ToNSStringNoCopy(arg0);
    NSMutableArray* queryWords = [[NSMutableArray alloc] init];

    NSLinguisticTagger * tagger = getTagger();
    tagger.string = input;

    [tagger enumerateTagsInRange:NSMakeRange(0, input.length)
                        scheme:NSLinguisticTagSchemeLemma
                      options:NSLinguisticTaggerOmitPunctuation | NSLinguisticTaggerOmitWhitespace
                    usingBlock:^(NSLinguisticTag  _Nullable tag, NSRange tokenRange, NSRange sentenceRange, BOOL * _Nonnull stop) {

      NSString * word = [input substringWithRange: tokenRange];
      NSString * lemma = nil;

      if (tag != nil && ![tag isEqualToString: word]) {
        lemma = tag;
      }

      NSDictionary * dict = @{
        @"lemma": lemma == nil ? @"" : lemma,
        @"word": word,
        @"start": [[NSNumber alloc] initWithUnsignedLong:tokenRange.location],
        @"length": [[NSNumber alloc] initWithUnsignedLong:tokenRange.length]
      };

      [queryWords addObject: dict];
    }];

    v8::Local<v8::Array> resultArr = Nan::New<v8::Array>([queryWords count]);
    uint32_t counter = 0;

    for (NSDictionary* resultObj in queryWords) {
      v8::Local<v8::Object> obj = Nan::New<v8::Object>();
      obj->Set(
        context,
        Nan::New("lemma").ToLocalChecked(),
        Nan::New(((NSString *)[resultObj objectForKey:@"lemma"]).UTF8String).ToLocalChecked()
      );
      obj->Set(
        context,
        Nan::New("word").ToLocalChecked(),
        Nan::New(((NSString *)[resultObj objectForKey:@"word"]).UTF8String).ToLocalChecked()
      );
      obj->Set(
        context,
        Nan::New("start").ToLocalChecked(),
        Nan::New(((NSNumber *)[resultObj objectForKey:@"start"]).intValue)
      );
      obj->Set(
        context,
        Nan::New("length").ToLocalChecked(),
        Nan::New(((NSNumber *)[resultObj objectForKey:@"length"]).intValue)
      );

      Nan::Set(resultArr, counter, obj);
      counter += 1;
    }

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