// Fill out your copyright notice in the Description page of Project Settings.


#include "GMEObject.h"
#include "Log/CsdkLogMacros.h"
#include "LogGME.h"
#include "GMESDK/tmg_sdk.h"
#include "Manager/CsdkVoiceSupportSubsystem.h"

#define VERSION "IM_ChatSDK_V3.1"

constexpr int ExpectLength = 500;

bool UGMEObject:: Init(UCsdkVoiceSupportSubsystem* CsdkVoiceSupportSubsystem)
{
	Super::Init(CsdkVoiceSupportSubsystem);
	//设置打印日志路径,需要在 Init 之前调用。
	
	auto InitGMELogPath = FPaths::Combine( FPaths::ProjectSavedDir(), TEXT("Csdk"));
	auto FullGMELogPath = FPaths::ConvertRelativePathToFull(InitGMELogPath);
	CSDK_LOG(LogGME,Log,TEXT("GME Log Path->InitGMELogPath:%s, FullGMELogPath:%s"),*InitGMELogPath,*FullGMELogPath);
	ITMGContextGetInstance()->SetLogPath(TCHAR_TO_UTF8(* FullGMELogPath));

	//GME的OpenId，使用字符串为唯一ID需提交工单
	const int InitRet = ITMGContextGetInstance()->Init(TCHAR_TO_UTF8(*AppID), TCHAR_TO_UTF8(*OpenId));


	if( InitRet!= nsgme::av::AV_OK)
	{
		CSDK_LOG(LogGME,Error,TEXT("GME Raw Init Result:%d"),InitRet);
		return false;
	}

	//设置回调
	ITMGContextGetInstance()->SetTMGDelegate(this);
	//检查麦克风权限????，有啥用？,实时语音里面有这个函数，
	//const int RetCode = ITMGContextGetInstance()->CheckMicPermission();
	
	//CSDK_LOG(LogGME, Log, TEXT("check Permission retcode =%d"), RetCode);

	if(!AppID.IsNumeric())
	{
		CSDK_LOG(LogGME, Error, TEXT("AppID is not Numeric,AppID = %s"), *AppID);
		return false;
	}

	
	constexpr char AuthBuffer[ExpectLength] = {};

	//生成本地鉴权，用于相关功能的加密和鉴权(语音消息)
	const unsigned int BufferLength = QAVSDK_AuthBuffer_GenAuthBuffer( FCString::Atoi(*AppID), nullptr, TCHAR_TO_UTF8(*OpenId), TCHAR_TO_UTF8(*AppKey), (unsigned char *)AuthBuffer, ExpectLength);
	//生成鉴权信息后，将鉴权赋值到 SDK 中。
	const int32 AuthResult = ITMGContextGetInstance()->GetPTT()->ApplyPTTAuthbuffer(AuthBuffer, BufferLength);
	if( InitRet!= nsgme::av::AV_OK)
	{
		CSDK_LOG(LogGME,Error,TEXT("ApplyPTTAuthbuffer Result:%d"),AuthResult);
		return false;
	}
	//????干啥呢
	//ITMGContextGetInstance()->SetAdvanceParams("ForceHighQuanlityState","0");

	TickerHandle = CSDK_Ticker::GetCoreTicker().AddTicker(FTickerDelegate::CreateUObject(this,&UGMEObject::Tick));
	CSDK_LOG(LogGME,Log,TEXT("Init Result:true"));
	return true;
}

int32 UGMEObject::UnInit()
{
	const int32 Ret = ITMGContextGetInstance()->Uninit();
	if(nsgme::av::AV_OK == Ret)
	{
		Super::UnInit();
		CSDK_LOG(LogGME,Log,TEXT("GME UnInit Success"));
		return Ret;
	}
	CSDK_LOG(LogGME,Error,TEXT("GME UnInit Failed With Code:%d"),Ret);
	return Ret;
}

int32 UGMEObject::Pause()
{
	return ITMGContextGetInstance()->Pause();
}

int32 UGMEObject::Resume()
{
	return ITMGContextGetInstance()->Resume();
}

bool UGMEObject::Tick(float DeltaTime)
{
	ITMGContextGetInstance()->Poll();
	return true;
}


