bool FOnlineUserCloudSteam::ClearFile(const FUniqueNetId& UserId, const FString& FileName)
{
	FScopeLock(&SteamSubsystem->UserCloudDataLock);
	// Search for the specified file
	FSteamUserCloudData* UserCloudData = SteamSubsystem->GetUserCloudEntry(UserId);
	if (UserCloudData)
	{
		return UserCloudData->ClearFileData(FileName);
	}

	return true;
}
bool FOnlineUserCloudSteam::WriteUserFile(const FUniqueNetId& UserId, const FString& FileName, TArray<uint8>& FileContents)
{
	FScopeLock(&SteamSubsystem->UserCloudDataLock);
	// Create or get the current entry for this file
	FSteamUserCloudData* UserCloud = SteamSubsystem->GetUserCloudEntry(UserId);
	if (UserCloud && FileName.Len() > 0)
	{
		FCloudFile* UserCloudFile = UserCloud->GetFileData(FileName, true);
		UserCloudFile->AsyncState = EOnlineAsyncTaskState::InProgress;
		SteamSubsystem->QueueAsyncTask(new FOnlineAsyncTaskSteamWriteUserFile(SteamSubsystem, FUniqueNetIdSteam(*(uint64*)UserId.GetBytes()), FileName, FileContents));
		return true;
	}

	return false;
}
bool FOnlineUserCloudSteam::GetFileContents(const FUniqueNetId& UserId, const FString& FileName, TArray<uint8>& FileContents)
{
	FScopeLock(&SteamSubsystem->UserCloudDataLock);
	// Search for the specified file and return the raw data
	FSteamUserCloudData* UserCloudData = SteamSubsystem->GetUserCloudEntry(UserId);
	if (UserCloudData)
	{
		FCloudFile* SteamCloudFile = UserCloudData->GetFileData(FileName);
		if (SteamCloudFile && SteamCloudFile->AsyncState == EOnlineAsyncTaskState::Done && SteamCloudFile->Data.Num() > 0)
		{
			FileContents = SteamCloudFile->Data;
			return true;
		}
	}
	
	return false;
}
void FOnlineUserCloudSteam::DumpCloudFileState(const FUniqueNetId& UserId, const FString& FileName)
{
	if (FileName.Len() > 0)
	{
		UE_LOG_ONLINE(Log, TEXT("Cloud File State file %s:"), *FileName);
		{
			FScopeLock(&SteamSubsystem->UserCloudDataLock);
			FSteamUserCloudData* UserCloud = SteamSubsystem->GetUserCloudEntry(UserId);
			FCloudFileHeader* FileMetadata = UserCloud->GetFileMetadata(FileName);
			if (FileMetadata)
			{
				UE_LOG_ONLINE(Log, TEXT("\tMeta: FileName:%s DLName:%s FileSize:%d Hash:%s"), *FileMetadata->FileName, *FileMetadata->DLName, FileMetadata->FileSize, *FileMetadata->Hash);
			}
			else
			{
				UE_LOG_ONLINE(Log, TEXT("\tNo metadata found!"));
			}

			FCloudFile* FileData = UserCloud->GetFileData(FileName);
			if (FileData)
			{
				UE_LOG_ONLINE(Log, TEXT("\tFileCache: FileName:%s State:%s CacheSize:%d"), *FileData->FileName, EOnlineAsyncTaskState::ToString(FileData->AsyncState), FileData->Data.Num());
			}
			else
			{
				UE_LOG_ONLINE(Log, TEXT("\tNo cache entry found!"));
			}
		}

		int32 FileSize = SteamRemoteStorage()->GetFileSize(TCHAR_TO_UTF8(*FileName));

		UE_LOG_ONLINE(Log, TEXT("\tSteam: FileName:%s Size:%d Exists:%s Persistent:%s"),
			*FileName, FileSize, 
			SteamRemoteStorage()->FileExists(TCHAR_TO_UTF8(*FileName)) ? TEXT("Y") : TEXT("N"),
			SteamRemoteStorage()->FilePersisted(TCHAR_TO_UTF8(*FileName)) ? TEXT("Y") : TEXT("N"));
	}
}
uint32 UAURDriverOpenCV::FWorkerRunnable::Run()
{
	UE_LOG(LogAUR, Log, TEXT("AURDriverOpenCV: Worker thread start"))

	UAURVideoSource* current_video_source = nullptr;

	while (this->bContinue)
	{
		// whether we switch to a new vid src in this iteration
		bool new_video_source_now = false;
		FAURVideoConfiguration video_config_to_open;

		// Switch video source
		{
			FScopeLock lock(&Driver->VideoSourceLock);

			if (Driver->SwitchToNextVideoSource)
			{
				// Disconnect from previous video source
				if (current_video_source)
				{
					current_video_source->Disconnect();
				}

				video_config_to_open = Driver->NextVideoConfiguration; //save in local var in case it is modified before we connect to video source
				UAURVideoSource* next_src_obj = video_config_to_open.VideoSourceObject;

				const FString vid_src_name = next_src_obj ? next_src_obj->GetIdentifier() : "NULL";
				UE_LOG(LogAUR, Log, TEXT("AURDriverOpenCV: Switching video source to [%s]"), *vid_src_name);

				current_video_source = next_src_obj;
				// need to be kept in UPROPERTY for GC
				Driver->VideoSource = current_video_source;

				new_video_source_now = true;
				Driver->SwitchToNextVideoSource = false;
			}
		}

		// activate new video source after switch
		// this is outside the previous block because opening connection can take time
		// and we don't want to block VideoSourceLock
		if (new_video_source_now)
		{
			if (current_video_source)
			{
				current_video_source->Connect(video_config_to_open);
			}

			Driver->OnVideoSourceSwitch();
		}

		// If no video source or it is not open, wait for a change
		if (!current_video_source || !current_video_source->IsConnected())
		{
			FPlatformProcess::Sleep(0.25);
		}
		else
		{
			// get a new frame from camera - this blocks untill the next frame is available
			current_video_source->GetNextFrame(CapturedFrame);

			// compare the frame size to the size we expect from capture parameters
			auto frame_size = CapturedFrame.size();

			if (frame_size.width != Driver->FrameResolution.X || frame_size.height != Driver->FrameResolution.Y)
			{
				FIntPoint new_camera_res(frame_size.width, frame_size.height);

				UE_LOG(LogAUR, Error, TEXT("AURDriverOpenCV: Source returned frame of size %dx%d but %dx%d was expected from source's GetResolution()"),
					new_camera_res.X, new_camera_res.Y, Driver->FrameResolution.X, Driver->FrameResolution.Y);

				Driver->OnCameraPropertiesChange(new_camera_res);
			}
			else
			{
				if (Driver->IsCalibrationInProgress()) // calibration
				{
					if (Driver->WorldReference)
					{
						FScopeLock(&Driver->CalibrationLock);
						Driver->CalibrationProcess.ProcessFrame(CapturedFrame, Driver->WorldReference->RealTimeSeconds);

						if (Driver->CalibrationProcess.IsFinished())
						{
							Driver->OnCalibrationFinished();
						}
					}
					else
					{
						UE_LOG(LogAUR, Error, TEXT("AURDriverOpenCV: WorldReference is null, cannot measure time for calibration"))
					}
				}
				else if (this->Driver->bPerformOrientationTracking)
				{
					/**
					* Tracking markers and relative position with respect to them
					*/
					{
						// lock moved to AURArucoTracker
						//FScopeLock lock(&Driver->TrackerLock);
						Driver->Tracker.DetectMarkers(CapturedFrame);
					}
				}

				// ---------------------------
				// Create the frame to publish

				// Frame to fill is in RGBA format
				FColor* dest_pixel_ptr = Driver->WorkerFrame->Image.GetData();

				for (int32 pixel_r = 0; pixel_r < CapturedFrame.rows; pixel_r++)
				{
					for (int32 pixel_c = 0; pixel_c < CapturedFrame.cols; pixel_c++)
					{
						cv::Vec3b& src_pixel = CapturedFrame.at<cv::Vec3b>(pixel_r, pixel_c);

						// Captured image is in BGR format
						dest_pixel_ptr->R = src_pixel.val[2];
						dest_pixel_ptr->G = src_pixel.val[1];
						dest_pixel_ptr->B = src_pixel.val[0];

						dest_pixel_ptr++;
					}
				}

				Driver->StoreWorkerFrame();
			}
		}
	}
void FOnlineUserCloudSteam::GetUserFileList(const FUniqueNetId& UserId, TArray<FCloudFileHeader>& UserFiles)
{
	FScopeLock(&SteamSubsystem->UserCloudDataLock);
	FSteamUserCloudData* UserMetadata = SteamSubsystem->GetUserCloudEntry(UserId);  
	UserFiles = UserMetadata->CloudMetadata;
}