// Oleg Petruny staff

#pragma once

#include "CoreMinimal.h"
#include "Sound/SoundBase.h"
#include "UObject/Object.h"
#include "UObject/UObjectBase.h"
#include "AudioDevice.h"
#include "Kismet/GameplayStatics.h"
#include "Tickable.h"
#include "AudioLiner.generated.h"

USTRUCT(BlueprintType, Category = "Audio Liner|Structs")
struct FAudioLine {
	GENERATED_USTRUCT_BODY()

	UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Audio Liner|Structs|Audio Line")
		TArray<UAudioComponent*> components;

	int id;
	float volume;

	FAudioLine() {
		components.Empty();
	} 
	FAudioLine(TArray<USoundBase*> audioIn) {
		for (int i = 0; i < audioIn.Num(); i++) {
			UAudioComponent *x = NewObject<UAudioComponent>(); 
			x->SetSound(audioIn[i]);
			x->bAutoDestroy = 0;
			x->bAlwaysPlay = 1;
			x->bIsUISound = 1;
			x->SetTickableWhenPaused(true);
			x->bTickInEditor = 1;
			//x->bAllowConcurrentTick = 1;
			//x->SetComponentTickEnabled(true);
			//x->Tick
			components.Add(x);
			//this->components.Add(UGameplayStatics::CreateSound2D(World, audioIn[i], 1.0f, 1.0f, 0.0f, nullptr, false, false));
		}
	}
};

UENUM(BlueprintType, Category = "Audio Liner|Enums")
enum class EAudioLineChange : uint8 {
	Play = 0 UMETA(DisplayName = "Play"),
	Stop = 1 UMETA(DisplayName = "Stop"),
	FadeIn = 2 UMETA(DisplayName = "FadeIn"),
	FadeOut = 3 UMETA(DisplayName = "FadeOut"),
	Adjust = 4 UMETA(DisplayName = "Adjust")
};

USTRUCT(BlueprintType, Category = "Audio Liner|Structs")
struct FAudioLineToChange {
	GENERATED_USTRUCT_BODY()

	UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Audio Liner|Structs|Audio LineToChange")
		int line;
	UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Audio Liner|Structs|Audio LineToChange")
		EAudioLineChange change;
	UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Audio Liner|Structs|Audio LineToChange")
		bool fullSync;
	UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Audio Liner|Structs|Audio LineToChange")
		float volume;
	UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Audio Liner|Structs|Audio LineToChange")
		float duration;
	UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Audio Liner|Structs|Audio LineToChange")
		float delay; //like IPv4 packet TTL before playing
	
	FAudioLineToChange() {
		line = 0;
		change = EAudioLineChange::Play;
		fullSync = false;
		volume = 0.0f;
		duration = 0.0f;
		delay = 0.0f;
	}
	FAudioLineToChange(int lineIn, EAudioLineChange changeIn, bool fullSyncIn, float volumeIn, float durationIn, float delayIn) {
		line = lineIn;
		change = changeIn;
		fullSync = fullSyncIn;
		volume = volumeIn;
		duration = durationIn;
		delay = delayIn;
	}
};

/*
* Audio synchronizer
* After first line start, will start count time for other lines synchronization
* Work with lines by changeAudioLine()
*/
UCLASS(Blueprintable, BlueprintType, Category = "Audio Liner")
class LOST_EDGE_API UAudioLiner : public UObject, public FTickableGameObject {
	GENERATED_BODY()

private:
	UPROPERTY()
		float creationTime = 0.0f;
	UPROPERTY()
		bool bIsCreateOnRunning = false;
	UPROPERTY()
		float bps;
	UPROPERTY()
		float tolerance = 0.1f;
	UPROPERTY()
		TArray<FAudioLineToChange> work;
	UPROPERTY()
		TArray<FAudioLine> lines;
	UPROPERTY()
		bool bPrintWorkCount;
	UPROPERTY()
		bool bPrintWorkDetails;
	UPROPERTY()
		bool bPrintLinesDetails;

	float getTimeSinceCreation() {
		return 0;//GetWorld()->GetTimeSeconds() - creationTime;
	}
	
public:
	UAudioLiner() {
		bIsCreateOnRunning = GIsRunning;
		//if (GetWorld()->bIsWorldInitialized)
			//creationTime = GetWorld()->GetTimeSeconds();
	}

	virtual void Tick(float DeltaTime) override;
	virtual bool IsTickable() const override;
	virtual bool IsTickableInEditor() const override;
	virtual bool IsTickableWhenPaused() const override;
	virtual TStatId GetStatId() const override;
	//virtual UWorld *GetWorld() const override;

	UFUNCTION(BlueprintCallable, Category = "Audio Liner", meta=(AdvancedDisplay = "2"))
		static void createAudioLiner(float bpm, TArray<FAudioLine> linesIn, UAudioLiner *&Liner, bool bPrintWorkCountNew, bool bPrintWorkDetailsNew, bool bPrintLinesDetailsNew);
	UFUNCTION(BlueprintCallable, Category = "Audio Liner")
		void changeAudioLine(TArray<FAudioLineToChange> changes);

	UFUNCTION(BlueprintPure, Category = "Audio Liner")
	static void createComponents(TArray<USoundBase *> audioIn, TArray<UAudioComponent *> &componentsOut) {
		for (int i = 0; i < audioIn.Num(); i++) {
			UAudioComponent *x = NewObject<UAudioComponent>();
			x->SetSound(audioIn[i]);
			componentsOut.Add(x);
		}
	}
	UFUNCTION(BlueprintPure, Category = "Audio Liner")
	static void createAudioLine(TArray<USoundBase *> audioIn, FAudioLine &audioLineOut) {
		createComponents(audioIn, audioLineOut.components);
	}
	UFUNCTION(BlueprintPure, Category = "Audio Liner")
	static void getLastLineVolume(FAudioLine lineIn, float &volumeOut) {
		volumeOut = lineIn.volume;
	}
};