int32 UGMEObject::StartRecording(const FRecordFileCompleted& InRecordFileCompleted)
{
	RecordFileCompleted = InRecordFileCompleted;
	const int32 Result = ITMGContextGetInstance()->GetPTT()->StartRecording(TCHAR_TO_UTF8(*GetRecordingFileName()));
	if(nsgme::av::AV_OK != Result)
	{
		CSDK_LOG(LogGME,Error,TEXT("StartRecording, Code=%d"),Result);
	}
	CSDK_LOG(LogGME,Log,TEXT("StartRecording, Code=%d"),Result);
	return Result;
}

int32 UGMEObject::StopRecordingRaw()
{
	const int32 Result = ITMGContextGetInstance()->GetPTT()->StopRecording();
	if(nsgme::av::AV_OK != Result)
	{
		CSDK_LOG(LogGME,Error,TEXT("StopRecording, Code=%d"),Result);
	}
	CSDK_LOG(LogGME,Log,TEXT("StopRecording, Code=%d"),Result);
	return Result;
}


int32 UGMEObject::StopRecording()
{
	return StopRecordingRaw();
}

int32 UGMEObject::CancelRecording()
{
	const int32 Result = ITMGContextGetInstance()->GetPTT()->CancelRecording();
	if(nsgme::av::AV_OK != Result)
	{
		CSDK_LOG(LogGME,Error,TEXT("CancelRecording, Code=%d"),Result);
	}
	CSDK_LOG(LogGME,Log,TEXT("CancelRecording, Code=%d"),Result);
	return Result;
}


int32 UGMEObject::UploadRecordedFile(const FString& FilePath, const FUploadRecordedFileCompleted& InOnUploadRecordedFileCompleted)
{
	GetUploadCallbackMap().Add(FilePath, InOnUploadRecordedFileCompleted);
	const int32 Result = ITMGContextGetInstance()->GetPTT()->UploadRecordedFile(TCHAR_TO_UTF8(*FilePath));
	if(nsgme::av::AV_OK != Result)
	{
		CSDK_LOG(LogGME,Error,TEXT("UploadRecordedFile, Code=%d"),Result);
		return Result;
	}
	CSDK_LOG(LogGME,Log,TEXT("UploadRecordedFile, Code=%d"),Result);
	return Result;
}

int32 UGMEObject::DownloadRecordedFile(const FString& FileId, FDownloadRecordedFileCompleted InOnDownloadRecordedFileCompleted)
{
	const int32 Result = ITMGContextGetInstance()->GetPTT()->DownloadRecordedFile(TCHAR_TO_UTF8(*FileId),TCHAR_TO_UTF8(*GetDownloadFilePath()));
	if(nsgme::av::AV_OK != Result)
	{
		CSDK_LOG(LogGME,Error,TEXT("DownloadRecordedFile, Code=%d"),Result);
		return Result;
	}
	GetDownloadCallbackMap().Add(FileId,InOnDownloadRecordedFileCompleted);
	CSDK_LOG(LogGME,Log,TEXT("DownloadRecordedFile, Code=%d"),Result);
	return Result;
}

int32 UGMEObject::PlayRecordedFile(const FString& FilePath,const FPlayRecordedFileCompleted& PlayRecordedFileCompleted)
{
	GetPlayCallbackMap().Add(FilePath,PlayRecordedFileCompleted);
	const int32 Result = ITMGContextGetInstance()->GetPTT()->PlayRecordedFile(TCHAR_TO_UTF8(*FilePath));
	if(nsgme::av::AV_OK != Result)
	{
		CSDK_LOG(LogGME,Error,TEXT("PlayRecordedFile, Code=%d"),Result);
	}
	CSDK_LOG(LogGME,Log,TEXT("PlayRecordedFile, Code=%d"),Result);
	return Result;
}

int32 UGMEObject::StopPlayFile()
{
	const int32 Result = ITMGContextGetInstance()->GetPTT()->StopPlayFile();
	if(nsgme::av::AV_OK != Result)
	{
		CSDK_LOG(LogGME,Error,TEXT("StopPlayFile, Code=%d"),Result);
	}
	CSDK_LOG(LogGME,Log,TEXT("StopPlayFile, Code=%d"),Result);
	return Result;
}

int32 UGMEObject::GetFileSize(const FString& FilePath)
{
	const int32 Result = ITMGContextGetInstance()->GetPTT()->GetFileSize(TCHAR_TO_UTF8(*FilePath));
	CSDK_LOG(LogGME,Log,TEXT("GetFileSize,FilePath=%s, Size=%d"),*FilePath,Result);
	return Result;
}

