// (CVN) - Candy Valley Network GmbH

#pragma once

#include <CoreMinimal.h>
#include <Engine/Datatable.h>
// EXTERNAL INCLUDES
#include <Kismet/BlueprintFunctionLibrary.h>

// INTERNAL INCLUDES
#include "LovenseTypes.h"
#include <Containers/Array.h>

#include "LovenseFunctionLibrary.generated.h"



/**
 * @brief Blueprint exposed wrapper library for the Lovense Manager.
 */
UCLASS()
class LOVENSEINTEGRATION_API ULovenseFunctionLibrary : public UBlueprintFunctionLibrary {
	GENERATED_BODY()

public:
	ULovenseFunctionLibrary(const FObjectInitializer& ObjectInitializer);

	/**
	 * @brief Will generate the lovense config file and automatically start the integration if "StartWithLovenseActive" config is true.
	 * \n Usually gets called automatically in StartupModule(), but you can set the cvar "li.Initialization.Automatic" to false if you want to manually initialize.
	 * \n It's safe to call this even if the integration is already running.
	 */
	UFUNCTION(BlueprintCallable, Category = "Lovense")
	static void Initialize();

	/**
	 * @brief Will start the integration. This includes:
	 * \n	1. Fetching config values.
	 * \n	2. Starting the heartbeat to update timers.
	 * \n	3. Notifying listeners that the integration has started.
	 * \n	4. Updating adapters.
	 * @note Does not check if the integration is already running, as calling this while the integration is already running is safe.
	 */
	UFUNCTION(BlueprintCallable, Category = "Lovense")
	static void StartLovense();

	/**
	 * @brief Will stop the integration. This includes:
	 * \n	1. Stopping and resetting timers.
	 * \n	2. Clearing any currently running commands.
	 * \n	3. Clearing active and temporary data.
	 * \n	4. Stopping the heartbeat.
	 * \n	5. Notifying listeners that the integration has stopped.
	 * @note Does not check if the integration is already stopped, as calling this while the integration is already stopped is safe.
	 */
	UFUNCTION(BlueprintCallable, Category = "Lovense")
	static void StopLovense();

	/** @brief Will fetch all available lovense adapters and update toys. */
	UFUNCTION(BlueprintCallable, Category = "Lovense")
	static void UpdateAdapters();

	/** @brief Will fetch all available toys from the currently active lovense adapters. */
	UFUNCTION(BlueprintCallable, Category = "Lovense")
	static void UpdateToys();

	/** @brief Whether the integration is currently running. */
	UFUNCTION(BlueprintPure, Category = "Lovense")
	static bool IsLovenseRunning();

	/** @brief Whether the lovense adapters are currently being updated. */
	UFUNCTION(BlueprintPure, Category = "Lovense")
	static bool IsUpdatingAdapters();

	/** @brief Whether the toys are currently being updated. */
	UFUNCTION(BlueprintPure, Category = "Lovense")
	static bool IsUpdatingToys();

	/**
	 * @brief All currently available toys.
	 * \n <b>Warning -</b> If you cache these, make sure to subscribe to ULovenseEvents::onLovenseUpdatedToys and re-get them, as the toy objects will have been destroyed.
	 */
	UFUNCTION(BlueprintPure, Category = "Lovense")
	static const TArray<class ULovenseToy*>& GetToys();

	/**
	 * @brief Will command a single or all toys to set the speed of all functions to 0.
	 * @param toy Specifies the toy commands should be cleared on. Can be nullptr, in that case commands are cleared on all available toys.
	 */
	UFUNCTION(BlueprintCallable, Category = "Lovense")
	static void ClearAllCommands(class ULovenseToy* toy = nullptr);

	/**
	 * @brief Will send the specified command to a single or all available toys.
	 * @note See the respective SendCommand_*() functions for more information on the respective commands.
	 * @param command The command to be sent.
	 * @param toy Specifies the toy the command is sent to. Can be nullptr, in that case the command is sent to all available toys.
	 * @param valueA Not relevant for the Test, RotateChange, AirIn, AirOut and Battery command. The speed of the vibration. Valid range is 0-20, 0 meaning off.
	 * @param time Only relevant for the A* commands. Time in seconds for how long the command should run. Only full seconds are supported.
	 * @param valueB Only relevant for the AVibRotate AVibAir commands.
	 * \n For AVibRotate: The speed of the rotation. Valid range is 0-20, 0 meaning off.
	 * \n For AVibAir: The speed of the air pulsation. Valid range is 0-3, 0 meaning off.
	 * @param valueC Only relevant for the AVibRotateAir command. The speed of the air pulsation. Valid range is 0-3, 0 meaning off.
	 * @param pattern Only relevant for the Pattern command. The data defining the pattern. See FLovensePattern for more information.
	 * @param callback Only relevant for the Battery command.
	 * @return Whether the command was successfully sent.
	 */
	UFUNCTION(BlueprintCallable, Category = "Lovense")
	static bool SendCommands(
		TArray<class UELovenseCommands*> commands,
		ULovenseToy* toy,
		float time,
		float loopRunningSec,
		float loopPauseSec,
		FOnLovenseResponseDynamic callback
	);
	UFUNCTION(BlueprintCallable, Category = "Lovense")
	static bool SendPosition(
		int position,
		ULovenseToy* toy,
		FOnLovenseResponseDynamic callback
	);

