// Copyright (c) 2017, the Dart project authors.  Please see the AUTHORS file
// for details. All rights reserved. Use of this source code is governed by a
// BSD-style license that can be found in the LICENSE file.

#if !defined(DART_IO_DISABLED)

#include "bin/namespace.h"

#include "bin/builtin.h"
#include "bin/dartutils.h"
#include "include/dart_api.h"
#include "platform/globals.h"

namespace dart {
namespace bin {

static constexpr int kNamespaceNativeFieldIndex = 0;

static void ReleaseNamespace(void* isolate_callback_data, void* peer) {
  Namespace* namespc = reinterpret_cast<Namespace*>(peer);
  ASSERT(namespc != nullptr);
  namespc->Release();
}

#if defined(DEBUG)
static bool IsNamespace(Dart_Handle namespc_obj) {
  Dart_Handle namespc_type =
      DartUtils::GetDartType("dart:io", "_NamespaceImpl");
  ASSERT(!Dart_IsError(namespc_type));
  bool isinstance = false;
  Dart_Handle result =
      Dart_ObjectIsType(namespc_obj, namespc_type, &isinstance);
  ASSERT(!Dart_IsError(result));
  return isinstance;
}
#endif

void FUNCTION_NAME(Namespace_Create)(Dart_NativeArguments args) {
  Dart_Handle namespc_obj = Dart_GetNativeArgument(args, 0);
  if (Dart_IsError(namespc_obj)) {
    Dart_PropagateError(namespc_obj);
  }
  DEBUG_ASSERT(IsNamespace(namespc_obj));

  // Allocate a native wrapper for the platform namespc bits.
  Namespace* namespc = nullptr;
  Dart_Handle result;
  Dart_Handle native_namespc = Dart_GetNativeArgument(args, 1);
  if (Dart_IsInteger(native_namespc)) {
    int64_t namespc_val;
    result = Dart_IntegerToInt64(native_namespc, &namespc_val);
    if (Dart_IsError(result)) {
      Dart_PropagateError(result);
    }
    namespc = Namespace::Create(namespc_val);
  } else if (Dart_IsString(native_namespc)) {
    const char* namespc_path;
    result = Dart_StringToCString(native_namespc, &namespc_path);
    if (Dart_IsError(result)) {
      Dart_PropagateError(result);
    }
    namespc = Namespace::Create(namespc_path);
  } else {
    // Propagate a type error.
    Dart_ThrowException(
        DartUtils::NewDartArgumentError("Argument must be an int or a String"));
  }

  // We were unable to create a native Namespace wrapper object due to some
  // OS-level error.
  if (namespc == nullptr) {
    Dart_SetReturnValue(args, DartUtils::NewDartOSError());
    return;
  }

  // Set the Dart objects native field to the native wrapper.
  result = Dart_SetNativeInstanceField(namespc_obj, kNamespaceNativeFieldIndex,
                                       reinterpret_cast<intptr_t>(namespc));
  if (Dart_IsError(result)) {
    namespc->Release();
    Dart_PropagateError(result);
  }

  // Set up a finalizer for the Dart object so that we can do any necessary
  // platform-specific cleanup for the namespc.
  Dart_NewFinalizableHandle(namespc_obj, reinterpret_cast<void*>(namespc),
                            sizeof(*namespc), ReleaseNamespace);
  Dart_SetReturnValue(args, namespc_obj);
}

void FUNCTION_NAME(Namespace_GetDefault)(Dart_NativeArguments args) {
  Dart_SetIntegerReturnValue(args, Namespace::Default());
}

void FUNCTION_NAME(Namespace_GetPointer)(Dart_NativeArguments args) {
  Namespace* namespc = Namespace::GetNamespace(args, 0);
  ASSERT(namespc != nullptr);
  namespc->Retain();
  Dart_SetIntegerReturnValue(args, reinterpret_cast<intptr_t>(namespc));
}

Namespace* Namespace::GetNamespace(Dart_NativeArguments args, intptr_t index) {
  Namespace* namespc;
  Dart_Handle status =
      Namespace::GetNativeNamespaceArgument(args, index, &namespc);
  if (Dart_IsError(status)) {
    Dart_PropagateError(status);
  }
  return namespc;
}

bool Namespace::IsDefault(Namespace* namespc) {
  return (namespc == nullptr) || (namespc->namespc() == nullptr);
}

Dart_Handle Namespace::GetNativeNamespaceArgument(Dart_NativeArguments args,
                                                  intptr_t index,
                                                  Namespace** namespc) {
  Dart_Handle namespc_obj = Dart_GetNativeArgument(args, index);
  if (Dart_IsError(namespc_obj)) {
    return namespc_obj;
  }
  DEBUG_ASSERT(IsNamespace(namespc_obj));

  Dart_Handle result =
      Dart_GetNativeInstanceField(namespc_obj, kNamespaceNativeFieldIndex,
                                  reinterpret_cast<intptr_t*>(namespc));
  if (Dart_IsError(result)) {
    return result;
  }
  if (*namespc == nullptr) {
    return Dart_NewUnhandledExceptionError(
        DartUtils::NewInternalError("No native peer"));
  }
  return Dart_Null();
}

}  // namespace bin
}  // namespace dart

#endif  // !defined(DART_IO_DISABLED)