float UGMEObject::GetVoiceFileDuration(const FString& FilePath)
{
	const int32 Result = ITMGContextGetInstance()->GetPTT()->GetVoiceFileDuration(TCHAR_TO_UTF8(*FilePath));
	const float Duration = Result/1000.f;
	CSDK_LOG(LogGME,Log,TEXT("GetVoiceFileDuration,FilePath=%s, Duration=%d"),*FilePath,Result);
	return Duration;
}

int32 UGMEObject::SpeechToText(const FString& FileId,const FSpeechToTextCompleted& SpeechToTextCompleted)
{
	GetSpeechToTextCallbackMap().Add(FileId,SpeechToTextCompleted);
	const int32 Result =ITMGContextGetInstance()->GetPTT()->SpeechToText(TCHAR_TO_UTF8(*FileId));
	if(nsgme::av::AV_OK != Result)
	{
		CSDK_LOG(LogGME,Error,TEXT("SpeechToText, Code=%d"),Result);
	}
	CSDK_LOG(LogGME,Log,TEXT("SpeechToText, Code=%d"),Result);
	return Result;
}

int32 UGMEObject::StartStreamSpeechToText(const FStreamSpeechToTextRunning& InStreamSpeechToTextRunning,const FStreamSpeechToTextCompleted& StreamSpeechToTextCompleted)
{
	const FString FileName = GetRecordingFileName();
	StreamSpeechToTextRunning = InStreamSpeechToTextRunning;
	GetStreamSpeechToTextCompletedCallbackMap().Add(FileName,StreamSpeechToTextCompleted);
	const int32 Result = ITMGContextGetInstance()->GetPTT()->StartRecordingWithStreamingRecognition(TCHAR_TO_UTF8(*FileName));
	if(nsgme::av::AV_OK != Result)
	{
		CSDK_LOG(LogGME,Error,TEXT("SpeechToText, Code=%d"),Result);
	}
	CSDK_LOG(LogGME,Log,TEXT("SpeechToText, Code=%d"),Result);
	return Result;

}

int32 UGMEObject::StopStreamSpeechToText()
{
	CSDK_LOG(LogGME,Log,TEXT("Instead StopStreamSpeechToText By StopRecording"));
	return StopRecordingRaw();
}

int32 UGMEObject::EnterVoiceRoom(const FString& RoomId, const FEnterRoomCompleted& EnterRoomCompleted)
{
	switch (VoiceRoomStatus) {
		case EVoiceRoomStatus::NoRoom:
			break;
		case EVoiceRoomStatus::PreEnterRoom:
		case EVoiceRoomStatus::Online:
		case EVoiceRoomStatus::Offline:
		case EVoiceRoomStatus::PreExitRoom:
		default:
			CSDK_LOG(LogGME,Error,TEXT("EnterVoiceRoom Failed, Already has a VoiceRoom. CurrentRoomId:%s,  New RoomId:%s"),*CurrentRoomId,*RoomId);
			return -1;
	}

	if(RoomId.IsEmpty())
	{
		CSDK_LOG(LogGME,Error,TEXT("EnterVoiceRoom Failed, New RoomId is Empty"),*RoomId);
		return -1;
	}

	constexpr char AuthBuffer[ExpectLength] = {};
	//生成本地鉴权，用于相关功能的加密和鉴权(语音消息)
	const unsigned int BufferLength = QAVSDK_AuthBuffer_GenAuthBuffer( FCString::Atoi(*AppID), TCHAR_TO_UTF8(*RoomId) , TCHAR_TO_UTF8(*OpenId), TCHAR_TO_UTF8(*AppKey), (unsigned char *)AuthBuffer, ExpectLength);
	const int32 Result = ITMGContextGetInstance()->EnterRoom(TCHAR_TO_UTF8(*RoomId) , ITMG_ROOM_TYPE_FLUENCY, AuthBuffer,BufferLength);
	if(nsgme::av::AV_OK == Result)
	{
		VoiceRoomStatus=EVoiceRoomStatus::PreEnterRoom;
		CurrentRoomId = RoomId;
		EnterRoomCompletedCallback = EnterRoomCompleted;
	}
	CSDK_LOG(LogGME,Log,TEXT("EnterVoiceRoom, RoomId:%s, Result:%d"),*RoomId,Result);
	return Result;
}