	UFUNCTION(BlueprintCallable, Category = "Lovense")
	static bool SetUpPatternV2(
		TArray<class UEPositionPatternValue*> positionValues,
		ULovenseToy* toy,
		FOnLovenseResponseDynamic callback
	);

	UFUNCTION(BlueprintCallable, Category = "Lovense")
	static bool StartPatternV2(
		ULovenseToy* toy,
		int startTime,
		int offsetTime,
		FOnLovenseResponseDynamic callback
	);

	UFUNCTION(BlueprintCallable, Category = "Lovense")
	static bool StopPatternV2(
		ULovenseToy* toy,
		FOnLovenseResponseDynamic callback
	);

	UFUNCTION(BlueprintCallable, Category = "Lovense")
	static bool GetPatternV2SyncTime(
		ULovenseToy* toy,
		FOnGetSyncTime callback
	);
	

	/*UFUNCTION(BlueprintCallable, Category = "Lovense")
	static bool SendCommands1(
			ULovenseToy* toy,
			float time,
			float loopRunningSec,
			float loopPauseSec,
			FOnLovenseResponseDynamic callback,
			int vibrate = -1,
			int vibrate1 = -1,
			int vibrate2 = -1,
			int vibrate3 = -1,
			int rotate = -1,
			int pump = -1,
			int thrust = -1,
			int suction = -1,
			int fingering = -1,
			int depth = -1
			
		);
*/
	UFUNCTION(BlueprintCallable, Category = "Lovense")
	static bool SendCommand_Preset(
		class ULovenseToy* toy,
		const FString& name,
		float timeSec,
		FOnLovenseResponseDynamic callback
	);

	UFUNCTION(BlueprintCallable, Category = "Lovense")
	static bool SendCommand_Stop(
		class ULovenseToy* toy,
		FOnLovenseResponseDynamic callback
	);
	
	UFUNCTION(BlueprintCallable, Category = "Lovense")
	static bool SendCommand_Pattern(
		class ULovenseToy* toy,
		const TArray<int32>& pattern,
		FOnLovenseResponseDynamic callback,
		bool bVibrate = true,
		bool bRotate = true,
		bool bPump = true,
		bool bThrust = true,
		bool bSuck = true,
		bool bFinger = true,
		bool bDepth = true,
		bool bOscillate = true,
		int32 interval = 100,
		float time = 1
	);

	/** @brief Object holding global Lovense Integration delegates. */
	UFUNCTION(BlueprintPure, Category = "Lovense")
	static class ULovenseEvents* GetLovenseEvents();

	/**
	 * @brief Fetch the config value "StartWithLovenseActive" from config.
	 * \n If true, lovense will be started on StartupModule(). Defaults to false.
	 */
	UFUNCTION(BlueprintPure, Category = "Lovense")
	static bool GetStartWithLovenseActive();

	/** @brief Fetch the config value "DeviceIpOverride" from config. If this ip is valid, it will be used to connect with the adapters. */
	UFUNCTION(BlueprintPure, Category = "Lovense")
	static FString GetDeviceIpOverride();

	/** @brief Fetch the config value "DevicePortOverride" from config. If this port is valid, it will be used to connect with the adapters. */
	UFUNCTION(BlueprintPure, Category = "Lovense")
	static FString GetDevicePortOverride();
	/** @brief Fetch the config value "DevicePortOverride" from config. If this port is valid, it will be used to connect with the adapters. */

	UFUNCTION(BlueprintPure, Category = "Lovense")
	static bool GetUseInputDevice();

	UFUNCTION(BlueprintPure, Category = "Lovense")
	static bool GetUseHttps();

	/**
	 * @brief Get the cached "ToyDelay" config value.
	 * \n Toy delay is in milliseconds. Value range is 0-2000ms and defaults to 500ms.
	 * \n The toy delay adjusts the start time for pattern generation based on animations. (See ULovenseToyAnimationControlComponent)
	 */
	UFUNCTION(BlueprintPure, Category = "Lovense")
	static int32 GetToyDelay();

	/**
	 * @brief Get the cached "ToyStrengthMultiplier" config value.
	 * \n Value range is 0.0f-1.0f and defaults to 1.0f.
	 * \n The toy strength multiplier scales the speed values before commands are sent.
	 */
	UFUNCTION(BlueprintPure, Category = "Lovense")
	static float GetToyStrengthMultiplier();



