// Oleg Petruny staff

#include "AudioLiner.h"

void UAudioLiner::createAudioLiner(float bpm, TArray<FAudioLine> linesIn, UAudioLiner *&Liner, bool bPrintWorkCountNew, bool bPrintWorkDetailsNew, bool bPrintLinesDetailsNew) {
	Liner = NewObject<UAudioLiner>();
	Liner->bps = 60/bpm;
	Liner->lines = linesIn;
	Liner->bIsCreateOnRunning = true;//GIsRunning;
	Liner->bPrintWorkCount = bPrintWorkCountNew;
	Liner->bPrintWorkDetails = bPrintWorkDetailsNew;
	Liner->bPrintLinesDetails = bPrintLinesDetailsNew;
}

void UAudioLiner::changeAudioLine(TArray<FAudioLineToChange> changes) {
	this->work.Append(changes);
}

void UAudioLiner::Tick(float DeltaTime) {
	GEngine->AddOnScreenDebugMessage(-1, 0.01f, FColor::Yellow, FString::SanitizeFloat(getTimeSinceCreation()));
	//GEngine->AddOnScreenDebugMessage(-1, 0.01f, FColor::Yellow, FString::SanitizeFloat(getTimeSinceCreation()));
	//if (this->bps*0.25f > ((getTimeSinceCreation() - FMath::FloorToInt(getTimeSinceCreation()))) * this->bps || ((getTimeSinceCreation() - FMath::FloorToInt(getTimeSinceCreation()))) * this->bps > this->bps*0.75f) {
	//for (int i = 0; i < this->lines.Num(); i++) {
		//GEngine->AddOnScreenDebugMessage(-1, 0.01f, FColor::Yellow, this->lines[i].components[0]->IsActive() ? "True" : "False");
	//}
	if (this->bPrintWorkCount) {
		GEngine->AddOnScreenDebugMessage(-1, 0.01f, FColor::Yellow, "work count: " + FString::FromInt(this->work.Num()));
	}
	if (this->bPrintLinesDetails) {
		for (int a = 0; a < this->lines.Num(); a++) {
			GEngine->AddOnScreenDebugMessage(a+1, 1.0f, FColor::Yellow, ("line: " + FString::FromInt(a) + " volume: " + FString::SanitizeFloat(this->lines[a].volume) + " id: " + FString::FromInt(this->lines[a].id)));
		}
	}
	for (int i = 0; i < this->work.Num(); i++) {
		if (this->bPrintWorkDetails) {
			GEngine->AddOnScreenDebugMessage(0, (this->work[i].delay != 0) ? 0.01f : 2.0f, FColor::Yellow, "work line: " + FString::FromInt(this->work[i].line) + " id: " + FString::FromInt(this->lines[this->work[i].line].id) + " delay: " + FString::SanitizeFloat(this->work[i].delay));
		}
		
		//GEngine->AddOnScreenDebugMessage(-1, 15.0f, FColor::Yellow, FString::SanitizeFloat((getTimeSinceCreation() <= work[i].line.group.components[work[i].line.group.id]->Sound->Duration) ? getTimeSinceCreation() :
			//getTimeSinceCreation() - (FMath::FloorToInt(getTimeSinceCreation() / work[i].line.group.components[work[i].line.group.id]->Sound->Duration) - 1) * work[i].line.group.components[work[i].line.group.id]->Sound->Duration));
		if (this->work[i].delay <= 0) {
			switch (this->work[i].change) {
				case EAudioLineChange::Play:
				{
					this->lines[this->work[i].line].components[this->lines[this->work[i].line].id]->VolumeMultiplier = this->work[i].volume = this->lines[this->work[i].line].volume;
					if (this->work[i].fullSync) {
						this->lines[this->work[i].line].components[this->lines[this->work[i].line].id]->Play(this->lines[this->work[i].line].components[this->lines[this->work[i].line].id]->Sound->Duration * ((getTimeSinceCreation() / this->lines[this->work[i].line].components[this->lines[this->work[i].line].id]->Sound->Duration) - (FMath::FloorToInt(getTimeSinceCreation() / this->lines[this->work[i].line].components[this->lines[this->work[i].line].id]->Sound->Duration))));
					} else {
						this->lines[this->work[i].line].components[this->lines[this->work[i].line].id]->Play(((getTimeSinceCreation() - FMath::FloorToInt(getTimeSinceCreation()))) * this->bps);
					}
					if (this->lines[this->work[i].line].id > 0) {
						//if (this->lines[this->work[i].line].components[this->lines[this->work[i].line].id - 1]->GetPlayState 0_0) {}
						if (this->lines[this->work[i].line].components[this->lines[this->work[i].line].id - 1]->IsPlaying()) {
							this->lines[this->work[i].line].components[this->lines[this->work[i].line].id - 1]->FadeOut(2, 0);
						}
					}
					if (this->lines[this->work[i].line].id < this->lines[this->work[i].line].components.Num() - 1) {
						this->lines[this->work[i].line].id++;
						this->work.Add(FAudioLineToChange(this->work[i].line, EAudioLineChange::FadeIn, true, this->work[i].volume, 1, this->lines[this->work[i].line].components[this->lines[this->work[i].line].id]->Sound->Duration - this->lines[this->work[i].line].components[this->lines[this->work[i].line].id]->Sound->Duration * ((getTimeSinceCreation() / this->lines[this->work[i].line].components[this->lines[this->work[i].line].id]->Sound->Duration) - (FMath::FloorToInt(getTimeSinceCreation() / this->lines[this->work[i].line].components[this->lines[this->work[i].line].id]->Sound->Duration))) - this->bps));
					}
					this->work.RemoveAt(i, 1, true);
					i--;
					break;
				}
				case EAudioLineChange::Stop:
				{
					if (this->lines[this->work[i].line].id > 0) {
						for (int ii = 0; ii < this->work.Num(); ii++) {
							if (this->work[ii].line == this->work[i].line && ii != i) {
								this->work.RemoveAt(ii, 1, true);
								//break;
							}
						}
					}
					this->lines[this->work[i].line].volume = 0;
					if (this->work[i].fullSync) {
						if (bps * 0.25f > ((getTimeSinceCreation() - FMath::FloorToInt(getTimeSinceCreation()))) * bps || ((getTimeSinceCreation() - FMath::FloorToInt(getTimeSinceCreation()))) * bps > bps * 0.75f) {
							this->lines[this->work[i].line].components[this->lines[this->work[i].line].id]->Stop();
							this->lines[this->work[i].line].id = 0;
							this->work.RemoveAt(i, 1, true);
							i--;
						}
					} else {
						this->lines[this->work[i].line].components[this->lines[this->work[i].line].id]->Stop();
						this->lines[this->work[i].line].id = 0;
						this->work.RemoveAt(i, 1, true);
						i--;
					}
					break;
				}
				case EAudioLineChange::FadeIn:
				{
					this->lines[this->work[i].line].volume = this->work[i].volume;
					if (this->work[i].fullSync) {
						this->lines[this->work[i].line].components[this->lines[this->work[i].line].id]->FadeIn(this->work[i].duration, this->work[i].volume, this->lines[this->work[i].line].components[this->lines[this->work[i].line].id]->Sound->Duration * ((getTimeSinceCreation() / this->lines[this->work[i].line].components[this->lines[this->work[i].line].id]->Sound->Duration) - (FMath::FloorToInt(getTimeSinceCreation() / this->lines[this->work[i].line].components[this->lines[this->work[i].line].id]->Sound->Duration))));
					} else {
						this->lines[this->work[i].line].components[this->lines[this->work[i].line].id]->FadeIn(this->work[i].duration, this->work[i].volume, ((getTimeSinceCreation() - FMath::FloorToInt(getTimeSinceCreation()))) * bps);
					}
					if (this->lines[this->work[i].line].id > 0) {
						//if (this->lines[this->work[i].line].components[this->lines[this->work[i].line].id - 1]->GetPlayState 0_0) {}
						if (this->lines[this->work[i].line].components[this->lines[this->work[i].line].id - 1]->IsPlaying()) {
							this->lines[this->work[i].line].components[this->lines[this->work[i].line].id - 1]->FadeOut(2, 0);
						}
					}
					if (this->lines[this->work[i].line].id < this->lines[this->work[i].line].components.Num() - 1) {
						this->lines[this->work[i].line].id++;
						this->work.Add(FAudioLineToChange(this->work[i].line, EAudioLineChange::FadeIn, true, this->work[i].volume, 1, this->lines[this->work[i].line].components[this->lines[this->work[i].line].id]->Sound->Duration - this->lines[this->work[i].line].components[this->lines[this->work[i].line].id]->Sound->Duration * ((getTimeSinceCreation() / this->lines[this->work[i].line].components[this->lines[this->work[i].line].id]->Sound->Duration) - (FMath::FloorToInt(getTimeSinceCreation() / this->lines[this->work[i].line].components[this->lines[this->work[i].line].id]->Sound->Duration))) - this->bps));
					}
					this->work.RemoveAt(i, 1, true);
					i--;
					break;
				}
				case EAudioLineChange::FadeOut:
				{
					if (this->lines[this->work[i].line].components.Num() > 1) {
						for (int ii = 0; ii < this->work.Num(); ii++) {
							if (this->work[ii].line == this->work[i].line && ii != i) {
								this->work.RemoveAt(ii, 1, true);
								//break;
							}
						}
					}
					this->lines[this->work[i].line].volume = 0;
					if (this->work[i].fullSync) {
						if (this->bps * 0.25f > ((getTimeSinceCreation() - FMath::FloorToInt(getTimeSinceCreation()))) * this->bps || ((getTimeSinceCreation() - FMath::FloorToInt(getTimeSinceCreation()))) * this->bps > this->bps * 0.75f) {
							this->lines[this->work[i].line].components[this->lines[this->work[i].line].id]->FadeOut(this->work[i].duration, this->work[i].volume);
							this->lines[this->work[i].line].id = 0;
							this->work.RemoveAt(i, 1, true);
							i--;
						}
					} else {
						this->lines[this->work[i].line].components[this->lines[this->work[i].line].id]->FadeOut(this->work[i].duration, this->work[i].volume);
						this->lines[this->work[i].line].id = 0;
						this->work.RemoveAt(i, 1, true);
						i--;
					}
					break;
				}
				case EAudioLineChange::Adjust:
				{
					if (this->lines[this->work[i].line].id > 0) {
						for (int ii = 0; ii < this->work.Num(); ii++) {
							if (this->work[ii].line == this->work[i].line && ii != i) {
								this->work[ii].volume = this->work[i].volume;
								//break;
							}
						}
					}
					this->lines[this->work[i].line].volume = this->work[i].volume;
					if (this->work[i].fullSync) {
						if (this->bps * 0.25f > ((getTimeSinceCreation() - FMath::FloorToInt(getTimeSinceCreation()))) * this->bps || ((getTimeSinceCreation() - FMath::FloorToInt(getTimeSinceCreation()))) * this->bps > this->bps * 0.75f) {
							this->lines[this->work[i].line].components[this->lines[this->work[i].line].id]->AdjustVolume(this->work[i].duration, this->work[i].volume);
							this->work.RemoveAt(i, 1, true);
							i--;
						}
					} else {
						this->lines[this->work[i].line].components[this->lines[this->work[i].line].id]->AdjustVolume(this->work[i].duration, this->work[i].volume);
						this->work.RemoveAt(i, 1, true);
						i--;
					}
					break;
				}
				default:
				{
					this->work.RemoveAt(i, 1, true);
					i--;
					break;
				}
			}
		} else {
			this->work[i].delay -= DeltaTime;
		}
	}
	//}
	
}

bool UAudioLiner::IsTickable() const {
	return bIsCreateOnRunning;
}

bool UAudioLiner::IsTickableInEditor() const {
	return bIsCreateOnRunning;
}

bool UAudioLiner::IsTickableWhenPaused() const {
	return bIsCreateOnRunning;
}

TStatId UAudioLiner::GetStatId() const {
	return TStatId();
}

//UWorld *UAudioLiner::GetWorld() const {
	//return GetOuter()->GetWorld();
//}