// (CVN) - Candy Valley Network GmbH

#include "Adapters/LovenseConnectAdapterBase.h"

// EXTERNAL INCLUDES
#include <HttpModule.h>
#include <IHttpRequest.h>
#include <Dom/JsonObject.h>
#include <Dom/JsonValue.h>
#include <Serialization/JsonReader.h>
#include <Serialization/JsonSerializer.h>

// INTERNAL INCLUDES
#include "LovenseManager.h"
#include "LovenseToy.h"
#include "LovenseTypes.h"

// INTERNAL INCLUDES


LovenseConnectAdapterBase::LovenseConnectAdapterBase()
	: ILovenseAdapter() {}

#if WITH_LOVENSE

bool LovenseConnectAdapterBase::SendCommand_Stop(const FString& toyId, FOnLovenseResponse callback) {
	return this->SendCommand(
		FString::Printf(
			TEXT("{\"command\":\"Function\",\"action\":\"Stop\",\"timeSec\":0,\"toy\":\"%s\",\"apiVer\":1}"),
			*toyId
		),callback
	);
}

bool LovenseConnectAdapterBase::SendCommands(TArray<class UELovenseCommands*> commands, const FString& toyId, float timeSec, float loopRunningSec, float loopPauseSec, FOnLovenseResponse callback) {
	bool onlyVibrate = false;
	if (commands.Num() <= 0) {
		onlyVibrate = true;
	}
	FString cmds = FString::Printf(
		TEXT("{\"command\":\"Function\",\"toy\":\"%s\",\"action\":\""),
		*toyId
	);
	bool bHaveVibrate = false, bHaveVibrate1 = false, bHaveVibrate2 = false, bHaveVibrate3 = false;
	for (const UELovenseCommands* command : commands) {
		if (command->GetCommandType() == ELovenseCommandType::VIBRATE) {
			bHaveVibrate = true;
		}
		else if(command->GetCommandType() == ELovenseCommandType::VIBRATE1) {
			bHaveVibrate1 = true;
		}
		else if(command->GetCommandType() == ELovenseCommandType::VIBRATE2) {
			bHaveVibrate2 = true;
		}
		else if(command->GetCommandType() == ELovenseCommandType::VIBRATE3) {
			bHaveVibrate3 = true;
		}
	}
	if (onlyVibrate) {
		cmds.Append("Vibrate:10");
	}
	else {
		for (UELovenseCommands* command : commands)
		{
			if (command->GetCommandType() == ELovenseCommandType::VIBRATE) {
				FString temp = FString::Printf(
					TEXT("Vibrate:%d,"),
					command->GetValue()
				);
				cmds.Append(temp);
			}
			if (command->GetCommandType() == ELovenseCommandType::VIBRATE1 && !bHaveVibrate) {
				FString temp = FString::Printf(
					TEXT("Vibrate1:%d,"),
					command->GetValue()
				);
				cmds.Append(temp);
			}
			if (command->GetCommandType() == ELovenseCommandType::VIBRATE2 && !bHaveVibrate) {
				FString temp = FString::Printf(
					TEXT("Vibrate2:%d,"),
					command->GetValue()
				);
				cmds.Append(temp);
			}
			if (command->GetCommandType() == ELovenseCommandType::VIBRATE3 && !bHaveVibrate) {
				FString temp = FString::Printf(
					TEXT("Vibrate3:%d,"),
					command->GetValue()
				);
				cmds.Append(temp);
			}
			if (command->GetCommandType() == ELovenseCommandType::ROTATE) {
				FString temp = FString::Printf(
					TEXT("Rotate:%d,"),
					command->GetValue()
				);
				cmds.Append(temp);
			}
			if (command->GetCommandType() == ELovenseCommandType::PUMP) {
				int value = 0;
				if (command->GetValue() > 3) {
					value = 3;
				}
				else if (command->GetValue() < 0) {
					value = 0;
				}
				else {
					value = command->GetValue();
				}
				FString temp = FString::Printf(
					TEXT("Pump:%d,"),
					value
				);
				cmds.Append(temp);
			}
			if (command->GetCommandType() == ELovenseCommandType::THRUST) {
				FString temp = FString::Printf(
					TEXT("Thrust:%d,"),
					command->GetValue()
				);
				cmds.Append(temp);
			}
			if (command->GetCommandType() == ELovenseCommandType::SUCTION) {
				FString temp = FString::Printf(
					TEXT("Suction:%d,"),
					command->GetValue()
				);
				cmds.Append(temp);
			}
			if (command->GetCommandType() == ELovenseCommandType::FINGERING) {
				FString temp = FString::Printf(
					TEXT("Fingering:%d,"),
					command->GetValue()
				);
				cmds.Append(temp);
			}
			if (command->GetCommandType() == ELovenseCommandType::DEPTH) {
				FString temp = FString::Printf(
					TEXT("Depth:%d,"),
					command->GetValue()
				);
				cmds.Append(temp);
			}
			if (command->GetCommandType() == ELovenseCommandType::POSITION) {
				UELovenseSolaceProCommands* cmd = static_cast<UELovenseSolaceProCommands*>(command);
				FString temp = FString::Printf(
					TEXT("Stroke:%d-%d,"),
					cmd->GetStrokeLowValue(),
					cmd->GetStrokeHighValue()
				);
				cmds.Append(temp);
			}
			if (command->GetCommandType() == ELovenseCommandType::OSCILLATE) {
				FString temp = FString::Printf(
					TEXT("Oscillate:%d,"),
					command->GetValue()
				);
				cmds.Append(temp);
			}

		}
		cmds.RemoveAt(cmds.Len() - 1, 1);
	}
	cmds.Append("\",");
	if (timeSec < 0) {
		timeSec = 0;
	}
	if (timeSec == static_cast<int>(timeSec)) {
		cmds.Append(
			FString::Printf(
				TEXT("\"timeSec\":%d,"),
				static_cast<int>(timeSec)
			)
		);
	}
	else {
		cmds.Append(
			FString::Printf(
				TEXT("\"timeSec\":%.2f,"),
				timeSec
			)
		);
	}
	
	if (loopRunningSec > 0) {
		if (loopRunningSec == static_cast<int>(loopRunningSec)) {
			cmds.Append(
				FString::Printf(
					TEXT("\"loopRunningSec\":%d,"),
					static_cast<int>(loopRunningSec)
				)
			);
		}
		else {
			cmds.Append(
				FString::Printf(
					TEXT("\"loopRunningSec\":%.2f,"),
					loopRunningSec
				)
			);
		}
		
		if (loopPauseSec == static_cast<int>(loopRunningSec)) {
			cmds.Append(
				FString::Printf(
					TEXT("\"loopPauseSec\":%d,"),
					static_cast<int>(loopRunningSec)
				)
			);
		}
		else {
			cmds.Append(
				FString::Printf(
					TEXT("\"loopPauseSec\":%.2f,"),
					loopPauseSec
				)
			);
		}
		
	}
	//cmds.RemoveAt(cmds.Len() - 1, 1);
	cmds.Append("\"apiVer\":1}");
	return this->SendCommand(cmds,callback);
}

