/*
 * Class:     org_gamepad4j_desktop_GamepadJniWrapper
 * Method:    natGetIdOfPad
 * Signature: ()I
 */
JNIEXPORT jint JNICALL Java_org_gamepad4j_desktop_GamepadJniWrapper_natGetIdOfPad(
		JNIEnv *env, jobject obj, jint index) {
	struct Gamepad_device * device = Gamepad_deviceAtIndex(index);
	if(device == NULL) {
		return -1;
	}
	return device->deviceID;
}
/*
 * Class:     org_gamepad4j_desktop_GamepadJniWrapper
 * Method:    natGetNumberOfButtons
 * Signature: (I)I
 */
JNIEXPORT jint JNICALL Java_org_gamepad4j_desktop_GamepadJniWrapper_natGetNumberOfButtons(
		JNIEnv *env, jobject obj, jint index) {
	struct Gamepad_device * device = Gamepad_deviceAtIndex(index);
	if(device == NULL) {
		return -1;
	}
	return device->numButtons;
}
/*
 * Class:     org_gamepad4j_desktop_GamepadJniWrapper
 * Method:    natGetControllerButtonState
 * Signature: (B[])V
 */
JNIEXPORT jint JNICALL Java_org_gamepad4j_desktop_GamepadJniWrapper_natGetControllerButtonState(
		JNIEnv *env, jobject obj, jint gamepadIndex, jint buttonIndex)
{
	struct Gamepad_device * device = Gamepad_deviceAtIndex(gamepadIndex);
	if(device == NULL) {
		return -1;
	}
	return device->buttonStates[buttonIndex];
}
/*
 * Class:     org_gamepad4j_desktop_GamepadJniWrapper
 * Method:    natGetControllerId
 * Signature: (I)S
 */
JNIEXPORT jstring JNICALL Java_org_gamepad4j_desktop_GamepadJniWrapper_natGetControllerDescription(
		JNIEnv *env, jobject obj, jint gamepadIndex)
{
	unsigned int index = gamepadIndex;
	struct Gamepad_device * device = Gamepad_deviceAtIndex(index);
	if(device == NULL) {
		return NULL;
	}
    jstring result = (*env)->NewStringUTF(env, device->description); // C style string to Java String
    return result;
}
/*
 * Class:     org_gamepad4j_desktop_GamepadJniWrapper
 * Method:    natGetControllerAxisState
 * Signature: (B[])V
 */
JNIEXPORT jfloat JNICALL Java_org_gamepad4j_desktop_GamepadJniWrapper_natGetControllerAxisState(
		JNIEnv *env, jobject obj, jint gamepadIndex, jint axisIndex)
{
	struct Gamepad_device * device = Gamepad_deviceAtIndex(gamepadIndex);
	if(device == NULL) {
		printf("ERROR: Can't update axis of device!\n");
		return -1;
	}
	// TODO: Bounds check for "axisIndex" variable
	float result = device->axisStates[axisIndex];
	return result;
}
/*
 * Class:     org_gamepad4j_desktop_GamepadJniWrapper
 * Method:    natGetControllerId
 * Signature: (I[])V
 */
JNIEXPORT void JNICALL Java_org_gamepad4j_desktop_GamepadJniWrapper_natGetControllerIDs(
		JNIEnv *env, jobject obj, jint gamepadIndex, jintArray idArray)
{
	struct Gamepad_device * device = Gamepad_deviceAtIndex(gamepadIndex);
	if(device == NULL) {
		return;
	}
	jint *ids = (*env)->GetIntArrayElements(env, idArray, NULL);

	ids[0] = device->deviceID;
	ids[1] = device->vendorID;
	ids[2] = device->productID;

	(*env)->ReleaseIntArrayElements(env, idArray, ids, 0);
}
/*
 * Class:     org_gamepad4j_desktop_GamepadJniWrapper
 * Method:    natGetControllerAxesStates
 * Signature: (F[])V
 */
JNIEXPORT void JNICALL Java_org_gamepad4j_desktop_GamepadJniWrapper_natGetControllerAxesStates(
		JNIEnv *env, jobject obj, jint gamepadIndex, jfloatArray axesArray)
{
	unsigned int axisIndex;

	struct Gamepad_device * device = Gamepad_deviceAtIndex(gamepadIndex);
	if(device == NULL) {
		printf("ERROR: Can't update axes of device!\n");
		return;
	}

	jfloat *states = (*env)->GetFloatArrayElements(env, axesArray, NULL);

	for (axisIndex = 0; axisIndex <= device->numAxes; axisIndex++) {
		states[axisIndex] = device->axisStates[axisIndex];
	}

	(*env)->ReleaseFloatArrayElements(env, axesArray, states, 0);
}
//-- private methods -----
static bool is_gamepad_supported(
	int gamepad_index, 
	CommonDeviceState::eDeviceType device_type_filter,
	CommonDeviceState::eDeviceType &out_device_type)
{
	bool bIsValidDevice = false;

	const Gamepad_device *devInfo= Gamepad_deviceAtIndex(static_cast<unsigned int>(gamepad_index));

	if (devInfo != nullptr)
	{
		// See if the next filtered device is a controller type that we care about
		for (int gamepad_type_index = 0; gamepad_type_index < MAX_CONTROLLER_TYPE_INDEX; ++gamepad_type_index)
		{
			const GamepadAPIDeviceFilter &supported_type = g_supported_gamepad_infos[gamepad_type_index];

			if (supported_type.bGamepadApiSupported &&
				devInfo->productID == supported_type.filter.product_id &&
				devInfo->vendorID == supported_type.filter.vendor_id)
			{				
				CommonDeviceState::eDeviceType device_type =
					static_cast<CommonDeviceState::eDeviceType>(CommonDeviceState::Controller + gamepad_type_index);

                // Don't enumerate PSNavi controllers when using virtual controllers
                // (since a PSNavi controller will be considered a kind of virtual controller)
                if (device_type != CommonDeviceState::PSNavi ||
                    (device_type == CommonDeviceState::PSNavi && ControllerGamepadEnumerator::virtual_controller_count == 0))
                {
				    if (device_type_filter == device_type || device_type_filter == CommonDeviceState::INVALID_DEVICE_TYPE)
				    {
					    out_device_type= device_type;
					    bIsValidDevice = true;
					    break;
				    }
                }
			}
		}
	}

	return bIsValidDevice;
}
/*
 * Class:     org_gamepad4j_desktop_GamepadJniWrapper
 * Method:    natGetControllerButtonStates
 * Signature: (B[])V
 */
JNIEXPORT void JNICALL Java_org_gamepad4j_desktop_GamepadJniWrapper_natGetControllerButtonStates(
		JNIEnv *env, jobject obj, jint gamepadIndex, jbooleanArray buttonArray)
{
	unsigned int buttonIndex;

	struct Gamepad_device * device = Gamepad_deviceAtIndex(gamepadIndex);
	if(device == NULL) {
		return;
	}

	jboolean *states = (*env)->GetBooleanArrayElements(env, buttonArray, NULL);

	for (buttonIndex = 0; buttonIndex <= device->numButtons; buttonIndex++) {
		states[buttonIndex] = JNI_FALSE;
		if (device->buttonStates[buttonIndex]) {
			states[buttonIndex] = JNI_TRUE;
		}
	}

	(*env)->ReleaseBooleanArrayElements(env, buttonArray, states, 0);
}
int ControllerGamepadEnumerator::get_product_id() const
{
	return is_valid() ? Gamepad_deviceAtIndex(static_cast<unsigned int>(m_controllerIndex))->productID : -1;
}