// Oleg Petruny staff

#pragma once

#include "CoreMinimal.h"
#include "GameFramework/Actor.h"
#include "Sound/SoundBase.h"
#include "UObject/UObjectBase.h"
#include "AudioDevice.h"
#include "Kismet/GameplayStatics.h"
#include "AAudioLiner.generated.h"

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

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

	int id;
	float volume;

	FAAudioLine() {
		components.Empty();
	}
	FAAudioLine(TArray<USoundBase *> audioIn) {
		for (int i = 0; i < audioIn.Num(); i++) {
			UAudioComponent *x = NewObject<UAudioComponent>();
			x->SetSound(audioIn[i]);
			x->bAutoDestroy = false;
			x->bIsUISound = true;
			x->bIsMusic = true;
			this->components.Add(x);
		}
	}
};

UENUM(BlueprintType, Category = "Audio Liner|Enums")
enum class EAAudioLineChange : 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 FAAudioLineToChange {
	GENERATED_USTRUCT_BODY()

		UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Audio Liner|Structs|Audio LineToChange")
		int line;
	UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Audio Liner|Structs|Audio LineToChange")
		EAAudioLineChange 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

	FAAudioLineToChange() {
		line = 0;
		change = EAAudioLineChange::Play;
		fullSync = false;
		volume = 0.0f;
		duration = 0.0f;
		delay = 0.0f;
	}
	FAAudioLineToChange(int lineIn, EAAudioLineChange 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 AAAudioLiner : public AActor {
	GENERATED_BODY()

private:
	/*UPROPERTY(E)
	bool bIsCreateOnRunning = false;*/
	UPROPERTY(EditAnywhere)
		float bps;
	UPROPERTY(EditAnywhere)
		float tolerance = 0.1f;
	UPROPERTY()
		float time;
	UPROPERTY()
		TArray<FAAudioLineToChange> work;
	UPROPERTY(EditAnywhere)
		TArray<FAAudioLine> lines;
	UPROPERTY(EditAnywhere)
		bool bPrintWorkCount;
	UPROPERTY(EditAnywhere)
		bool bPrintWorkDetails;
	UPROPERTY(EditAnywhere)
		bool bPrintLinesDetails;

public:
	AAAudioLiner();
	virtual void Tick(float DeltaTime) override;
	/*AAudioLiner() {
		bIsCreateOnRunning = GIsRunning;
	}

	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;
	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(BlueprintCallable, Category = "Audio Liner")
		void changeAAudioLine(TArray<FAAudioLineToChange> 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(BlueprintCallable, Category = "Audio Liner")
	void addAudioLine(TArray<USoundBase *> audioIn, FAAudioLine &audioLineOut) {
		createComponents(audioIn, audioLineOut.components);
		this->lines.Add(audioLineOut);
	}
	UFUNCTION(BlueprintPure, Category = "Audio Liner")
	static void createAudioLine(TArray<USoundBase *> audioIn, FAAudioLine &audioLineOut) {
		createComponents(audioIn, audioLineOut.components);
	}
	UFUNCTION(BlueprintPure, Category = "Audio Liner")
	static void getLastLineVolume(FAAudioLine lineIn, float &volumeOut) {
		volumeOut = lineIn.volume;
	}
};