// Copyright (c) FIRST and other WPILib contributors.
// Open Source Software; you can modify and/or share it under the terms of
// the WPILib BSD license file in the root directory of this project.

#pragma once

#include <jni.h>
#include <stdint.h>

#include <span>
#include <string_view>

struct HAL_MatchInfo;
struct HAL_OpModeOption;
struct HAL_Value;

namespace wpi::hal {

void ReportError(JNIEnv* env, int32_t status, bool doThrow = true);

void ThrowError(JNIEnv* env, int32_t status, int32_t minRange, int32_t maxRange,
                int32_t requestedValue);

inline bool CheckStatus(JNIEnv* env, int32_t status, bool doThrow = true) {
  if (status != 0) {
    ReportError(env, status, doThrow);
  }
  return status == 0;
}

inline bool CheckStatusRange(JNIEnv* env, int32_t status, int32_t minRange,
                             int32_t maxRange, int32_t requestedValue) {
  if (status != 0) {
    ThrowError(env, status, minRange, maxRange, requestedValue);
  }
  return status == 0;
}

inline bool CheckStatusForceThrow(JNIEnv* env, int32_t status) {
  if (status != 0) {
    ThrowError(env, status, 0, 0, 0);
  }
  return status == 0;
}

void ThrowNullPointerException(JNIEnv* env, std::string_view msg);
void ThrowCANStreamOverflowException(JNIEnv* env, jobjectArray messages,
                                     jint length);
void ThrowIllegalArgumentException(JNIEnv* env, std::string_view msg);
void ThrowIndexOutOfBoundsException(JNIEnv* env, std::string_view msg);
void ThrowBoundaryException(JNIEnv* env, double value, double lower,
                            double upper);

jobject CreateOpModeOption(JNIEnv* env, const HAL_OpModeOption& option);
jobjectArray CreateOpModeOptionArray(JNIEnv* env,
                                     std::span<const HAL_OpModeOption> options);
HAL_OpModeOption CreateOpModeOptionFromJava(JNIEnv* env, jobject option);

jobject CreateREVPHVersion(JNIEnv* env, uint32_t firmwareMajor,
                           uint32_t firmwareMinor, uint32_t firmwareFix,
                           uint32_t hardwareMinor, uint32_t hardwareMajor,
                           uint32_t uniqueId);

void SetCanStatusObject(JNIEnv* env, jobject canStatus,
                        float percentBusUtilization, uint32_t busOffCount,
                        uint32_t txFullCount, uint32_t receiveErrorCount,
                        uint32_t transmitErrorCount);

void SetMatchInfoObject(JNIEnv* env, jobject matchStatus,
                        const HAL_MatchInfo& matchInfo);

jbyteArray SetCANReceiveMessageObject(JNIEnv* env, jobject canData,
                                      int32_t length, int32_t flags,
                                      uint64_t timestamp);

jbyteArray SetCANStreamObject(JNIEnv* env, jobject canStreamData,
                              int32_t length, int32_t flags, uint32_t messageId,
                              uint64_t timestamp);

jobject CreateHALValue(JNIEnv* env, const HAL_Value& value);

jobject CreatePowerDistributionVersion(JNIEnv* env, uint32_t firmwareMajor,
                                       uint32_t firmwareMinor,
                                       uint32_t firmwareFix,
                                       uint32_t hardwareMinor,
                                       uint32_t hardwareMajor,
                                       uint32_t uniqueId);

jobject CreateCANStreamMessage(JNIEnv* env);

JavaVM* GetJVM();

}  // namespace wpi::hal