	/** @brief Checks if ipString is a valid IP. */
	UFUNCTION(BlueprintPure, Category = "Lovense")
	static bool IsIPStringValid(const FString& ipString);

	/** @brief Checks if portString is a valid port. */
	UFUNCTION(BlueprintPure, Category = "Lovense")
	static bool IsPortStringValid(const FString& portString);

	/**
	 * @brief Write the config value "StartWithLovenseActive" to config.
	 * \n If true, lovense will be started on StartupModule(). Defaults to false.
	 */
	UFUNCTION(BlueprintCallable, Category = "Lovense")
	static void SetStartWithLovenseActive(bool bStartActive);

	/** @brief Write the config value "DeviceIpOverride" to config. If this ip is valid, it will be used to connect with the adapters. */
	UFUNCTION(BlueprintCallable, Category = "Lovense")
	static void SetDeviceIpOverride(const FString& ipOverride);

	/** @brief Write the config value "DevicePortOverride" to config. If this port is valid, it will be used to connect with the adapters. */
	UFUNCTION(BlueprintCallable, Category = "Lovense")
	static void SetDevicePortOverride(const FString& portOverride);

	
	/**
	 * @brief Set cached "ToyDelay" config value and write it to config.
	 * \n Toy delay is in milliseconds. Value range is 0-2000ms and defaults to 500ms.
	 * \n The value only gets clamped on load, so caller should make sure it is in range.
	 * \n The toy delay adjusts the start time for pattern generation based on animations. (See ULovenseToyAnimationControlComponent)
	 */
	UFUNCTION(BlueprintCallable, Category = "Lovense")
	static void SetToyDelay(int32 value);

	/**
	 * @brief Set cached "ToyStrengthMultiplier" config value and write it to config.
	 * \n Value range is 0.0f-1.0f and defaults to 1.0f.
	 * \n The value only gets clamped on load, so caller should make sure it is in range.
	 * \n The toy strength multiplier scales the speed values before commands are sent.
	 */
	UFUNCTION(BlueprintCallable, Category = "Lovense")
	static void SetToyStrengthMultiplier(float value);

	UFUNCTION(BlueprintCallable, Category = "Lovense")
	static void SetUseInputDevice(bool value);

	UFUNCTION(BlueprintCallable, Category = "Lovense")
	static void SetUseHttps(bool value);

	UFUNCTION(BlueprintCallable, Category = "Lovense")
	static void RestartSearch();

	/**
	 * @brief Set cached "ToyVibration" config value and write it to config.
	 * \n If false, vibration speed values will be set to 0 before commands are sent, so toys will not vibrate. Defaults to true;
	 * @note If value is false, will clear all commands to ensure that any vibrating toys stop vibrating.
	 */
	

	UFUNCTION(BlueprintCallable, Category = "Lovense")
	static void StartEventsAPI(const FString ip, int32 port, FOnEventCode resultCodeCallback, FOnGetLovenseEventToys toysCallback);

	UFUNCTION(BlueprintCallable, Category = "Lovense")
	static void SetConnectEventCallback(const FString ip, FOnLovenseConnectStatusEvent connectCallback);

	UFUNCTION(BlueprintCallable, Category = "Lovense")
	static void SetBatteryEventCallback(const FString ip, FOnLovenseBatteryEvent batteryCallback);

	UFUNCTION(BlueprintCallable, Category = "Lovense")
	static void SetFunctionsValueEventCallback(const FString ip, FOnFunctionsEvent functionsValueCallback);

	UFUNCTION(BlueprintCallable, Category = "Lovense")
	static void SetEveryShakeEventCallback(const FString ip, FOnEveryShakeEvent everyShakeCallback);

	UFUNCTION(BlueprintCallable, Category = "Lovense")
	static void SetButtonEventCallback(const FString ip, FOnButtonEvent buttonCallback);
	
	UFUNCTION(BlueprintCallable, Category = "Lovense")
	static void SetMotionChangedEventCallback(const FString ip, FOnMotionChangedEvent buttonCallback);

	UFUNCTION(BlueprintCallable, Category = "Lovense")
	static void StopEventsAPI(const FString ip);

	UFUNCTION(BlueprintCallable, Category = "Lovense")
	static bool ToyIsSupportThisCommand(const FString toyType,const ELovenseCommandType commandType);

	UFUNCTION(BlueprintCallable, Category = "Lovense")
	static TArray<ELovenseCommandType> GetSupportCommandsByType(const FString toyType);

	UFUNCTION(BlueprintCallable, Category = "Lovense")
	static void InitSupportByDataTable(UDataTable* LovenseSupportDataTable);
	UFUNCTION(BlueprintCallable, Category = "Lovense")
	static void SetErrorEventCallback(FOnLovenseErrorResponse errorCallback);

};
