#include <com_mysaifu_jvm_java_security_provider_WCESecureRandomSpi.h>
#include <wincrypt.h>
#include "wcesecurity.h"

/*
 * Class:     com_mysaifu_jvm_java_security_provider_WCESecureRandomSpi
 * Method:    openNative
 * Signature: ()I
 */
JNIEXPORT jint JNICALL Java_com_mysaifu_jvm_java_security_provider_WCESecureRandomSpi_openNative
  (JNIEnv *env, jobject)
{
  HCRYPTPROV   hCryptProv = NULL;

  if (! CryptAcquireContext(&hCryptProv, NULL, NULL, PROV_RSA_FULL, 0))
  {
    throwException(env, NULLPOINTER_EXCEPTION_NAME, GetLastError());
  }
  return hCryptProv;
}

/*
 * Class:     com_mysaifu_jvm_java_security_provider_WCESecureRandomSpi
 * Method:    closeNative
 * Signature: (I)V
 */
JNIEXPORT void JNICALL Java_com_mysaifu_jvm_java_security_provider_WCESecureRandomSpi_closeNative
  (JNIEnv *env, jobject, jint nativePointer)
{
  HCRYPTPROV hCryptProv = static_cast<HCRYPTPROV>(nativePointer);
  if (! hCryptProv)
  {
    return;
  }
  CryptReleaseContext(hCryptProv, 0);
}

/*
 * Class:     com_mysaifu_jvm_java_security_provider_WCESecureRandomSpi
 * Method:    engineNativeSetSeed
 * Signature: (I[B)V
 */
JNIEXPORT void JNICALL Java_com_mysaifu_jvm_java_security_provider_WCESecureRandomSpi_engineNativeSetSeed
  (JNIEnv *env, jobject, jint nativePointer, jbyteArray seed)
{
  HCRYPTPROV hCryptProv = static_cast<HCRYPTPROV>(nativePointer);
  if (! hCryptProv)
  {
    return;
  }
  const jsize len = env->GetArrayLength(seed);
  jbyte* data = static_cast<jbyte*>(env->GetPrimitiveArrayCritical(seed, NULL));
  BYTE* buff = (BYTE*) malloc(len);
  memcpy(buff, data, len);
  BOOL b = CryptGenRandom(hCryptProv, len, buff);
  free(buff);
  env->ReleasePrimitiveArrayCritical(seed, data, 0);

  if (! b)
  {
    throwException(env, ILLEGALSTATE_EXCEPTION_NAME, GetLastError());
  }
}

/*
 * Class:     com_mysaifu_jvm_java_security_provider_WCESecureRandomSpi
 * Method:    engineNativeNextBytes
 * Signature: (I[B)V
 */
JNIEXPORT void JNICALL Java_com_mysaifu_jvm_java_security_provider_WCESecureRandomSpi_engineNativeNextBytes
  (JNIEnv *env, jobject, jint nativePointer, jbyteArray bytes)
{
  HCRYPTPROV hCryptProv = static_cast<HCRYPTPROV>(nativePointer);
  if (! hCryptProv)
  {
    return;
  }
  const jsize len = env->GetArrayLength(bytes);
  jbyte* data = static_cast<jbyte*>(env->GetPrimitiveArrayCritical(bytes, NULL));
  BOOL b = CryptGenRandom(hCryptProv, len, reinterpret_cast<BYTE*>(data));
  env->ReleasePrimitiveArrayCritical(bytes, data, 0);

  if (! b)
  {
    throwException(env, ILLEGALSTATE_EXCEPTION_NAME, GetLastError());
  }
}

/*
 * Class:     com_mysaifu_jvm_java_security_provider_WCESecureRandomSpi
 * Method:    engineNativeGenerateSeed
 * Signature: (II)[B
 */
JNIEXPORT jbyteArray JNICALL Java_com_mysaifu_jvm_java_security_provider_WCESecureRandomSpi_engineNativeGenerateSeed
  (JNIEnv *env, jobject, jint nativePointer, jint numBytes)
{
  HCRYPTPROV hCryptProv = static_cast<HCRYPTPROV>(nativePointer);
  if (! hCryptProv)
  {
    return NULL;
  }

  jbyteArray result = env->NewByteArray(numBytes);
  if (result)
  {
    const jsize len = numBytes;
    jbyte* data = static_cast<jbyte*>(env->GetPrimitiveArrayCritical(result, NULL));
    BOOL b = CryptGenRandom(hCryptProv, len, reinterpret_cast<BYTE*>(data));
    env->ReleasePrimitiveArrayCritical(result, data, 0);

    if (! b)
    {
      throwException(env, ILLEGALSTATE_EXCEPTION_NAME, GetLastError());
    }
  }
  return result;
}