#import "PlaytorchImage.h"
#import "ImageHostObject.h"

@implementation PlaytorchImage
RCT_EXPORT_MODULE()

- (void)install {
    // nothing
}

namespace foome {

  using namespace facebook;
  using namespace react;

  static jsi::Value __hostFunction_install(
      jsi::Runtime &rt,
      TurboModule &turboModule,
      const jsi::Value *args,
      size_t count) {

        auto propName = jsi::PropNameID::forUtf8(rt, "makeImage");
        auto funcImpl = [](jsi::Runtime& runtime,
                           const jsi::Value& thisValue,
                           const jsi::Value* arguments,
                           size_t count) -> jsi::Value {
          std::unique_ptr<playtorch::image::Image> image = playtorchimage::makeImage();
          auto imageHostObject = std::make_shared<playtorch::image::ImageHostObject>(runtime, std::move(image));
          return jsi::Object::createFromHostObject(runtime, std::move(imageHostObject));
        };

        auto makeImageImpl = jsi::Function::createFromHostFunction(rt, propName, 0, std::move(funcImpl));

        jsi::Object ns(rt);
        ns.setProperty(rt, "makeImage", std::move(makeImageImpl));
        rt.global().setProperty(rt, "__playtorch_image__", ns);

        return jsi::Value::undefined();
  }

  class JSI_EXPORT FooModule : public facebook::react::TurboModule {
  public:
    FooModule(std::string name, std::shared_ptr<CallInvoker> jsInvoker) :
    TurboModule(name, jsInvoker) {
      methodMap_["install"] = {0, __hostFunction_install};
    }

    // Note: keep this method declared inline to avoid conflicts
    // between RTTI and non-RTTI compilation units
    facebook::jsi::Value get(
        facebook::jsi::Runtime &runtime,
        const facebook::jsi::PropNameID &propName) override {
      {
        std::string propNameUtf8 = propName.utf8(runtime);
         auto p = methodMap_.find(propNameUtf8);
        if (p == methodMap_.end()) {
          // Method was not found, let JS decide what to do.
          return facebook::jsi::Value::undefined();
        } else {
          return get(runtime, propName, p->second);
        }
      }
    }

    jsi::Value get(
        jsi::Runtime &runtime,
        const jsi::PropNameID &propName,
        const MethodMetadata &meta) {
      auto result = jsi::Function::createFromHostFunction(
          runtime,
          propName,
          static_cast<unsigned int>(meta.argCount),
          [this, meta](
              jsi::Runtime &rt,
              const jsi::Value &thisVal,
              const jsi::Value *args,
              size_t count) { return meta.invoker(rt, *this, args, count); });
//      // If we have a JS wrapper, cache the result of this lookup
//      // We don't cache misses, to allow for methodMap_ to dynamically be extended
//      if (jsRepresentation_) {
//        jsRepresentation_->setProperty(runtime, propName, result);
//      }
      return result;
    }

  };

}

// Don't compile this code when we build for the old architecture.
#ifdef RCT_NEW_ARCH_ENABLED
- (std::shared_ptr<facebook::react::TurboModule>)getTurboModule:
    (const facebook::react::ObjCTurboModule::InitParams &)params
{
//    return std::make_shared<facebook::react::NativePlaytorchImageSpecJSI>(params);
  return std::make_shared<foome::FooModule>(params.moduleName, params.jsInvoker);
}
#endif

@end
