/// <reference path="./base.d.ts" />
/// <reference path="./clock_event.d.ts" />
/// <reference path="./clock_change.d.ts" />
/// <reference path="./message_validation.d.ts" />
/// <reference path="./parameter_event_handler.d.ts" />
/// <reference path="./message_info.d.ts" />

import { ChildProcess } from 'child_process';

declare module 'rclnodejs' {
  type Class = new (...args: any[]) => any;

  /**
   * Create a node.
   *
   * @remarks
   * See {@link Node}
   *
   * @param nodeName - The name used to register in ROS.
   * @param namespace - The namespace used in ROS, default is an empty string.
   * @param context - The context, default is Context.defaultContext().
   * @param options - The node options, default is NodeOptions.defaultOptions.
   * @param args - The arguments to be passed to the node, default is an empty array.
   * @param useGlobalArguments - If true, the node will use global arguments, default is true.
   *                            If false, the node will not use global arguments.
   * @returns The new Node instance.
   * @deprecated since 0.18.0, Use new Node constructor.
   */
  function createNode(
    nodeName: string,
    namespace?: string,
    context?: Context,
    options?: NodeOptions,
    args?: string[],
    useGlobalArguments?: boolean
  ): Node;

  /**
   * Create a managed Node that implements a well-defined life-cycle state
   * model using the {@link https://github.com/ros2/rcl/tree/master/rcl_lifecycle|ros2 client library (rcl) lifecyle api}.
   * @param nodeName - The name used to register in ROS.
   * @param namespace - The namespace used in ROS, default is an empty string.
   * @param context - The context, default is Context.defaultContext().
   * @param options - The options to configure the new node behavior.
   * @params enableCommunicationInterface: boolean - Enable lifecycle service interfaces, e.g., GetState.
   * @returns The instance of LifecycleNode.
   * @deprecated since 0.18.0, Use new LifecycleNode constructor.
   */
  function createLifecycleNode(
    nodeName: string,
    namespace?: string,
    context?: Context,
    options?: NodeOptions,
    enableCommunicationInterface?: boolean
  ): lifecycle.LifecycleNode;

  /**
   * Init the module.
   *
   * @param context - The context, default is Context.defaultContext().
   * @param argv - The commandline arguments, the default value is process.argv.
   * @returns A Promise.
   */
  function init(context?: Context, argv?: string[]): Promise<void>;

  /**
   * Remove ROS-specific arguments from the given argument list.
   * @param argv - The argument list to process.
   * @returns The argument list with ROS arguments removed.
   */
  function removeROSArgs(argv: string[]): string[];

  /**
   * Start detection and processing of units of work.
   *
   * @param node - The node to be spun.
   * @param timeout - ms to wait, block forever if negative, return immediately when 0, default is 10.
   * @deprecated since 0.18.0, Use Node.spin(timeout)
   */
  function spin(node: Node, timeout?: number): void;

  /**
   * Execute one item of work or wait until a timeout expires.
   *
   * @param node - The node to be spun.
   * @param timeout - ms to wait, block forever if negative, return immediately when 0, default is 10.
   * @deprecated since 0.18.0, Use Node.spinOnce(timeout)*/
  function spinOnce(node: Node, timeout?: number): void;

  /**
   * Options for waitForMessage.
   */
  interface WaitForMessageOptions {
    /** Timeout in milliseconds. If omitted, waits indefinitely. */
    timeout?: number;
    /** QoS profile for the temporary subscription. */
    qos?: QoS;
  }

  /**
   * Wait for a single message on a topic.
   *
   * Creates a temporary subscription, waits for the first message to arrive,
   * and returns it. The node must be spinning before calling this function.
   *
   * This is the rclnodejs equivalent of rclpy's `wait_for_message`.
   *
   * @param typeClass - The ROS message type class.
   * @param node - The node to create the temporary subscription on.
   * @param topic - The topic name to listen on.
   * @param options - Options including timeout and QoS.
   * @returns Resolves with the received message.
   * @throws Error if timeout expires before a message arrives.
   */
  function waitForMessage<T extends TypeClass<MessageTypeClassName>>(
    typeClass: T,
    node: Node,
    topic: string,
    options?: WaitForMessageOptions
  ): Promise<MessageType<T>>;

  /**
   * Stop all activity, destroy all nodes and node components.
   *
   * @param context - The context, default is Context.defaultContext()
   */
  function shutdown(context?: Context): void;

  /**
   * Shutdown all RCL environments via their contexts.
   * @throws Error if there is a problem shutting down the context or while destroying or shutting down a node within it.
   */
  function shutdownAll(): void;

  /**
   * Test if the module is shutdown.
   *
   * @returns True if the module is shut down, otherwise return false.
   */
  function isShutdown(): boolean;

  /**
   * Get the interface package, which is used by publisher/subscription or client/service.
   *
   * @param  name - The name of interface to be required.
   * @returns The object of the required package/interface.
   */
  function require<T extends TypeClassName>(name: T): InterfaceType<T>;
  function require(name: string): object;

