/**
 * Copyright 2022-2025 Theai, Inc. dba Inworld AI
 */

#include "Audio/InworldAudioCapture_Microphone.h"

void FInworldAudioCapture_Microphone::StartCapture()
{
	if (!AudioCapture.IsStreamOpen())
	{
#if ENGINE_MAJOR_VERSION >= 5 && ENGINE_MINOR_VERSION >= 3
		Audio::FOnAudioCaptureFunction OnCapture =
			[this](const void* AudioData, int32 NumFrames, int32 NumChannels, int32 SampleRate, double StreamTime, bool bOverFlow)
		{
			OnAudioCapture((const float*) AudioData, NumFrames, NumChannels, SampleRate);
		};

		AudioCapture.OpenAudioCaptureStream(AudioCaptureDeviceParams, MoveTemp(OnCapture), 1024);
#else
		Audio::FOnCaptureFunction OnCapture =
			[this](const float* AudioData, int32 NumFrames, int32 NumChannels, int32 SampleRate, double StreamTime, bool bOverFlow)
		{
			OnAudioCapture(AudioData, NumFrames, NumChannels, SampleRate);
		};

		AudioCapture.OpenCaptureStream(AudioCaptureDeviceParams, MoveTemp(OnCapture), 1024);
#endif
	}

	if (AudioCapture.IsStreamOpen())
	{
		AudioCapture.StartStream();
	}
}

void FInworldAudioCapture_Microphone::StopCapture()
{
	if (AudioCapture.IsStreamOpen())
	{
		AudioCapture.StopStream();
		AudioCapture.CloseStream();
	}
}

void FInworldAudioCapture_Microphone::SetCaptureDeviceById(const FString& DeviceId)
{
	Audio::FAudioCaptureDeviceParams Params;

	if (DeviceId.IsEmpty())
	{
		Params = Audio::FAudioCaptureDeviceParams();
	}
	else
	{
		TArray<Audio::FCaptureDeviceInfo> CaptureDeviceInfos;
		AudioCapture.GetCaptureDevicesAvailable(CaptureDeviceInfos);

		int32 DeviceIndex = 0;
		int32 CaptureDeviceIndex = 0;
		Audio::FCaptureDeviceInfo OutInfo;
		bool bFoundMatchingInputDevice = false;
		while (CaptureDeviceIndex < CaptureDeviceInfos.Num())
		{
			AudioCapture.GetCaptureDeviceInfo(OutInfo, DeviceIndex);
			if (OutInfo.DeviceId == DeviceId)
			{
				Params.DeviceIndex = CaptureDeviceIndex;
				Params.bUseHardwareAEC = OutInfo.bSupportsHardwareAEC;
				bFoundMatchingInputDevice = true;
				break;
			}
			if (CaptureDeviceInfos.ContainsByPredicate(
					[OutInfo](const Audio::FCaptureDeviceInfo& CaptureDeviceInfo) -> bool
					{
						return CaptureDeviceInfo.DeviceId == OutInfo.DeviceId;
					}))
			{
				CaptureDeviceIndex++;
			}
			DeviceIndex++;
		}
		if (!bFoundMatchingInputDevice)
		{
			return;
		}
	}

	if (AudioCaptureDeviceParams.DeviceIndex == Params.DeviceIndex)
	{
		return;
	}

	AudioCaptureDeviceParams = Params;
}

void FInworldAudioCapture_Microphone::OnAudioCapture(const float* AudioData, int32 NumFrames, int32 NumChannels, int32 SampleRate)
{
	AudioDataCallback(FInworldAudioCaptureData{TArray<float>(AudioData, NumFrames * NumChannels), NumChannels, SampleRate});
}