int32 UGMEObject::ExitVoiceRoom(const FExitRoomCompleted& ExitRoomCompleted)
{
	switch (VoiceRoomStatus) {
		case EVoiceRoomStatus::NoRoom:
			CSDK_LOG(LogGME,Error,TEXT("ExitVoiceRoom Failed, No VoiceRoom"));
			return -1;
		case EVoiceRoomStatus::PreEnterRoom:
			break;
		case EVoiceRoomStatus::Online:
			break;
		case EVoiceRoomStatus::Offline:
			break;
		case EVoiceRoomStatus::PreExitRoom:
			CSDK_LOG(LogGME,Error,TEXT("ExitVoiceRoom Failed, Already in ExitVoiceRoom"));
			return -1;
		default:;
	}

	const int32 Result = ITMGContextGetInstance()->ExitRoom();
	if(nsgme::av::AV_OK == Result)
	{
		ExitRoomCompletedCallback = ExitRoomCompleted;
		VoiceRoomStatus = EVoiceRoomStatus::PreExitRoom;
	}
	CSDK_LOG(LogGME,Log,TEXT("ExitChatRoom, RoomId:%s, Result:%d"),*CurrentRoomId,Result);
	return Result;
}

int32 UGMEObject::SWitchVoiceRoom(const FString& RoomId, const FSwitchRoomCompleted& SwitchRoomCompleted)
{
	
	constexpr char AuthBuffer[ExpectLength] = {};
	const unsigned int BufferLength = QAVSDK_AuthBuffer_GenAuthBuffer( FCString::Atoi(*AppID), TCHAR_TO_UTF8(*RoomId) , TCHAR_TO_UTF8(*OpenId), TCHAR_TO_UTF8(*AppKey), (unsigned char *)AuthBuffer, ExpectLength);
	const int32 Result = ITMGContextGetInstance()->GetRoom()->SwitchRoom(TCHAR_TO_UTF8(*RoomId),AuthBuffer, BufferLength);

	if(nsgme::av::AV_OK == Result)
	{
		CurrentRoomId = RoomId;
		SwitchRoomCompletedCallback = SwitchRoomCompleted;
	}
	CSDK_LOG(LogGME,Log,TEXT("SWitchVoiceRoom, RoomId:%s, Result:%d"),*RoomId,Result);
	return Result;
}

TArray<FString> UGMEObject::GetVoiceRoomMemberOpenIds() const
{
	return VoiceRoomMembers;
}

int32 UGMEObject::OpenMic()
{
	UE_LOG(LogGME, Log, TEXT("Log: OpenMic"));
	const int32 Result = ITMGContextGetInstance()->GetAudioCtrl()->EnableMic(true);
	UE_LOG(LogGME, Log, TEXT("Log: OpenMic result code: %d"), Result);
	return Result;
}

int32 UGMEObject::CloseMic()
{
	UE_LOG(LogGME, Log, TEXT("Log: CloseMic"));
	const int32 Result = ITMGContextGetInstance()->GetAudioCtrl()->EnableMic(false);
	UE_LOG(LogGME, Log, TEXT("Log: CloseMic result code: %d"), Result);
	return Result;
}

int32 UGMEObject::OpenSpeaker()
{
	UE_LOG(LogGME, Log, TEXT("Log: OpenSpeaker"));
	const int32 Result = ITMGContextGetInstance()->GetAudioCtrl()->EnableSpeaker(true);
	UE_LOG(LogGME, Log, TEXT("Log: OpenSpeaker result code: %d"), Result);
	return Result;
}

int32 UGMEObject::CloseSpeaker()
{
	UE_LOG(LogGME, Log, TEXT("Log: CloseSpeaker"));
	const int32 Result = ITMGContextGetInstance()->GetAudioCtrl()->EnableSpeaker(false);
	UE_LOG(LogGME, Log, TEXT("Log: CloseSpeaker result code: %d"), Result);
	return Result;
}

int32 UGMEObject::SetMicVolume(int32 Vol)
{
	return ITMGContextGetInstance()->GetAudioCtrl()->SetMicVolume(Vol);
}

int32 UGMEObject::GetMicVolume()
{
	return ITMGContextGetInstance()->GetAudioCtrl()->GetMicVolume();
}

ECsdkMicState UGMEObject::GetMicState()
{
	return static_cast<ECsdkMicState>(ITMGContextGetInstance()->GetAudioCtrl()->GetMicState());
}

int32 UGMEObject::SetSpeakerVolume(int32 Vol)
{
	return ITMGContextGetInstance()->GetAudioCtrl()->SetSpeakerVolume(Vol);
}