bool LovenseConnectAdapterBase::SendPosition(int position, const FString& toyId, FOnLovenseResponse callback) {
	return this->SendCommand(
		FString::Printf(
			TEXT("{\"command\":\"Position\",\"value\":\"%d\",\"toy\":\"%s\",\"apiVer\":1}"),
			position,
			*toyId
		), callback
	);
}

bool LovenseConnectAdapterBase::SetUpPatternV2(TArray<class UEPositionPatternValue*> positionValues, const FString& toyId, FOnLovenseResponse callback) {
	int count = 0;
	FString actions = TEXT("[");
	//{"ts":0,"pos":10},
	for (const UEPositionPatternValue* pos : positionValues) {
		count++;
		FString temp = FString::Printf(TEXT("{\"ts\":%d,\"pos\":%d},"),
			pos->GetTime(),
			pos->GetPosition()
		);
		actions.Append(
			temp
		);
	}
	if (count == 0) {

	}
	actions.RemoveAt(actions.Len() - 1, 1);
	actions.Append("]");
	return this->SendCommand( 
		FString::Printf(
			TEXT("{\"command\":\"PatternV2\",\"type\":\"Setup\",\"actions\":%s,\"toy\":\"%s\",\"apiVer\":1}"),
			*actions,
			*toyId
		),callback
	);
	return true;
}

bool LovenseConnectAdapterBase::StartPatternV2(const FString& toyId, int startTime, int offsetTime, FOnLovenseResponse callback) {
	return this->SendCommand(
		FString::Printf(
			TEXT("{\"command\":\"PatternV2\",\"type\":\"Play\",\"startTime\":%d,\"offsetTime\":%d,\"toy\":\"%s\",\"apiVer\":1}"),
			startTime,
			offsetTime,
			*toyId
			), callback
	);
}

