// Copyright (c) 2012, 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.

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#include "include/bin/dart_io_api.h"
#include "include/dart_api.h"
#include "include/dart_tools_api.h"

#include "platform/assert.h"

#include "bin/builtin.h"
#include "bin/dartutils.h"
#include "bin/file.h"
#include "bin/io_natives.h"
#include "bin/platform.h"

namespace dart {
namespace bin {

// Lists the native functions implementing basic functionality in
// standalone dart, such as printing, file I/O, and platform information.
// Advanced I/O classes like sockets and process management are implemented
// using functions listed in io_natives.cc.
#define BUILTIN_NATIVE_LIST(V) V(Builtin_PrintString, 1)

BUILTIN_NATIVE_LIST(DECLARE_FUNCTION);

static struct NativeEntries {
  const char* name_;
  Dart_NativeFunction function_;
  int argument_count_;
} BuiltinEntries[] = {BUILTIN_NATIVE_LIST(REGISTER_FUNCTION)};

void Builtin_DummyNative(Dart_NativeArguments args) {
  UNREACHABLE();
}

/**
 * Looks up native functions in both libdart_builtin and libdart_io.
 */
Dart_NativeFunction Builtin::NativeLookup(Dart_Handle name,
                                          int argument_count,
                                          bool* auto_setup_scope) {
  const char* function_name = nullptr;
  Dart_Handle err = Dart_StringToCString(name, &function_name);
  if (Dart_IsError(err)) {
    Dart_PropagateError(err);
  }
  ASSERT(function_name != nullptr);
  ASSERT(auto_setup_scope != nullptr);
  *auto_setup_scope = true;
  int num_entries = sizeof(BuiltinEntries) / sizeof(struct NativeEntries);
  for (int i = 0; i < num_entries; i++) {
    struct NativeEntries* entry = &(BuiltinEntries[i]);
    if ((strcmp(function_name, entry->name_) == 0) &&
        (entry->argument_count_ == argument_count)) {
      return reinterpret_cast<Dart_NativeFunction>(entry->function_);
    }
  }
  Dart_NativeFunction result =
      IONativeLookup(name, argument_count, auto_setup_scope);
  if (result == nullptr) {
    result = Builtin_DummyNative;
  }
  return result;
}

void* Builtin::FfiNativeLookup(const char* name, uintptr_t argument_count) {
  return IOFfiNativeLookup(name, argument_count);
}

const uint8_t* Builtin::NativeSymbol(Dart_NativeFunction nf) {
  int num_entries = sizeof(BuiltinEntries) / sizeof(struct NativeEntries);
  for (int i = 0; i < num_entries; i++) {
    struct NativeEntries* entry = &(BuiltinEntries[i]);
    if (reinterpret_cast<Dart_NativeFunction>(entry->function_) == nf) {
      return reinterpret_cast<const uint8_t*>(entry->name_);
    }
  }
  return IONativeSymbol(nf);
}

// Implementation of native functions which are used for some
// test/debug functionality in standalone dart mode.
void FUNCTION_NAME(Builtin_PrintString)(Dart_NativeArguments args) {
  intptr_t length = 0;
  Dart_Handle str = Dart_GetNativeArgument(args, 0);
  Dart_Handle result = Dart_StringUTF8Length(str, &length);
  if (Dart_IsError(result)) {
    Dart_PropagateError(result);
  }
  intptr_t new_length = length + 1;
  uint8_t* chars = Dart_ScopeAllocate(new_length);
  ASSERT(chars != nullptr);
  result = Dart_CopyUTF8EncodingOfString(str, chars, length);
  if (Dart_IsError(result)) {
    Dart_PropagateError(result);
  }
  chars[length] = '\n';

  // Uses fwrite to support printing NUL bytes.
  const uint8_t* cursor = chars;
  intptr_t remaining = new_length;
  while (remaining > 0) {
    intptr_t written = fwrite(cursor, 1, remaining, stdout);
    if (written == 0) break;  // EOF or error: ignore
    cursor += written;
    remaining -= written;
  }
  fflush(stdout);
  if (ShouldCaptureStdout()) {
    // For now we report print output on the Stdout stream.
    const char* res =
        Dart_ServiceSendDataEvent("Stdout", "WriteEvent", chars, new_length);
    ASSERT(res == nullptr);
  }
}

}  // namespace bin
}  // namespace dart