int32 UGMEObject::GetSpeakerVolume()
{
	return ITMGContextGetInstance()->GetAudioCtrl()->GetSpeakerVolume();
}

ECsdkSpeakerState UGMEObject::GetSpeakerState()
{
	return static_cast<ECsdkSpeakerState>(ITMGContextGetInstance()->GetAudioCtrl()->GetMicState());
}


void UGMEObject::OnEvent(ITMG_MAIN_EVENT_TYPE eventType, const char *data) {

	CSDK_LOG(LogGME, Log, TEXT("eventType:%d,OnEvent %s"),eventType, UTF8_TO_TCHAR(data));
	TSharedPtr<FJsonObject> JsonObject;
	TSharedRef<TJsonReader<>> Reader = TJsonReaderFactory<>::Create(FString(UTF8_TO_TCHAR(data)));
	FJsonSerializer::Deserialize(Reader, JsonObject);

	//加入房间回调
 	if (eventType == ITMG_MAIN_EVENT_TYPE_ENTER_ROOM) {
 		OnEnterVoiceRoom(JsonObject);
 	}
	//退出房间
 	else if (eventType == ITMG_MAIN_EVENT_TYPE_EXIT_ROOM) {
 		OnExitVoiceRoom(JsonObject);
 	}
	//语音房间断线回调
 	else if (eventType == ITMG_MAIN_EVENT_TYPE_ROOM_DISCONNECT) {
 		OnVoiceRoomDisconnect(JsonObject);
 	}
	//语音房间断线重连回调
	else if(eventType ==ITMG_MAIN_EVENT_TYPE_RECONNECT_START )
	{
	    OnVoiceRoomReconnectStart(JsonObject);
	}
	//语音房间断线重连成功回调
    else if(eventType ==ITMG_MAIN_EVENT_TYPE_RECONNECT_SUCCESS )
    {
    	OnVoiceRoomReconnectSuccess(JsonObject);
    }
	//成员变更回调
 	else if (eventType == ITMG_MAIN_EVENT_TYPE_USER_UPDATE) {
 		OnVoiceRoomUserUpdate(JsonObject);
 	}
	//录音完成回调
 	else if (eventType == ITMG_MAIN_EVENT_TYPE_PTT_RECORD_COMPLETE) {
 		OnRecordingCompletedEvent(JsonObject);
 	}
	//上传语音文件回调
 	else if (eventType == ITMG_MAIN_EVENT_TYPE_PTT_UPLOAD_COMPLETE) {
 		OnUploadRecordedFileCompletedEvent(JsonObject);
 	}
	//下载语音文件回调
 	else if (eventType == ITMG_MAIN_EVENT_TYPE_PTT_DOWNLOAD_COMPLETE) {
 		OnDownloadRecordedFileCompletedEvent(JsonObject);
 	}
	//播放语音回调
 	else if (eventType == ITMG_MAIN_EVENT_TYPE_PTT_PLAY_COMPLETE) {
 		OnPlayRecordedFileCompletedEvent(JsonObject);
 	}
	//语音转文本回调
 	else if (eventType == ITMG_MAIN_EVENT_TYPE_PTT_SPEECH2TEXT_COMPLETE) {
 		OnSpeechToTextCompletedEvent(JsonObject);
 	}
	//停止录制并完成识别后才返回文字，相当于一段话说完才会返回识别的文字
     else if(eventType == ITMG_MAIN_EVENT_TYPE_PTT_STREAMINGRECOGNITION_COMPLETE)
     {
     	OnStreamSpeechToTextCompletedEvent(JsonObject);
     }
	//录音过程中就会实时返回识别到的文字，相当于边说话边返回识别到的文字。
     else if(eventType == ITMG_MAIN_EVENT_TYPE_PTT_STREAMINGRECOGNITION_IS_RUNNING)
     {
     	OnStreamSpeechToTextRunningEvent(JsonObject);
     }
     else if(eventType == ITMG_MAIN_EVENT_TYPE_SWITCH_ROOM)//切换房间回调
     {
     	int32 nResult = JsonObject->GetIntegerField(TEXT("result"));
     	FString text = JsonObject->GetStringField(TEXT("error_info"));
     	SwitchRoomCompletedCallback.ExecuteIfBound(nResult,text);
     	// StreamRealTimeASR.Broadcast(nResult, text);
     }
}