bool LovenseConnectAdapterBase::StopPatternV2(const FString& toyId, FOnLovenseResponse callback) {
	return this->SendCommand(
		FString::Printf(
			TEXT("{\"command\":\"PatternV2\",\"type\":\"Stop\",\"toy\":\"%s\",\"apiVer\":1}"),
			*toyId
		), callback
	);
}

bool LovenseConnectAdapterBase::GetPatternV2SyncTime(FOnLovenseResponse callback) {
	return this->SendCommand(
		FString::Printf(
			TEXT("{\"command\":\"PatternV2\",\"type\":\"SyncTime\",\"apiVer\":1}")
		), callback
	);
}

bool LovenseConnectAdapterBase::SendCommand_Pattern(const FString& toyId, FLovensePattern& pattern, FOnLovenseResponse callback) {
	TArray<FString> functions = TArray<FString>();
	functions.Reserve(9);
	if (pattern.bVibrate) functions.Add(TEXT("v"));
	if (pattern.bVibrate1) functions.Add(TEXT("v1"));
	if (pattern.bVibrate2) functions.Add(TEXT("v2"));
	if (pattern.bVibrate3) functions.Add(TEXT("v3"));
	if (pattern.bRotate) functions.Add(TEXT("r"));
	if (pattern.bPump) functions.Add(TEXT("p"));
	if (pattern.bThrust) functions.Add(TEXT("t"));
	if (pattern.bSuck) functions.Add(TEXT("s"));
	if (pattern.bFinger) functions.Add(TEXT("f"));
	if (pattern.bDepth) functions.Add(TEXT("d"));
	if (pattern.bOscillate) functions.Add(TEXT("o"));

	FString functionsString = FString::Join(functions, TEXT(","));

	FString timeStr = FString::Printf(TEXT("%f"), pattern.time);

	while (timeStr.EndsWith(TEXT("0")) || timeStr.EndsWith(TEXT(".")))
	{
		if (timeStr.EndsWith(TEXT("."))) {
			timeStr = timeStr.LeftChop(1);
			break;
		}
		timeStr = timeStr.LeftChop(1);
	}

	return this->SendCommand(
		FString::Printf(
			TEXT("{\"command\":\"Pattern\",\"rule\":\"V:1;F:%s;S:%d#\",\"strength\":\"%s\",\"timeSec\":%s,\"toy\":\"%s\",\"apiVer\":1}"),
			*functionsString,
			pattern.interval,
			*pattern.ParsePattern(),
			*timeStr,
			*toyId
		),callback
	);
}

bool LovenseConnectAdapterBase::SendCommand_Preset(const FString& toyId, const FString& name,float timeSec, FOnLovenseResponse callback) {
	FString cmds = FString::Printf(
		TEXT("{\"command\":\"Preset\",\"toy\":\"%s\",\"name\":\"%s\",\"apiVer\":1,\"timeSec\":"),
		*toyId,
		*name
	);
	if (timeSec == static_cast<int>(timeSec)) {
		cmds.Append(
			FString::Printf(
				TEXT("%d}"),
				static_cast<int>(timeSec)
			)
		);
	}
	else {
		cmds.Append(
			FString::Printf(
				TEXT("%.2f}"),
				timeSec
			)
		);
	}
	return this->SendCommand(
		cmds, callback
	);
}




#endif

void LovenseConnectAdapterBase::ParseToyDescription(FLovenseToyDescription& toyDescription, TSharedPtr<FJsonObject> toyObject) {
#if WITH_LOVENSE
	toyDescription.id = toyObject->GetStringField(TEXT("id"));
	toyDescription.name = toyObject->GetStringField(TEXT("name"));
	toyDescription.status = toyObject->GetIntegerField(TEXT("status"));
	TSharedPtr<FJsonValue> batteryValue = toyObject->GetField<EJson::None>(TEXT("battery"));
	// Battery status of -1 means the status of the battery is unknown.
	toyDescription.battery = batteryValue->IsNull() ? -1 : FMath::RoundHalfFromZero(batteryValue->AsNumber());
	toyDescription.firmwareVersion = toyObject->GetStringField(TEXT("fVersion"));
	toyDescription.nickname = toyObject->GetStringField(TEXT("nickName"));
	TSharedPtr<FJsonValue> versionValue = toyObject->GetField<EJson::None>(TEXT("version"));
	FString version = versionValue->IsNull() ? TEXT("") : versionValue->AsString();
	toyDescription.version = version;
	if (toyDescription.name == TEXT("gush") && toyDescription.version == TEXT("2")) {
		toyDescription.name = TEXT("gush2");
	}
#endif
}
