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

#ifndef RUNTIME_ENGINE_INCLUDE_DART_ENGINE_H_
#define RUNTIME_ENGINE_INCLUDE_DART_ENGINE_H_
#include "include/dart_api.h"

/**
 * Initializes the Dart engine.
 *
 * \param[out] error Set to NULL if initialization was successful.
 *    Otherwise is set to a description of error which occurred during
 *    initialization. The caller is responsible for freeing the error string.
 *
 * \return true if initialization is successful and false otherwise.
 */
DART_EXPORT bool DartEngine_Init(char** error);

/**
 * Shuts down the Dart engine.
 *
 * Stops all isolates and frees all resources.
 */
DART_EXPORT void DartEngine_Shutdown();

/**
 * Kind of a snapshot represented by DartEngine_SnapshotData.
 */
typedef enum {
  DartEngine_SnapshotKind_Kernel,
  DartEngine_SnapshotKind_AOT,
} DartEngine_SnapshotKind;

/**
 * Dart snapshot data, sufficient to create an isolate.
 */
typedef struct DartEngine_SnapshotData {
  /**
   * Uniquely identifies a snapshot.
   *
   * It is passed to \ref Dart_CreateIsolateGroup or
   * \ref Dart_CreateIsolateGroupFromKernel.
   */
  const char* script_uri;
  /**
   * Whether this snapshot data reprents Kernel or AOT snapshot.
   */
  DartEngine_SnapshotKind kind;
  union {
    struct {
      const uint8_t* kernel_buffer;
      intptr_t kernel_buffer_size;
    };
    struct {
      const uint8_t* snapshot_data;
      const uint8_t* snapshot_text;
    };
  };
} DartEngine_SnapshotData;

/**
 * Creates a new isolate for a given snapshot.
 *
 * \param snapshot_data Snapshot to start an isolate from.
 * \param[out] error Set to NULL if an isolate is created successfully.
 *    Otherwise is set to a description of error which occurred during
 *    isolate creation. The caller is responsible for freeing the error string.
 *
 * \return The new isolate on success, or NULL if isolate creation failed.
 */
DART_EXPORT Dart_Isolate
DartEngine_CreateIsolate(DartEngine_SnapshotData snapshot_data, char** error);

/**
 * Blocks until the isolate is available for entering and enters an isolate.
 *
 * This is an alternative to \ref Dart_EnterIsolate, which can be used when
 * multiple threads may try to enter the same isolate at the same time.
 *
 * Use \ref DartEngine_ReleaseIsolate instead of `Dart_ExitIsolate` if you
 * use this function.
 */
DART_EXPORT void DartEngine_AcquireIsolate(Dart_Isolate isolate);

/**
 * Exits an isolate and makes it available for entering by other threads.
 *
 * This is an alternative to \ref Dart_ExitIsolate, which should be used
 * if you use \ref DartEngine_AcquireIsolate.
 */
DART_EXPORT void DartEngine_ReleaseIsolate();

/**
 * A callback invoked when there's an error handling isolate message.
 *
 * \param error Dart error happened during handling a message.
 * \param destination_isolate The isolate where the error occurred.
 */
typedef void (*DartEngine_HandleMessageErrorCallback)(
    Dart_Handle error,
    Dart_Isolate destination_isolate);

/**
 * Sets the callback for errors during handling isolate messages.
 */
DART_EXPORT void DartEngine_SetHandleMessageErrorCallback(
    DartEngine_HandleMessageErrorCallback handle_message_error_callback);

/**
 * Drains the microtasks queue. Requires to be an active isolate.
 *
 * Normally the microtasks queue is drained after handling each
 * isolate message, but when the engine calls into Dart, it might be
 * required to manually drain the microtasks queue.
 *
 * If a microtask throws, the error is returned to the caller,
 * and the queue may still contain more entries.
 * The caller should continue to drain the queue after handling the error.
 *
 * \return Dart_Handle invocation result.
 */
DART_EXPORT Dart_Handle DartEngine_DrainMicrotasksQueue();

/**
 * Handles a single message for an isolate.
 */
DART_EXPORT void DartEngine_HandleMessage(Dart_Isolate isolate);

/**
 * Message scheduling callback.
 *
 * Dart Engine calls this callback whenever there's a new message for an
 * isolate, and the callback should schedule an execution of
 * DartEngine_HandleMessage with given isolate.
 *
 * \param isolate Destination isolate for a mesage.
 * \param context Context from \ref DartEngine_MessageScheduler.
 */
typedef void (*DartEngine_ScheduleMessageCallback)(Dart_Isolate isolate,
                                                   void* context);

/**
 * Scheduler for isolate messages.
 *
 * Whenever Dart isolate has a new message, the Dart engine will
 * call schedule_callback and pass context into it.
 */
typedef struct DartEngine_MessageScheduler {
  DartEngine_ScheduleMessageCallback schedule_callback;
  void* context;
} DartEngine_MessageScheduler;

/**
 * Sets default message scheduler for all isolates.
 */
DART_EXPORT void DartEngine_SetDefaultMessageScheduler(
    DartEngine_MessageScheduler scheduler);

/**
 * Sets message scheduler for isolate.
 */
DART_EXPORT void DartEngine_SetMessageScheduler(
    DartEngine_MessageScheduler scheduler,
    Dart_Isolate isolate);

/**
 * Loads \ref DartEngine_SnapshotData from file
 *
 * User should not free uri and buffer.
 *
 * \param[out] error Set to NULL if snapshot could be loaded successfully.
 *    Otherwise is set to a description of error which occurred during
 *    snapshot loading. The caller is responsible for freeing the error string.
 *
 * \return Snapshot data with kind set to \ref DartEngine_SnapshotKind_Kernel.
 */
DART_EXPORT DartEngine_SnapshotData DartEngine_KernelFromFile(const char* path,
                                                              char** error);
/**
 * Loads \ref DartEngine_SnapshotData from file
 *
 * User should not free uri and buffers.
 *
 * \param[out] error Set to NULL if snapshot could be loaded successfully.
 *    Otherwise is set to a description of error which occurred during
 *    snapshot loading. The caller is responsible for freeing the error string.
 *
 * \return Snapshot data with kind set to \ref DartEngine_SnapshotKind_AOT.
 */
DART_EXPORT DartEngine_SnapshotData
DartEngine_AotSnapshotFromFile(const char* path, char** error);

#endif  // RUNTIME_ENGINE_INCLUDE_DART_ENGINE_H_