void UGMEObject::OnRecordingCompletedEvent(const TSharedPtr<FJsonObject>& JsonObject)
{
	const int32 Result = JsonObject->GetIntegerField(TEXT("result"));
	const FString FilePath = JsonObject->GetStringField(TEXT("file_path"));
	float Duration = 0;
	int32 FileSize = 0;
	if (Result == nsgme::av::AV_OK) {
		Duration = ITMGContextGetInstance()->GetPTT()->GetVoiceFileDuration(TCHAR_TO_UTF8(*FilePath));
		FileSize = ITMGContextGetInstance()->GetPTT()->GetFileSize(TCHAR_TO_UTF8(*FilePath));
	}
	else
	{
		CSDK_LOG(LogGME,Error,TEXT("OnStopRecordingEvent,code=%d"),Result);
	}
	RecordFileCompleted.ExecuteIfBound(Result, FilePath, Duration, FileSize);
	RecordFileCompleted.Unbind();
}

void UGMEObject::OnUploadRecordedFileCompletedEvent(const TSharedPtr<FJsonObject>& JsonObject)
{
	const int32 Result = JsonObject->GetIntegerField(TEXT("result"));
	const FString FilePath = JsonObject->GetStringField(TEXT("file_path"));
	const FString FileId = JsonObject->GetStringField(TEXT("file_id"));

	FString DebugString;
	TSharedRef<TJsonWriter<>> JsonWriter = TJsonWriterFactory<>::Create(&DebugString);
	FJsonSerializer::Serialize(JsonObject.ToSharedRef(), JsonWriter);
	CSDK_LOG(LogGME,Log,TEXT("OnUploadRecordedFileCompletedEvent:%s"),*DebugString);
	
	if(const auto CallbackPtr = GetUploadCallbackMap().Find(FilePath))
	{
		CallbackPtr->ExecuteIfBound(Result, FilePath, FileId);
		GetUploadCallbackMap().Remove(FilePath);
		return;
	}
	CSDK_LOG(LogGME, Error, TEXT("OnUploadFile Error,No Callback,FilePath:%s"), *FilePath);
}

void UGMEObject::OnDownloadRecordedFileCompletedEvent(const TSharedPtr<FJsonObject>& JsonObject) 
{
	const int32 Result = JsonObject->GetIntegerField(TEXT("result"));
	const FString FilePath = JsonObject->GetStringField(TEXT("file_path"));
	const FString FileId = JsonObject->GetStringField(TEXT("file_id"));
	
	if(Result != nsgme::av::AV_OK)
	{
		CSDK_LOG(LogGME,Error,TEXT("OnDownloadFile Code:%d,FilePath:%s,FileID:%s"),Result,*FilePath,*FileId);
	}
	CSDK_LOG(LogGME,Log,TEXT("OnDownloadFile Code:%d,FilePath:%s,FileID:%s"),Result,*FilePath,*FileId);

	if(const auto CallbackPtr = GetDownloadCallbackMap().Find(FileId))
	{
		CallbackPtr->ExecuteIfBound(Result, FilePath, FileId);
		GetDownloadCallbackMap().Remove(FileId);
		return;
	}
	CSDK_LOG(LogGME, Error, TEXT("OnDownloadFile Error,No Callback,FileId:%s"), *FileId);
}

void UGMEObject::OnPlayRecordedFileCompletedEvent(const TSharedPtr<FJsonObject>& JsonObject) 
{
	const int32 Result = JsonObject->GetIntegerField(TEXT("result"));
	const FString FilePath = JsonObject->GetStringField(TEXT("file_path"));

	if(const auto CallbackPtr = GetPlayCallbackMap().Find(FilePath))
	{
		CallbackPtr->ExecuteIfBound(Result);
		GetPlayCallbackMap().Remove(FilePath);
		return;
	}
	CSDK_LOG(LogGME, Error, TEXT("OnPlayRecordedFile Error,No Callback,FilePath:%s"), *FilePath);
}

void UGMEObject::OnSpeechToTextCompletedEvent(const TSharedPtr<FJsonObject>& JsonObject) 
{
	const int32 Result = JsonObject->GetIntegerField(TEXT("result"));
	const FString FileId = JsonObject->GetStringField(TEXT("file_id"));
	const FString Text = JsonObject->GetStringField(TEXT("text"));

	if(const auto CallbackPtr = GetSpeechToTextCallbackMap().Find(FileId))
	{
		CallbackPtr->ExecuteIfBound(Result,FileId,Text);;
		GetSpeechToTextCallbackMap().Remove(FileId);
		return;
	}
	CSDK_LOG(LogGME, Error, TEXT("OnSpeechToText Error,No Callback,FileId:%s"), *FileId);
}