  /**
   * Generate JavaScript structs files from the IDL of
   * messages(.msg) and services(.srv).
   * Search packages which locate under path $AMENT_PREFIX_PATH
   * and output JS files into the 'generated' folder.
   * Any existing files under the generated folder will
   * be overwritten.
   *
   * @returns A Promise.
   */
  function regenerateAll(): Promise<void>;

  /**
   * Judge if the topic or service is hidden,
   *
   * @remarks
   * See {@link http://design.ros2.org/articles/topic_and_service_names.html#hidden-topic-or-service-names}
   *
   * @param name - Name of topic or service.
   * @returns True if a given topic or service name is hidden, otherwise False.
   */
  function isTopicOrServiceHidden(name: string): boolean;

  /**
   * Expand a given topic name using given node name and namespace.
   *
   * @param  topicName - Topic name to be expanded.
   * @param  nodeName - Name of the node that this topic is associated with.
   * @param  nodeNamespace - Namespace that the topic is within.
   * @returns Expanded topic name which is fully qualified.
   */
  function expandTopicName(
    topicName: string,
    nodeName: string,
    nodeNamespace?: string
  ): string;

  /**
   * Create a plain JavaScript message object.
   *
   * @param type - type identifier, acceptable formats could be 'std_msgs/std/String'
   *                                or {package: 'std_msgs', type: 'msg', name: 'String'}
   * @returns A Message object or undefined if type is not recognized.
   */
  function createMessageObject<T extends TypeClass<MessageTypeClassName>>(
    type: T
  ): MessageType<T>;

  /**
   * Removes the default signal handler installed by rclnodejs. After calling this, rclnodejs
   * will no longer clean itself up when a SIGINT is received, it is the application's
   * responsibility to properly shut down all nodes and contexts.
   */
  function removeSignalHandlers(): void;

  /**
   * Get a list of action names and types for action clients associated with a node.
   * @param node - The node used for discovery.
   * @param nodeName - The name of a remote node to get action clients for.
   * @param namespace - Namespace of the remote node.
   * @returns An array of the names and types.
   */
  function getActionClientNamesAndTypesByNode(
    node: Node,
    nodeName: string,
    namespace: string
  ): NamesAndTypesQueryResult;

  /**
   * Get a list of action names and types for action servers associated with a node.
   * @param node - The node used for discovery.
   * @param nodeName - The name of a remote node to get action servers for.
   * @param namespace - Namespace of the remote node.
   * @returns An array of the names and types.
   */
  function getActionServerNamesAndTypesByNode(
    node: Node,
    nodeName: string,
    namespace: string
  ): NamesAndTypesQueryResult;

  /**
   * Get a list of action names and types.
   * @param node - The node used for discovery.
   * @returns An array of the names and types.
   */
  function getActionNamesAndTypes(node: Node): NamesAndTypesQueryResult;

  /**
   * Serialize a message to a Buffer.
   *
   * @param message - The message to be serialized.
   * @param typeClass - The type class of the message.
   * @returns A Buffer containing the serialized message.
   */
  function serializeMessage(message: object, typeClass: Class): Buffer;

  /**
   * Deserialize a message from a Buffer.
   *
   * @param buffer - The Buffer containing the serialized message.
   * @param typeClass - The type class of the message.
   * @returns An Object representing the deserialized message.
   */
  function deserializeMessage(buffer: Buffer, typeClass: Class): object;

  /**
   * Run a ROS2 package executable using 'ros2 run' command.
   * @param {string} packageName - The name of the ROS2 package.
   * @param {string} executableName - The name of the executable to run.
   * @param {string[]} [args=[]] - Additional arguments to pass to the executable.
   * @return {Promise<{process: ChildProcess}>} A Promise that resolves with the process.
   */
  function ros2Run(
    packageName: string,
    executableName: string,
    args: string[]
  ): Promise<{ process: ChildProcess }>;

  /**
   * Run a ROS2 launch file using 'ros2 launch' command.
   * @param {string} packageName - The name of the ROS2 package.
   * @param {string} launchFile - The name of the launch file to run.
   * @param {string[]} [args=[]] - Additional arguments to pass to the launch file.
   * @return {Promise<{process: ChildProcess}>} A Promise that resolves with the process.
   */
  function ros2Launch(
    packageName: string,
    launchFile: string,
    args: string[]
  ): Promise<{ process: ChildProcess }>;

  /**
   * Convert a message object to be JSON-safe by converting TypedArrays to regular arrays
   * and handling BigInt, Infinity, NaN, etc. for JSON serialization.
   * @param obj - The message object to convert
   * @returns A JSON-safe version of the object
   */
  function toJSONSafe(obj: any): any;

  /**
   * Convert a message object to a JSON string with proper handling of TypedArrays,
   * BigInt, and other non-JSON-serializable values.
   * @param obj - The message object to convert
   * @param space - Space parameter for JSON.stringify formatting
   * @returns The JSON string representation
   */
  function toJSONString(obj: any, space?: number): string;
}