void UGMEObject::OnStreamSpeechToTextCompletedEvent(const TSharedPtr<FJsonObject>& JsonObject) 
{
	const int32 Result = JsonObject->GetIntegerField(TEXT("result"));
	const FString Text = JsonObject->GetStringField(TEXT("text"));
	const FString FilePath = JsonObject->GetStringField(TEXT("file_path"));
	const FString FileId = JsonObject->GetStringField(TEXT("file_id"));

	if(const auto CallbackPtr = GetStreamSpeechToTextCompletedCallbackMap().Find(FilePath))
	{
		CallbackPtr->ExecuteIfBound(Result,FilePath,FileId,Text);
		GetStreamSpeechToTextCompletedCallbackMap().Remove(FilePath);
		return;
	}
	CSDK_LOG(LogGME, Error, TEXT("OnStreamSpeechToText Error,No Callback,FilePath:%s"), *FilePath);
}

void UGMEObject::OnStreamSpeechToTextRunningEvent(const TSharedPtr<FJsonObject>& JsonObject) 
{
	int32 Index = JsonObject->GetIntegerField(TEXT("index"));
	int32 SliceType = JsonObject->GetIntegerField(TEXT("slice_type"));
	const FString Text = JsonObject->GetStringField(TEXT("text"));

	StreamSpeechToTextRunning.ExecuteIfBound(Index,SliceType,Text);
}

void UGMEObject::OnEnterVoiceRoom(const TSharedPtr<FJsonObject>& JsonObject)
{
	const int32 Result = JsonObject->GetIntegerField(TEXT("result"));
	const FString ErrorInfo = JsonObject->GetStringField(TEXT("error_info"));
	CSDK_LOG(LogGME, Log, TEXT("Enter room Callback. result=%d, info = %ls"), Result, *ErrorInfo);
	
	if(VoiceRoomStatus!=EVoiceRoomStatus::PreEnterRoom)
	{
		CSDK_LOG(LogGME, Error, TEXT("VoiceRoomStatus Error:VoiceRoomStatus=%d"),VoiceRoomStatus);
		return;
	}

	if(EnterRoomCompletedCallback.IsBound())
	{
		EnterRoomCompletedCallback.Execute(Result,CurrentRoomId);
		EnterRoomCompletedCallback.Unbind();
	}

	//加入房间不成功
	if(Result!=0)
	{
		VoiceRoomStatus =EVoiceRoomStatus::NoRoom;
		CurrentRoomId.Empty();
	}
	else
	{
		VoiceRoomStatus = EVoiceRoomStatus::Online;
		bHasVoiceRoom = true;
		VoiceRoomMembers.Empty();
	}
	
}

void UGMEObject::OnExitVoiceRoom(const TSharedPtr<FJsonObject>& JsonObject)
{
	const int32 Result = JsonObject->GetIntegerField(TEXT("result"));
	const FString ErrorInfo = JsonObject->GetStringField(TEXT("error_info"));
	CSDK_LOG(LogGME, Log, TEXT("ExitRoom Callback. result=%d, info = %s"), Result, *ErrorInfo);

	switch (VoiceRoomStatus) {
	case EVoiceRoomStatus::NoRoom:
		CSDK_LOG(LogGME,Error,TEXT("ExitChatRoom Failed, No ChatRoom"));
		return;
	case EVoiceRoomStatus::PreEnterRoom:
		break;
	case EVoiceRoomStatus::Online:
		break;
	case EVoiceRoomStatus::Offline:
		break;
	case EVoiceRoomStatus::PreExitRoom:
		break;
	default:;
	}
	
	VoiceRoomStatus =EVoiceRoomStatus::NoRoom;
	const FString PreRoomId =CurrentRoomId;
	CurrentRoomId.Empty();
	VoiceRoomMembers.Empty();
	bHasVoiceRoom = false;
	
	if(ExitRoomCompletedCallback.IsBound())
	{
		ExitRoomCompletedCallback.Execute(Result,PreRoomId);
		ExitRoomCompletedCallback.Unbind();
	}
	
}

void UGMEObject::OnVoiceRoomDisconnect(const TSharedPtr<FJsonObject>& JsonObject)
{
	const int32 Result = JsonObject->GetIntegerField(TEXT("result"));
	const FString ErrorInfo = JsonObject->GetStringField(TEXT("error_info"));
	CSDK_LOG(LogGME, Log, TEXT("OnVoiceRoomDisconnect. result=%d, info = %s"), Result, *ErrorInfo);
	VoiceRoomStatus = EVoiceRoomStatus::Offline;
	VoiceSupportSubsystem->OnVoiceRoomDisconnect.Broadcast(Result,ErrorInfo);
}

void UGMEObject::OnVoiceRoomReconnectStart(const TSharedPtr<FJsonObject>& JsonObject)
{
	const int32 Result = JsonObject->GetIntegerField(TEXT("result"));
	const FString ErrorInfo = JsonObject->GetStringField(TEXT("error_info"));
	CSDK_LOG(LogGME, Log, TEXT("OnVoiceRoomReconnectStart. result=%d, info = %s"), Result, *ErrorInfo);
	VoiceSupportSubsystem->OnVoiceRoomReconnectStart.Broadcast(Result,ErrorInfo);
}

void UGMEObject::OnVoiceRoomReconnectSuccess(const TSharedPtr<FJsonObject>& JsonObject)
{
	const int32 Result = JsonObject->GetIntegerField(TEXT("result"));
	const FString ErrorInfo = JsonObject->GetStringField(TEXT("error_info"));
	CSDK_LOG(LogGME, Log, TEXT("OnVoiceRoomReconnectSuccess. result=%d, info = %s"), Result, *ErrorInfo);
	VoiceRoomStatus = EVoiceRoomStatus::Online;
	VoiceSupportSubsystem->OnVoiceRoomReconnectSuccess.Broadcast(Result,ErrorInfo);
}

void UGMEObject::OnVoiceRoomUserUpdate(const TSharedPtr<FJsonObject>& JsonObject)
{
	const int32 EventId = JsonObject->GetIntegerField(TEXT("event_id"));
	TArray<FString> IDs;
	TArray<TSharedPtr<FJsonValue>> UserList = JsonObject->GetArrayField(TEXT("user_list"));
	for (int i = 0; i < UserList.Num(); i++) {
		IDs.Add(UserList[i]->AsString());
	}
	switch (EventId)
	{
	case ITMG_EVENT_ID_USER_ENTER:
		//有成员进入房间
		OnUserEnterVoiceRoom(IDs);
		break;
	case ITMG_EVENT_ID_USER_EXIT:
		//有成员退出房间
		OnUserExitVoiceRoom(IDs);
		break;
	case ITMG_EVENT_ID_USER_HAS_AUDIO:
		//有成员发送音频包
		VoiceSupportSubsystem->OnMembersSpeakStart.Broadcast(IDs);

		break;
	case ITMG_EVENT_ID_USER_NO_AUDIO:
		//有成员停止发送音频包
		VoiceSupportSubsystem->OnMembersSpeakStop.Broadcast(IDs);
		break;
	case ITMG_EVENT_ID_USER_MIC_OPENED:
		//有成员打开麦克风
		VoiceSupportSubsystem->OnMembersMicOpened.Broadcast(IDs);

		break;
	case ITMG_EVENT_ID_USER_MIC_CLOSED:
		//有成员关闭麦克风
		VoiceSupportSubsystem->OnMembersMicClosed.Broadcast(IDs);
		break;
	default:
		break;
	}
}

void UGMEObject::OnUserEnterVoiceRoom(const TArray<FString>& NewMembers)
{
	VoiceSupportSubsystem->OnUserEnterVoiceRoom.Broadcast(NewMembers);
	VoiceRoomMembers.Append(NewMembers);
}

void UGMEObject::OnUserExitVoiceRoom(const TArray<FString>& ExitMembers)
{
	for(const auto& ExitMember:ExitMembers)
	{
		VoiceRoomMembers.Remove(ExitMember);
	}
	VoiceSupportSubsystem->OnUserExitVoiceRoom.Broadcast(ExitMembers);
}

FString UGMEObject::GetRecordingFileName()
{
	const FString OutFilename = FString::Printf(TEXT("%s.%s"), *FDateTime::Now().ToString(), TEXT("ogg"));
	auto Result = FPaths::Combine(GetFileStorePath(), OutFilename);
	CSDK_LOG(LogGME, Log, TEXT("GetRecordingFileName:%s"), *Result);
	return Result;
}

FString UGMEObject::GetDownloadFilePath()
{
	return FPaths::Combine(GetFileStorePath(),TEXT("GME")) ;
}

