for (int i=n-1; i>=0; i--)
	{
		// Move current root to end
		swap(arr[0], arr[i]);

		// call max heapify on the reduced heap
		heapify(arr, i, 0);
	}

// C++ program for implementation of Heap Sort
#include <iostream>

using namespace std;

// To heapify a subtree rooted with node i which is
// an index in arr[]. n is size of heap
void heapify(int arr[], int n, int i)
{
	int largest = i; // Initialize largest as root
	int l = 2*i + 1; // left = 2*i + 1
	int r = 2*i + 2; // right = 2*i + 2

	// If left child is larger than root
	if (l < n && arr[l] > arr[largest])
		largest = l;

	// If right child is larger than largest so far
	if (r < n && arr[r] > arr[largest])
		largest = r;

	// If largest is not root
	if (largest != i)
	{
		swap(arr[i], arr[largest]);

		// Recursively heapify the affected sub-tree
		heapify(arr, n, largest);
	}
}

// main function to do heap sort
void heapSort(int arr[], int n)
{
	// Build heap (rearrange array)
	for (int i = n / 2 - 1; i >= 0; i--)
		heapify(arr, n, i);

	// One by one extract an element from heap

}

/* A utility function to print array of size n */
void printArray(int arr[], int n)
{
	for (int i=0; i<n; ++i)
		cout << arr[i] << " ";
	cout << "\n";
}
int main()
{
	int arr[] = {12, 11, 13, 5, 6, 7};
	int n = sizeof(arr)/sizeof(arr[0]);

	heapSort(arr, n);

	cout << "Sorted array is \n";
	printArray(arr, n);
}


{
    if(!program || !program->isLinked())
        return {};

    auto nprogram = QSharedPointer<QOpenGLShaderProgram>::create();
    for(auto shader : program->shaders())
        nprogram->addShader(shader);

    if(!nprogram->link())
        nprogram.reset();

    return nprogram;
}


{
    if(!program || !program->isLinked())
        return {};

    auto nprogram = QSharedPointer<QOpenGLShaderProgram>::create();
    for(auto shader : program->shaders())
        nprogram->addShader(shader);

    if(!nprogram->link())
        nprogram.reset();

    return nprogram;
}

#include "OpenGLUtils.h"

QSharedPointer<QOpenGLShaderProgram> copyProgram(QSharedPointer<QOpenGLShaderProgram> program)


#include "OpenGLUtils.h"

QSharedPointer<QOpenGLShaderProgram> copyProgram(QSharedPointer<QOpenGLShaderProgram> program) {
    if(!program || !program->isLinked())
        return {};

    auto nprogram = QSharedPoin

    for(auto shader : program->shaders())
        nprogram->addShader(shader);

    if(!nprogram->link())
        nprogram.reset();

    return nprogram;
}


#include "Nodes/DlgNode.h"

#include "Kismet/GameplayStatics.h"
#include "EngineUtils.h"

#include "DlgContext.h"
#include "Logging/DlgLogger.h"
#include "DlgLocalizationHelper.h"
#include "DlgDialogueParticipant.h"
#include "Sound/SoundWave.h"

// Begin UObject interface
void UDlgNode::Serialize(FArchive& Ar)
{
	Super::Serialize(Ar);
	if (Ar.UE4Ver() >= VER_UE4_COOKED_ASSETS_IN_EDITOR_SUPPORT)
	{
		// NOTE: This modifies the Archive
		// DO NOT REMOVE THIS
		const FStripDataFlags StripFlags(Ar);

		// Only in editor, add the graph node
#if WITH_EDITOR
		if (!StripFlags.IsEditorDataStripped())
		{
			Ar << GraphNode;
		}
#endif // WITH_EDITOR
	}
	else
	{
		// Super old version, is this possible?
#if WITH_EDITOR
		Ar << GraphNode;
#endif // WITH_EDITOR
	}
}

void UDlgNode::AddReferencedObjects(UObject* InThis, FReferenceCollector& Collector)
{
	UDlgNode* This = CastChecked<UDlgNode>(InThis);

	// Add the GraphNode to the referenced objects
#if WITH_EDITOR
	Collector.AddReferencedObject(This->GraphNode, This);
#endif

	Super::AddReferencedObjects(InThis, Collector);
}

void UDlgNode::PostLoad()
{
	Super::PostLoad();

	// NOTE: We don't this here but instead we do it in the compile phase
	// Create thew new GUID
}

void UDlgNode::PostInitProperties()
{
	Super::PostInitProperties();

	// Ignore these cases
	if (HasAnyFlags(RF_ClassDefaultObject | RF_NeedLoad))
	{
		return;
	}

	// GUID is set in the dialogue compile phase
}

void UDlgNode::PostDuplicate(bool bDuplicateForPIE)
{
	Super::PostDuplicate(bDuplicateForPIE);

	// Used when duplicating Nodes.
	// We only generate a new GUID is the existing one is valid, otherwise it will be set in the compile phase
	if (HasGUID())
	{
		RegenerateGUID();
	}
}

void UDlgNode::PostEditImport()
{
	Super::PostEditImport();

	// Used when duplicating Nodes.
	// We only generate a new GUID is the existing one is valid, otherwise it will be set in the compile phase
	if (HasGUID())
	{
		RegenerateGUID();
	}
}

#if WITH_EDITOR
void UDlgNode::PostEditChangeProperty(FPropertyChangedEvent& PropertyChangedEvent)
{
	Super::PostEditChangeProperty(PropertyChangedEvent);

	// Signal to the listeners
	OnDialogueNodePropertyChanged.Broadcast(PropertyChangedEvent, BroadcastPropertyEdgeIndexChanged);
	BroadcastPropertyEdgeIndexChanged = INDEX_NONE;
}

void UDlgNode::PostEditChangeChainProperty(struct FPropertyChangedChainEvent& PropertyChangedEvent)
{
	// The Super::PostEditChangeChainProperty will construct a new FPropertyChangedEvent that will only have the Property and the
	// MemberProperty name and it will call the PostEditChangeProperty, so we must get the array index of the Nodes modified from here.
	// If you want to preserve all the change history of the tree you must broadcast the event from here to the children, but be warned
	// that Property and MemberProperty are not set properly.
	BroadcastPropertyEdgeIndexChanged = PropertyChangedEvent.GetArrayIndex(GET_MEMBER_NAME_STRING_CHECKED(UDlgNode, Children));
	Super::PostEditChangeChainProperty(PropertyChangedEvent);
}

#endif //WITH_EDITOR
// End UObject interface

// Begin own function
bool UDlgNode::HandleNodeEnter(UDlgContext& Context, TSet<const UDlgNode*> NodesEnteredWithThisStep)
{
	// Fire all the node enter events
	FireNodeEnterEvents(Context);

	for (FDlgEdge& Edge : Children)
	{
		Edge.RebuildConstructedText(Context, OwnerName);
	}

	return ReevaluateChildren(Context, {});
}

void UDlgNode::FireNodeEnterEvents(UDlgContext& Context)
{
	for (const FDlgEvent& Event : EnterEvents)
	{
		// Get Participant from either event or parent
		UObject* Participant = Context.GetMutableParticipant(Event.ParticipantName);
		if (!IsValid(Participant))
		{
			Participant = Context.GetMutableParticipant(OwnerName);
		}

		Event.Call(Context, TEXT("FireNodeEnterEvents"), Participant);
	}
}

bool UDlgNode::ReevaluateChildren(UDlgContext& Context, TSet<const UDlgNode*> AlreadyEvaluated)
{
	TArray<FDlgEdge>& AvailableOptions = Context.GetMutableOptionsArray();
	TArray<FDlgEdgeData>& AllOptions = Context.GetAllMutableOptionsArray();
	AvailableOptions.Empty();
	AllOptions.Empty();

	for (const FDlgEdge& Edge : Children)
	{
		const bool bSatisfied = Edge.Evaluate(Context, { this });

		if (bSatisfied || Edge.bIncludeInAllOptionListIfUnsatisfied)
		{
			AllOptions.Add(FDlgEdgeData{ bSatisfied, Edge });
		}
		if (bSatisfied)
		{
			AvailableOptions.Add(Edge);
		}
	}

	// no child, but no end node?
	if (AvailableOptions.Num() == 0)
	{
		FDlgLogger::Get().Errorf(
			TEXT("ReevaluateChildren (ReevaluateOptions) - no valid child option for a NODE.\nContext:\n\t%s"),
			*Context.GetContextString()
		);
		return false;
	}

	return true;
}

bool UDlgNode::CheckNodeEnterConditions(const UDlgContext& Context, TSet<const UDlgNode*> AlreadyVisitedNodes) const
{
	if (AlreadyVisitedNodes.Contains(this))
	{
		return true;
	}

	AlreadyVisitedNodes.Add(this);
	if (!FDlgCondition::EvaluateArray(Context, EnterConditions, OwnerName))
	{
		return false;
	}
	if (!bCheckChildrenOnEvaluation)
	{
		return true;
	}

	// Has a valid child?
	return HasAnySatisfiedChild(Context, AlreadyVisitedNodes);
}

bool UDlgNode::HasAnySatisfiedChild(const UDlgContext& Context, TSet<const UDlgNode*> AlreadyVisitedNodes) const
{
	for (const FDlgEdge& Edge : Children)
	{
		// Found at least one valid child
		if (Edge.Evaluate(Context, AlreadyVisitedNodes))
		{
			return true;
		}
	}

	return false;
}

bool UDlgNode::OptionSelected(int32 OptionIndex, UDlgContext& Context)
{
	const TArray<FDlgEdge>& AvailableOptions = Context.GetOptionsArray();
	if (AvailableOptions.IsValidIndex(OptionIndex))
	{
		check(AvailableOptions[OptionIndex].IsValid());
		return Context.EnterNode(AvailableOptions[OptionIndex].TargetIndex, {});
	}

	FDlgLogger::Get().Errorf(
		TEXT("OptionSelected - Failed to choose OptionIndex = %d - it only has %d valid options.\nContext:\n\t%s"),
		OptionIndex, AvailableOptions.Num(), *Context.GetContextString()
	);
	return false;
}

const TArray<int32> UDlgNode::GetNodeOpenChildren_DEPRECATED() const
{
	TArray<int32> OutArray;
	const int32 EdgesNum = Children.Num();
	for (int32 EdgeIndex = 0; EdgeIndex < EdgesNum; EdgeIndex++)
	{
		if (!Children[EdgeIndex].IsValid())
		{
			OutArray.Add(EdgeIndex);
		}
	}

	return OutArray;
}

FDlgEdge* UDlgNode::GetMutableNodeChildForTargetIndex(int32 TargetIndex)
{
	for (FDlgEdge& Edge : Children)
	{
		if (Edge.TargetIndex == TargetIndex)
		{
			return &Edge;
		}
	}

	return nullptr;
}

void UDlgNode::UpdateTextsValuesFromDefaultsAndRemappings(
	const UDlgSystemSettings& Settings, bool bEdges, bool bUpdateGraphNode
)
{
	// We only care about edges here
	if (bEdges)
	{
		const bool bSkipAfterFirstChild = Settings.bSetDefaultEdgeTextOnFirstChildOnly;
		if (Settings.bSetDefaultEdgeTexts)
		{
			const UDlgDialogue* Dialogue = GetDialogue();
			for (FDlgEdge& Edge : Children)
			{
				Edge.UpdateTextValueFromDefaultAndRemapping(*Dialogue, *this, Settings, false);

				// Set only one, kill the rest
				if (bSkipAfterFirstChild)
				{
					break;
				}
			}
		}

		// Update the rest of the texts remapping
		for (FDlgEdge& Edge : Children)
		{
			FDlgLocalizationHelper::UpdateTextFromRemapping(Settings, Edge.GetMutableUnformattedText());
		}
	}

	if (bUpdateGraphNode)
	{
		UpdateGraphNode();
	}
}

void UDlgNode::UpdateTextsNamespacesAndKeys(const UDlgSystemSettings& Settings, bool bEdges, bool bUpdateGraphNode)
{
	if (bEdges)
	{
		UObject* Outer = GetOuter();
		for (FDlgEdge& Edge : Children)
		{
			Edge.UpdateTextsNamespacesAndKeys(Outer, Settings);
		}
	}

	if (bUpdateGraphNode)
	{
		UpdateGraphNode();
	}
}

void UDlgNode::RebuildTextArguments(bool bEdges, bool bUpdateGraphNode)
{
	if (bEdges)
	{
		for (FDlgEdge& Edge : Children)
		{
			Edge.RebuildTextArguments();
		}
	}

	if (bUpdateGraphNode)
	{
		UpdateGraphNode();
	}
}

{
#if WITH_EDITOR
	UDlgDialogue::GetDialogueEditorAccess()->UpdateGraphNodeEdges(GraphNode);
#endif // WITH_EDITOR
}

void UDlgNode::GetAssociatedParticipants(TArray<FName>& OutArray) const
{
	if (OwnerName != NAME_None)
	{
		OutArray.AddUnique(OwnerName);
	}
}

UDlgDialogue* UDlgNode::GetDialogue() const
{
	return CastChecked<UDlgDialogue>(GetOuter());
}

USoundWave* UDlgNode::GetNodeVoiceSoundWave() const
{
	return Cast<USoundWave>(GetNodeVoiceSoundBase());
}

// End own functions


{
	UDlgNode* This = CastChecked<UDlgNode>(InThis);

	// Add the GraphNode to the referenced objects
#if WITH_EDITOR
	Collector.AddReferencedObject(This->GraphNode, This);
#endif

	Super::AddReferencedObjects(InThis, Collector);
}

#include "Nodes/DlgNode.h"

#include "Kismet/GameplayStatics.h"
#include "EngineUtils.h"

#include "DlgContext.h"
#include "Logging/DlgLogger.h"
#include "DlgLocalizationHelper.h"
#include "DlgDialogueParticipant.h"
#include "Sound/SoundWave.h"

// Begin UObject interface
void UDlgNode::Serialize(FArchive& Ar)
{
	Super::Serialize(Ar);
	if (Ar.UE4Ver() >= VER_UE4_COOKED_ASSETS_IN_EDITOR_SUPPORT)
	{
		// NOTE: This modifies the Archive
		// DO NOT REMOVE THIS
		const FStripDataFlags StripFlags(Ar);

		// Only in editor, add the graph node
#if WITH_EDITOR
		if (!StripFlags.IsEditorDataStripped())
		{
			Ar << GraphNode;
		}
#endif // WITH_EDITOR
	}
	else
	{
		// Super old version, is this possible?
#if WITH_EDITOR
		Ar << GraphNode;
#endif // WITH_EDITOR
	}
}

void UDlgNode::AddReferencedObjects(UObject* InThis, FReferenceCollector& Collector)

void UDlgNode::PostLoad()
{
	Super::PostLoad();

	// NOTE: We don't this here but instead we do it in the compile phase
	// Create thew new GUID
}

void UDlgNode::PostInitProperties()
{
	Super::PostInitProperties();

	// Ignore these cases
	if (HasAnyFlags(RF_ClassDefaultObject | RF_NeedLoad))
	{
		return;
	}

	// GUID is set in the dialogue compile phase
}

void UDlgNode::PostDuplicate(bool bDuplicateForPIE)
{
	Super::PostDuplicate(bDuplicateForPIE);

	// Used when duplicating Nodes.
	// We only generate a new GUID is the existing one is valid, otherwise it will be set in the compile phase
	if (HasGUID())
	{
		RegenerateGUID();
	}
}

void UDlgNode::PostEditImport()
{
	Super::PostEditImport();

	// Used when duplicating Nodes.
	// We only generate a new GUID is the existing one is valid, otherwise it will be set in the compile phase
	if (HasGUID())
	{
		RegenerateGUID();
	}
}

#if WITH_EDITOR
void UDlgNode::PostEditChangeProperty(FPropertyChangedEvent& PropertyChangedEvent)
{
	Super::PostEditChangeProperty(PropertyChangedEvent);

	// Signal to the listeners
	OnDialogueNodePropertyChanged.Broadcast(PropertyChangedEvent, BroadcastPropertyEdgeIndexChanged);
	BroadcastPropertyEdgeIndexChanged = INDEX_NONE;
}

void UDlgNode::PostEditChangeChainProperty(struct FPropertyChangedChainEvent& PropertyChangedEvent)
{
	// The Super::PostEditChangeChainProperty will construct a new FPropertyChangedEvent that will only have the Property and the
	// MemberProperty name and it will call the PostEditChangeProperty, so we must get the array index of the Nodes modified from here.
	// If you want to preserve all the change history of the tree you must broadcast the event from here to the children, but be warned
	// that Property and MemberProperty are not set properly.
	BroadcastPropertyEdgeIndexChanged = PropertyChangedEvent.GetArrayIndex(GET_MEMBER_NAME_STRING_CHECKED(UDlgNode, Children));
	Super::PostEditChangeChainProperty(PropertyChangedEvent);
}

#endif //WITH_EDITOR
// End UObject interface

// Begin own function
bool UDlgNode::HandleNodeEnter(UDlgContext& Context, TSet<const UDlgNode*> NodesEnteredWithThisStep)
{
	// Fire all the node enter events
	FireNodeEnterEvents(Context);

	for (FDlgEdge& Edge : Children)
	{
		Edge.RebuildConstructedText(Context, OwnerName);
	}

	return ReevaluateChildren(Context, {});
}

void UDlgNode::FireNodeEnterEvents(UDlgContext& Context)
{
	for (const FDlgEvent& Event : EnterEvents)
	{
		// Get Participant from either event or parent
		UObject* Participant = Context.GetMutableParticipant(Event.ParticipantName);
		if (!IsValid(Participant))
		{
			Participant = Context.GetMutableParticipant(OwnerName);
		}

		Event.Call(Context, TEXT("FireNodeEnterEvents"), Participant);
	}
}

bool UDlgNode::ReevaluateChildren(UDlgContext& Context, TSet<const UDlgNode*> AlreadyEvaluated)
{
	TArray<FDlgEdge>& AvailableOptions = Context.GetMutableOptionsArray();
	TArray<FDlgEdgeData>& AllOptions = Context.GetAllMutableOptionsArray();
	AvailableOptions.Empty();
	AllOptions.Empty();

	for (const FDlgEdge& Edge : Children)
	{
		const bool bSatisfied = Edge.Evaluate(Context, { this });

		if (bSatisfied || Edge.bIncludeInAllOptionListIfUnsatisfied)
		{
			AllOptions.Add(FDlgEdgeData{ bSatisfied, Edge });
		}
		if (bSatisfied)
		{
			AvailableOptions.Add(Edge);
		}
	}

	// no child, but no end node?
	if (AvailableOptions.Num() == 0)
	{
		FDlgLogger::Get().Errorf(
			TEXT("ReevaluateChildren (ReevaluateOptions) - no valid child option for a NODE.\nContext:\n\t%s"),
			*Context.GetContextString()
		);
		return false;
	}

	return true;
}

bool UDlgNode::CheckNodeEnterConditions(const UDlgContext& Context, TSet<const UDlgNode*> AlreadyVisitedNodes) const
{
	if (AlreadyVisitedNodes.Contains(this))
	{
		return true;
	}

	AlreadyVisitedNodes.Add(this);
	if (!FDlgCondition::EvaluateArray(Context, EnterConditions, OwnerName))
	{
		return false;
	}
	if (!bCheckChildrenOnEvaluation)
	{
		return true;
	}

	// Has a valid child?
	return HasAnySatisfiedChild(Context, AlreadyVisitedNodes);
}

bool UDlgNode::HasAnySatisfiedChild(const UDlgContext& Context, TSet<const UDlgNode*> AlreadyVisitedNodes) const
{
	for (const FDlgEdge& Edge : Children)
	{
		// Found at least one valid child
		if (Edge.Evaluate(Context, AlreadyVisitedNodes))
		{
			return true;
		}
	}

	return false;
}

bool UDlgNode::OptionSelected(int32 OptionIndex, UDlgContext& Context)
{
	const TArray<FDlgEdge>& AvailableOptions = Context.GetOptionsArray();
	if (AvailableOptions.IsValidIndex(OptionIndex))
	{
		check(AvailableOptions[OptionIndex].IsValid());
		return Context.EnterNode(AvailableOptions[OptionIndex].TargetIndex, {});
	}

	FDlgLogger::Get().Errorf(
		TEXT("OptionSelected - Failed to choose OptionIndex = %d - it only has %d valid options.\nContext:\n\t%s"),
		OptionIndex, AvailableOptions.Num(), *Context.GetContextString()
	);
	return false;
}

const TArray<int32> UDlgNode::GetNodeOpenChildren_DEPRECATED() const
{
	TArray<int32> OutArray;
	const int32 EdgesNum = Children.Num();
	for (int32 EdgeIndex = 0; EdgeIndex < EdgesNum; EdgeIndex++)
	{
		if (!Children[EdgeIndex].IsValid())
		{
			OutArray.Add(EdgeIndex);
		}
	}

	return OutArray;
}

FDlgEdge* UDlgNode::GetMutableNodeChildForTargetIndex(int32 TargetIndex)
{
	for (FDlgEdge& Edge : Children)
	{
		if (Edge.TargetIndex == TargetIndex)
		{
			return &Edge;
		}
	}

	return nullptr;
}

void UDlgNode::UpdateTextsValuesFromDefaultsAndRemappings(
	const UDlgSystemSettings& Settings, bool bEdges, bool bUpdateGraphNode
)
{
	// We only care about edges here
	if (bEdges)
	{
		const bool bSkipAfterFirstChild = Settings.bSetDefaultEdgeTextOnFirstChildOnly;
		if (Settings.bSetDefaultEdgeTexts)
		{
			const UDlgDialogue* Dialogue = GetDialogue();
			for (FDlgEdge& Edge : Children)
			{
				Edge.UpdateTextValueFromDefaultAndRemapping(*Dialogue, *this, Settings, false);

				// Set only one, kill the rest
				if (bSkipAfterFirstChild)
				{
					break;
				}
			}
		}

		// Update the rest of the texts remapping
		for (FDlgEdge& Edge : Children)
		{
			FDlgLocalizationHelper::UpdateTextFromRemapping(Settings, Edge.GetMutableUnformattedText());
		}
	}

	if (bUpdateGraphNode)
	{
		UpdateGraphNode();
	}
}

void UDlgNode::UpdateTextsNamespacesAndKeys(const UDlgSystemSettings& Settings, bool bEdges, bool bUpdateGraphNode)
{
	if (bEdges)
	{
		UObject* Outer = GetOuter();
		for (FDlgEdge& Edge : Children)
		{
			Edge.UpdateTextsNamespacesAndKeys(Outer, Settings);
		}
	}

	if (bUpdateGraphNode)
	{
		UpdateGraphNode();
	}
}

void UDlgNode::RebuildTextArguments(bool bEdges, bool bUpdateGraphNode)
{
	if (bEdges)
	{
		for (FDlgEdge& Edge : Children)
		{
			Edge.RebuildTextArguments();
		}
	}

	if (bUpdateGraphNode)
	{
		UpdateGraphNode();
	}
}

void UDlgNode::UpdateGraphNode()
{
#if WITH_EDITOR
	UDlgDialogue::GetDialogueEditorAccess()->UpdateGraphNodeEdges(GraphNode);
#endif // WITH_EDITOR
}

void UDlgNode::GetAssociatedParticipants(TArray<FName>& OutArray) const
{
	if (OwnerName != NAME_None)
	{
		OutArray.AddUnique(OwnerName);
	}
}

UDlgDialogue* UDlgNode::GetDialogue() const
{
	return CastChecked<UDlgDialogue>(GetOuter());
}

USoundWave* UDlgNode::GetNodeVoiceSoundWave() const
{
	return Cast<USoundWave>(GetNodeVoiceSoundBase());
}

// End own functions


#include "Nodes/DlgNode.h"

#include "Kismet/GameplayStatics.h"
#include "EngineUtils.h"

#include "DlgContext.h"
#include "Logging/DlgLogger.h"
#include "DlgLocalizationHelper.h"
#include "DlgDialogueParticipant.h"
#include "Sound/SoundWave.h"

// Begin UObject interface
void UDlgNode::Serialize(FArchive& Ar)
{
	Super::Serialize(Ar);
	if (Ar.UE4Ver() >= VER_UE4_COOKED_ASSETS_IN_EDITOR_SUPPORT)
	{
		// NOTE: This modifies the Archive
		// DO NOT REMOVE THIS
		const FStripDataFlags StripFlags(Ar);

		// Only in editor, add the graph node
#if WITH_EDITOR
		if (!StripFlags.IsEditorDataStripped())
		{
			Ar << GraphNode;
		}
#endif // WITH_EDITOR
	}
	else
	{
		// Super old version, is this possible?
#if WITH_EDITOR
		Ar << GraphNode;
#endif // WITH_EDITOR
	}
}

void UDlgNode::AddReferencedObjects(UObject* InThis, FReferenceCollector& Collector)
{
	UDlgNode* This = CastChecked<UDlgNode>(InThis);

	// Add the GraphNode to the referenced objects
#if WITH_EDITOR
	Collector.AddReferencedObject(This->GraphNode, This);
#endif

	Super::AddReferencedObjects(InThis, Collector);
}

void UDlgNode::PostLoad()
{
	Super::PostLoad();

	// NOTE: We don't this here but instead we do it in the compile phase
	// Create thew new GUID
}

void UDlgNode::PostInitProperties()
{
	Super::PostInitProperties();

	// Ignore these cases
	if (HasAnyFlags(RF_ClassDefaultObject | RF_NeedLoad))
	{
		return;
	}

	// GUID is set in the dialogue compile phase
}

void UDlgNode::PostDuplicate(bool bDuplicateForPIE)
{
	Super::PostDuplicate(bDuplicateForPIE);

	// Used when duplicating Nodes.
	// We only generate a new GUID is the existing one is valid, otherwise it will be set in the compile phase
	if (HasGUID())
	{
		RegenerateGUID();
	}
}

void UDlgNode::PostEditImport()
{
	Super::PostEditImport();

	// Used when duplicating Nodes.
	// We only generate a new GUID is the existing one is valid, otherwise it will be set in the compile phase
	if (HasGUID())
	{
		RegenerateGUID();
	}
}

#if WITH_EDITOR
void UDlgNode::PostEditChangeProperty(FPropertyChangedEvent& PropertyChangedEvent)
{
	Super::PostEditChangeProperty(PropertyChangedEvent);

	// Signal to the listeners
	OnDialogueNodePropertyChanged.Broadcast(PropertyChangedEvent, BroadcastPropertyEdgeIndexChanged);
	BroadcastPropertyEdgeIndexChanged = INDEX_NONE;
}

void UDlgNode::PostEditChangeChainProperty(struct FPropertyChangedChainEvent& PropertyChangedEvent)
{
	// The Super::PostEditChangeChainProperty will construct a new FPropertyChangedEvent that will only have the Property and the
	// MemberProperty name and it will call the PostEditChangeProperty, so we must get the array index of the Nodes modified from here.
	// If you want to preserve all the change history of the tree you must broadcast the event from here to the children, but be warned
	// that Property and MemberProperty are not set properly.
	BroadcastPropertyEdgeIndexChanged = PropertyChangedEvent.GetArrayIndex(GET_MEMBER_NAME_STRING_CHECKED(UDlgNode, Children));
	Super::PostEditChangeChainProperty(PropertyChangedEvent);
}

#endif //WITH_EDITOR
// End UObject interface

// Begin own function
bool UDlgNode::HandleNodeEnter(UDlgContext& Context, TSet<const UDlgNode*> NodesEnteredWithThisStep)
{
	// Fire all the node enter events
	FireNodeEnterEvents(Context);

	for (FDlgEdge& Edge : Children)
	{
		Edge.RebuildConstructedText(Context, OwnerName);
	}

	return ReevaluateChildren(Context, {});
}

void UDlgNode::FireNodeEnterEvents(UDlgContext& Context)
{
	for (const FDlgEvent& Event : EnterEvents)
	{
		// Get Participant from either event or parent
		UObject* Participant = Context.GetMutableParticipant(Event.ParticipantName);
		if (!IsValid(Participant))
		{
			Participant = Context.GetMutableParticipant(OwnerName);
		}

		Event.Call(Context, TEXT("FireNodeEnterEvents"), Participant);
	}
}

bool UDlgNode::ReevaluateChildren(UDlgContext& Context, TSet<const UDlgNode*> AlreadyEvaluated)
{
	TArray<FDlgEdge>& AvailableOptions = Context.GetMutableOptionsArray();
	TArray<FDlgEdgeData>& AllOptions = Context.GetAllMutableOptionsArray();
	AvailableOptions.Empty();
	AllOptions.Empty();

	for (const FDlgEdge& Edge : Children)
	{
		const bool bSatisfied = Edge.Evaluate(Context, { this });

		if (bSatisfied || Edge.bIncludeInAllOptionListIfUnsatisfied)
		{
			AllOptions.Add(FDlgEdgeData{ bSatisfied, Edge });
		}
		if (bSatisfied)
		{
			AvailableOptions.Add(Edge);
		}
	}

	// no child, but no end node?
	if (AvailableOptions.Num() == 0)
	{
		FDlgLogger::Get().Errorf(
			TEXT("ReevaluateChildren (ReevaluateOptions) - no valid child option for a NODE.\nContext:\n\t%s"),
			*Context.GetContextString()
		);
		return false;
	}

	return true;
}

bool UDlgNode::CheckNodeEnterConditions(const UDlgContext& Context, TSet<const UDlgNode*> AlreadyVisitedNodes) const
{
	if (AlreadyVisitedNodes.Contains(this))
	{
		return true;
	}

	AlreadyVisitedNodes.Add(this);
	if (!FDlgCondition::EvaluateArray(Context, EnterConditions, OwnerName))
	{
		return false;
	}
	if (!bCheckChildrenOnEvaluation)
	{
		return true;
	}

	// Has a valid child?
	return HasAnySatisfiedChild(Context, AlreadyVisitedNodes);
}

bool UDlgNode::HasAnySatisfiedChild(const UDlgContext& Context, TSet<const UDlgNode*> AlreadyVisitedNodes) const
{
	for (const FDlgEdge& Edge : Children)
	{
		// Found at least one valid child
		if (Edge.Evaluate(Context, AlreadyVisitedNodes))
		{
			return true;
		}
	}

}

bool UDlgNode::OptionSelected(int32 OptionIndex, UDlgContext& Context)
{
	const TArray<FDlgEdge>& AvailableOptions = Context.GetOptionsArray();
	if (AvailableOptions.IsValidIndex(OptionIndex))
	{
		check(AvailableOptions[OptionIndex].IsValid());
		return Context.EnterNode(AvailableOptions[OptionIndex].TargetIndex, {});
	}

	FDlgLogger::Get().Errorf(
		TEXT("OptionSelected - Failed to choose OptionIndex = %d - it only has %d valid options.\nContext:\n\t%s"),
		OptionIndex, AvailableOptions.Num(), *Context.GetContextString()
	);
	return false;
}

const TArray<int32> UDlgNode::GetNodeOpenChildren_DEPRECATED() const
{
	TArray<int32> OutArray;
	const int32 EdgesNum = Children.Num();
	for (int32 EdgeIndex = 0; EdgeIndex < EdgesNum; EdgeIndex++)
	{
		if (!Children[EdgeIndex].IsValid())
		{
			OutArray.Add(EdgeIndex);
		}
	}

	return OutArray;
}

FDlgEdge* UDlgNode::GetMutableNodeChildForTargetIndex(int32 TargetIndex)
{
	for (FDlgEdge& Edge : Children)
	{
		if (Edge.TargetIndex == TargetIndex)
		{
			return &Edge;
		}
	}

	return nullptr;
}

void UDlgNode::UpdateTextsValuesFromDefaultsAndRemappings(
	const UDlgSystemSettings& Settings, bool bEdges, bool bUpdateGraphNode
)
{
	// We only care about edges here
	if (bEdges)
	{
		const bool bSkipAfterFirstChild = Settings.bSetDefaultEdgeTextOnFirstChildOnly;
		if (Settings.bSetDefaultEdgeTexts)
		{
			const UDlgDialogue* Dialogue = GetDialogue();
			for (FDlgEdge& Edge : Children)
			{
				Edge.UpdateTextValueFromDefaultAndRemapping(*Dialogue, *this, Settings, false);

				// Set only one, kill the rest
				if (bSkipAfterFirstChild)
				{
					break;
				}
			}
		}

		// Update the rest of the texts remapping
		for (FDlgEdge& Edge : Children)
		{
			FDlgLocalizationHelper::UpdateTextFromRemapping(Settings, Edge.GetMutableUnformattedText());
		}
	}

	if (bUpdateGraphNode)
	{
		UpdateGraphNode();
	}
}

void UDlgNode::UpdateTextsNamespacesAndKeys(const UDlgSystemSettings& Settings, bool bEdges, bool bUpdateGraphNode)
{
	if (bEdges)
	{
		UObject* Outer = GetOuter();
		for (FDlgEdge& Edge : Children)
		{
			Edge.UpdateTextsNamespacesAndKeys(Outer, Settings);
		}
	}

	if (bUpdateGraphNode)
	{
		UpdateGraphNode();
	}
}

void UDlgNode::RebuildTextArguments(bool bEdges, bool bUpdateGraphNode)
{
	if (bEdges)
	{
		for (FDlgEdge& Edge : Children)
		{
			Edge.RebuildTextArguments();
		}
	}

	if (bUpdateGraphNode)
	{
		UpdateGraphNode();
	}
}

void UDlgNode::UpdateGraphNode()
{
#if WITH_EDITOR
	UDlgDialogue::GetDialogueEditorAccess()->UpdateGraphNodeEdges(GraphNode);
#endif // WITH_EDITOR
}

void UDlgNode::GetAssociatedParticipants(TArray<FName>& OutArray) const
{
	if (OwnerName != NAME_None)
	{
		OutArray.AddUnique(OwnerName);
	}
}

UDlgDialogue* UDlgNode::GetDialogue() const
{
	return CastChecked<UDlgDialogue>(GetOuter());
}

USoundWave* UDlgNode::GetNodeVoiceSoundWave() const
{
	return Cast<USoundWave>(GetNodeVoiceSoundBase());
}

// End own functions


bool UDlgNode::HandleNodeEnter(UDlgContext& Context, TSet<const UDlgNode*> NodesEnteredWithThisStep)
{
	// Fire all the node enter events
	FireNodeEnterEvents(Context);

	for (FDlgEdge& Edge : Children)
	{
		Edge.RebuildConstructedText(Context, OwnerName);
	}

	return ReevaluateChildren(Context, {});
}

#include "Nodes/DlgNode.h"

#include "Kismet/GameplayStatics.h"
#include "EngineUtils.h"

#include "DlgContext.h"
#include "Logging/DlgLogger.h"
#include "DlgLocalizationHelper.h"
#include "DlgDialogueParticipant.h"
#include "Sound/SoundWave.h"

// Begin UObject interface
void UDlgNode::Serialize(FArchive& Ar)
{
	Super::Serialize(Ar);
	if (Ar.UE4Ver() >= VER_UE4_COOKED_ASSETS_IN_EDITOR_SUPPORT)
	{
		// NOTE: This modifies the Archive
		// DO NOT REMOVE THIS
		const FStripDataFlags StripFlags(Ar);

		// Only in editor, add the graph node
#if WITH_EDITOR
		if (!StripFlags.IsEditorDataStripped())
		{
			Ar << GraphNode;
		}
#endif // WITH_EDITOR
	}
	else
	{
		// Super old version, is this possible?
#if WITH_EDITOR
		Ar << GraphNode;
#endif // WITH_EDITOR
	}
}

void UDlgNode::AddReferencedObjects(UObject* InThis, FReferenceCollector& Collector)
{
	UDlgNode* This = CastChecked<UDlgNode>(InThis);

	// Add the GraphNode to the referenced objects
#if WITH_EDITOR
	Collector.AddReferencedObject(This->GraphNode, This);
#endif

	Super::AddReferencedObjects(InThis, Collector);
}

void UDlgNode::PostLoad()
{
	Super::PostLoad();

	// NOTE: We don't this here but instead we do it in the compile phase
	// Create thew new GUID
}

void UDlgNode::PostInitProperties()
{
	Super::PostInitProperties();

	// Ignore these cases
	if (HasAnyFlags(RF_ClassDefaultObject | RF_NeedLoad))
	{
		return;
	}

	// GUID is set in the dialogue compile phase
}

void UDlgNode::PostDuplicate(bool bDuplicateForPIE)
{
	Super::PostDuplicate(bDuplicateForPIE);

	// Used when duplicating Nodes.
	// We only generate a new GUID is the existing one is valid, otherwise it will be set in the compile phase
	if (HasGUID())
	{
		RegenerateGUID();
	}
}

void UDlgNode::PostEditImport()
{
	Super::PostEditImport();

	// Used when duplicating Nodes.
	// We only generate a new GUID is the existing one is valid, otherwise it will be set in the compile phase
	if (HasGUID())
	{
		RegenerateGUID();
	}
}

#if WITH_EDITOR
void UDlgNode::PostEditChangeProperty(FPropertyChangedEvent& PropertyChangedEvent)
{
	Super::PostEditChangeProperty(PropertyChangedEvent);

	// Signal to the listeners
	OnDialogueNodePropertyChanged.Broadcast(PropertyChangedEvent, BroadcastPropertyEdgeIndexChanged);
	BroadcastPropertyEdgeIndexChanged = INDEX_NONE;
}

void UDlgNode::PostEditChangeChainProperty(struct FPropertyChangedChainEvent& PropertyChangedEvent)
{
	// The Super::PostEditChangeChainProperty will construct a new FPropertyChangedEvent that will only have the Property and the
	// MemberProperty name and it will call the PostEditChangeProperty, so we must get the array index of the Nodes modified from here.
	// If you want to preserve all the change history of the tree you must broadcast the event from here to the children, but be warned
	// that Property and MemberProperty are not set properly.
	BroadcastPropertyEdgeIndexChanged = PropertyChangedEvent.GetArrayIndex(GET_MEMBER_NAME_STRING_CHECKED(UDlgNode, Children));
	Super::PostEditChangeChainProperty(PropertyChangedEvent);
}

#endif //WITH_EDITOR
// End UObject interface

// Begin own function

void UDlgNode::FireNodeEnterEvents(UDlgContext& Context)
{
	for (const FDlgEvent& Event : EnterEvents)
	{
		// Get Participant from either event or parent
		UObject* Participant = Context.GetMutableParticipant(Event.ParticipantName);
		if (!IsValid(Participant))
		{
			Participant = Context.GetMutableParticipant(OwnerName);
		}

		Event.Call(Context, TEXT("FireNodeEnterEvents"), Participant);
	}
}

bool UDlgNode::ReevaluateChildren(UDlgContext& Context, TSet<const UDlgNode*> AlreadyEvaluated)
{
	TArray<FDlgEdge>& AvailableOptions = Context.GetMutableOptionsArray();
	TArray<FDlgEdgeData>& AllOptions = Context.GetAllMutableOptionsArray();
	AvailableOptions.Empty();
	AllOptions.Empty();

	for (const FDlgEdge& Edge : Children)
	{
		const bool bSatisfied = Edge.Evaluate(Context, { this });

		if (bSatisfied || Edge.bIncludeInAllOptionListIfUnsatisfied)
		{
			AllOptions.Add(FDlgEdgeData{ bSatisfied, Edge });
		}
		if (bSatisfied)
		{
			AvailableOptions.Add(Edge);
		}
	}

	// no child, but no end node?
	if (AvailableOptions.Num() == 0)
	{
		FDlgLogger::Get().Errorf(
			TEXT("ReevaluateChildren (ReevaluateOptions) - no valid child option for a NODE.\nContext:\n\t%s"),
			*Context.GetContextString()
		);
		return false;
	}

	return true;
}

bool UDlgNode::CheckNodeEnterConditions(const UDlgContext& Context, TSet<const UDlgNode*> AlreadyVisitedNodes) const
{
	if (AlreadyVisitedNodes.Contains(this))
	{
		return true;
	}

	AlreadyVisitedNodes.Add(this);
	if (!FDlgCondition::EvaluateArray(Context, EnterConditions, OwnerName))
	{
		return false;
	}
	if (!bCheckChildrenOnEvaluation)
	{
		return true;
	}

	// Has a valid child?
	return HasAnySatisfiedChild(Context, AlreadyVisitedNodes);
}

bool UDlgNode::HasAnySatisfiedChild(const UDlgContext& Context, TSet<const UDlgNode*> AlreadyVisitedNodes) const
{
	for (const FDlgEdge& Edge : Children)
	{
		// Found at least one valid child
		if (Edge.Evaluate(Context, AlreadyVisitedNodes))
		{
			return true;
		}
	}

	return false;
}

bool UDlgNode::OptionSelected(int32 OptionIndex, UDlgContext& Context)
{
	const TArray<FDlgEdge>& AvailableOptions = Context.GetOptionsArray();
	if (AvailableOptions.IsValidIndex(OptionIndex))
	{
		check(AvailableOptions[OptionIndex].IsValid());
		return Context.EnterNode(AvailableOptions[OptionIndex].TargetIndex, {});
	}

	FDlgLogger::Get().Errorf(
		TEXT("OptionSelected - Failed to choose OptionIndex = %d - it only has %d valid options.\nContext:\n\t%s"),
		OptionIndex, AvailableOptions.Num(), *Context.GetContextString()
	);
	return false;
}

const TArray<int32> UDlgNode::GetNodeOpenChildren_DEPRECATED() const
{
	TArray<int32> OutArray;
	const int32 EdgesNum = Children.Num();
	for (int32 EdgeIndex = 0; EdgeIndex < EdgesNum; EdgeIndex++)
	{
		if (!Children[EdgeIndex].IsValid())
		{
			OutArray.Add(EdgeIndex);
		}
	}

	return OutArray;
}

FDlgEdge* UDlgNode::GetMutableNodeChildForTargetIndex(int32 TargetIndex)
{
	for (FDlgEdge& Edge : Children)
	{
		if (Edge.TargetIndex == TargetIndex)
		{
			return &Edge;
		}
	}

	return nullptr;
}

void UDlgNode::UpdateTextsValuesFromDefaultsAndRemappings(
	const UDlgSystemSettings& Settings, bool bEdges, bool bUpdateGraphNode
)
{
	// We only care about edges here
	if (bEdges)
	{
		const bool bSkipAfterFirstChild = Settings.bSetDefaultEdgeTextOnFirstChildOnly;
		if (Settings.bSetDefaultEdgeTexts)
		{
			const UDlgDialogue* Dialogue = GetDialogue();
			for (FDlgEdge& Edge : Children)
			{
				Edge.UpdateTextValueFromDefaultAndRemapping(*Dialogue, *this, Settings, false);

				// Set only one, kill the rest
				if (bSkipAfterFirstChild)
				{
					break;
				}
			}
		}

		// Update the rest of the texts remapping
		for (FDlgEdge& Edge : Children)
		{
			FDlgLocalizationHelper::UpdateTextFromRemapping(Settings, Edge.GetMutableUnformattedText());
		}
	}

	if (bUpdateGraphNode)
	{
		UpdateGraphNode();
	}
}

void UDlgNode::UpdateTextsNamespacesAndKeys(const UDlgSystemSettings& Settings, bool bEdges, bool bUpdateGraphNode)
{
	if (bEdges)
	{
		UObject* Outer = GetOuter();
		for (FDlgEdge& Edge : Children)
		{
			Edge.UpdateTextsNamespacesAndKeys(Outer, Settings);
		}
	}

	if (bUpdateGraphNode)
	{
		UpdateGraphNode();
	}
}

void UDlgNode::RebuildTextArguments(bool bEdges, bool bUpdateGraphNode)
{
	if (bEdges)
	{
		for (FDlgEdge& Edge : Children)
		{
			Edge.RebuildTextArguments();
		}
	}

	if (bUpdateGraphNode)
	{
		UpdateGraphNode();
	}
}

void UDlgNode::UpdateGraphNode()
{
#if WITH_EDITOR
	UDlgDialogue::GetDialogueEditorAccess()->UpdateGraphNodeEdges(GraphNode);
#endif // WITH_EDITOR
}

void UDlgNode::GetAssociatedParticipants(TArray<FName>& OutArray) const
{
	if (OwnerName != NAME_None)
	{
		OutArray.AddUnique(OwnerName);
	}
}

UDlgDialogue* UDlgNode::GetDialogue() const
{
	return CastChecked<UDlgDialogue>(GetOuter());
}

USoundWave* UDlgNode::GetNodeVoiceSoundWave() const
{
	return Cast<USoundWave>(GetNodeVoiceSoundBase());
}

// End own functions


for (int32 EdgeIndex = 0; EdgeIndex < EdgesNum; EdgeIndex++)
	{
		if (!Children[EdgeIndex].IsValid())
		{
			OutArray.Add(EdgeIndex);
		}
	}

#include "Nodes/DlgNode.h"

#include "Kismet/GameplayStatics.h"
#include "EngineUtils.h"

#include "DlgContext.h"
#include "Logging/DlgLogger.h"
#include "DlgLocalizationHelper.h"
#include "DlgDialogueParticipant.h"
#include "Sound/SoundWave.h"

// Begin UObject interface
void UDlgNode::Serialize(FArchive& Ar)
{
	Super::Serialize(Ar);
	if (Ar.UE4Ver() >= VER_UE4_COOKED_ASSETS_IN_EDITOR_SUPPORT)
	{
		// NOTE: This modifies the Archive
		// DO NOT REMOVE THIS
		const FStripDataFlags StripFlags(Ar);

		// Only in editor, add the graph node
#if WITH_EDITOR
		if (!StripFlags.IsEditorDataStripped())
		{
			Ar << GraphNode;
		}
#endif // WITH_EDITOR
	}
	else
	{
		// Super old version, is this possible?
#if WITH_EDITOR
		Ar << GraphNode;
#endif // WITH_EDITOR
	}
}

void UDlgNode::AddReferencedObjects(UObject* InThis, FReferenceCollector& Collector)
{
	UDlgNode* This = CastChecked<UDlgNode>(InThis);

	// Add the GraphNode to the referenced objects
#if WITH_EDITOR
	Collector.AddReferencedObject(This->GraphNode, This);
#endif

	Super::AddReferencedObjects(InThis, Collector);
}

void UDlgNode::PostLoad()
{
	Super::PostLoad();

	// NOTE: We don't this here but instead we do it in the compile phase
	// Create thew new GUID
}

void UDlgNode::PostInitProperties()
{
	Super::PostInitProperties();

	// Ignore these cases
	if (HasAnyFlags(RF_ClassDefaultObject | RF_NeedLoad))
	{
		return;
	}

	// GUID is set in the dialogue compile phase
}

void UDlgNode::PostDuplicate(bool bDuplicateForPIE)
{
	Super::PostDuplicate(bDuplicateForPIE);

	// Used when duplicating Nodes.
	// We only generate a new GUID is the existing one is valid, otherwise it will be set in the compile phase
	if (HasGUID())
	{
		RegenerateGUID();
	}
}

void UDlgNode::PostEditImport()
{
	Super::PostEditImport();

	// Used when duplicating Nodes.
	// We only generate a new GUID is the existing one is valid, otherwise it will be set in the compile phase
	if (HasGUID())
	{
		RegenerateGUID();
	}
}

#if WITH_EDITOR
void UDlgNode::PostEditChangeProperty(FPropertyChangedEvent& PropertyChangedEvent)
{
	Super::PostEditChangeProperty(PropertyChangedEvent);

	// Signal to the listeners
	OnDialogueNodePropertyChanged.Broadcast(PropertyChangedEvent, BroadcastPropertyEdgeIndexChanged);
	BroadcastPropertyEdgeIndexChanged = INDEX_NONE;
}

void UDlgNode::PostEditChangeChainProperty(struct FPropertyChangedChainEvent& PropertyChangedEvent)
{
	// The Super::PostEditChangeChainProperty will construct a new FPropertyChangedEvent that will only have the Property and the
	// MemberProperty name and it will call the PostEditChangeProperty, so we must get the array index of the Nodes modified from here.
	// If you want to preserve all the change history of the tree you must broadcast the event from here to the children, but be warned
	// that Property and MemberProperty are not set properly.
	BroadcastPropertyEdgeIndexChanged = PropertyChangedEvent.GetArrayIndex(GET_MEMBER_NAME_STRING_CHECKED(UDlgNode, Children));
	Super::PostEditChangeChainProperty(PropertyChangedEvent);
}

#endif //WITH_EDITOR
// End UObject interface

// Begin own function
bool UDlgNode::HandleNodeEnter(UDlgContext& Context, TSet<const UDlgNode*> NodesEnteredWithThisStep)
{
	// Fire all the node enter events
	FireNodeEnterEvents(Context);

	for (FDlgEdge& Edge : Children)
	{
		Edge.RebuildConstructedText(Context, OwnerName);
	}

	return ReevaluateChildren(Context, {});
}

void UDlgNode::FireNodeEnterEvents(UDlgContext& Context)
{
	for (const FDlgEvent& Event : EnterEvents)
	{
		// Get Participant from either event or parent
		UObject* Participant = Context.GetMutableParticipant(Event.ParticipantName);
		if (!IsValid(Participant))
		{
			Participant = Context.GetMutableParticipant(OwnerName);
		}

		Event.Call(Context, TEXT("FireNodeEnterEvents"), Participant);
	}
}

bool UDlgNode::ReevaluateChildren(UDlgContext& Context, TSet<const UDlgNode*> AlreadyEvaluated)
{
	TArray<FDlgEdge>& AvailableOptions = Context.GetMutableOptionsArray();
	TArray<FDlgEdgeData>& AllOptions = Context.GetAllMutableOptionsArray();
	AvailableOptions.Empty();
	AllOptions.Empty();

	for (const FDlgEdge& Edge : Children)
	{
		const bool bSatisfied = Edge.Evaluate(Context, { this });

		if (bSatisfied || Edge.bIncludeInAllOptionListIfUnsatisfied)
		{
			AllOptions.Add(FDlgEdgeData{ bSatisfied, Edge });
		}
		if (bSatisfied)
		{
			AvailableOptions.Add(Edge);
		}
	}

	// no child, but no end node?
	if (AvailableOptions.Num() == 0)
	{
		FDlgLogger::Get().Errorf(
			TEXT("ReevaluateChildren (ReevaluateOptions) - no valid child option for a NODE.\nContext:\n\t%s"),
			*Context.GetContextString()
		);
		return false;
	}

	return true;
}

bool UDlgNode::CheckNodeEnterConditions(const UDlgContext& Context, TSet<const UDlgNode*> AlreadyVisitedNodes) const
{
	if (AlreadyVisitedNodes.Contains(this))
	{
		return true;
	}

	AlreadyVisitedNodes.Add(this);
	if (!FDlgCondition::EvaluateArray(Context, EnterConditions, OwnerName))
	{
		return false;
	}
	if (!bCheckChildrenOnEvaluation)
	{
		return true;
	}

	// Has a valid child?
	return HasAnySatisfiedChild(Context, AlreadyVisitedNodes);
}

bool UDlgNode::HasAnySatisfiedChild(const UDlgContext& Context, TSet<const UDlgNode*> AlreadyVisitedNodes) const
{
	for (const FDlgEdge& Edge : Children)
	{
		// Found at least one valid child
		if (Edge.Evaluate(Context, AlreadyVisitedNodes))
		{
			return true;
		}
	}

	return false;
}

bool UDlgNode::OptionSelected(int32 OptionIndex, UDlgContext& Context)
{
	const TArray<FDlgEdge>& AvailableOptions = Context.GetOptionsArray();
	if (AvailableOptions.IsValidIndex(OptionIndex))
	{
		check(AvailableOptions[OptionIndex].IsValid());
		return Context.EnterNode(AvailableOptions[OptionIndex].TargetIndex, {});
	}

	FDlgLogger::Get().Errorf(
		TEXT("OptionSelected - Failed to choose OptionIndex = %d - it only has %d valid options.\nContext:\n\t%s"),
		OptionIndex, AvailableOptions.Num(), *Context.GetContextString()
	);
	return false;
}

const TArray<int32> UDlgNode::GetNodeOpenChildren_DEPRECATED() const
{
	TArray<int32> OutArray;
	const int32 EdgesNum = Children.Num();

	return OutArray;
}

FDlgEdge* UDlgNode::GetMutableNodeChildForTargetIndex(int32 TargetIndex)
{
	for (FDlgEdge& Edge : Children)
	{
		if (Edge.TargetIndex == TargetIndex)
		{
			return &Edge;
		}
	}

	return nullptr;
}

void UDlgNode::UpdateTextsValuesFromDefaultsAndRemappings(
	const UDlgSystemSettings& Settings, bool bEdges, bool bUpdateGraphNode
)
{
	// We only care about edges here
	if (bEdges)
	{
		const bool bSkipAfterFirstChild = Settings.bSetDefaultEdgeTextOnFirstChildOnly;
		if (Settings.bSetDefaultEdgeTexts)
		{
			const UDlgDialogue* Dialogue = GetDialogue();
			for (FDlgEdge& Edge : Children)
			{
				Edge.UpdateTextValueFromDefaultAndRemapping(*Dialogue, *this, Settings, false);

				// Set only one, kill the rest
				if (bSkipAfterFirstChild)
				{
					break;
				}
			}
		}

		// Update the rest of the texts remapping
		for (FDlgEdge& Edge : Children)
		{
			FDlgLocalizationHelper::UpdateTextFromRemapping(Settings, Edge.GetMutableUnformattedText());
		}
	}

	if (bUpdateGraphNode)
	{
		UpdateGraphNode();
	}
}

void UDlgNode::UpdateTextsNamespacesAndKeys(const UDlgSystemSettings& Settings, bool bEdges, bool bUpdateGraphNode)
{
	if (bEdges)
	{
		UObject* Outer = GetOuter();
		for (FDlgEdge& Edge : Children)
		{
			Edge.UpdateTextsNamespacesAndKeys(Outer, Settings);
		}
	}

	if (bUpdateGraphNode)
	{
		UpdateGraphNode();
	}
}

void UDlgNode::RebuildTextArguments(bool bEdges, bool bUpdateGraphNode)
{
	if (bEdges)
	{
		for (FDlgEdge& Edge : Children)
		{
			Edge.RebuildTextArguments();
		}
	}

	if (bUpdateGraphNode)
	{
		UpdateGraphNode();
	}
}

void UDlgNode::UpdateGraphNode()
{
#if WITH_EDITOR
	UDlgDialogue::GetDialogueEditorAccess()->UpdateGraphNodeEdges(GraphNode);
#endif // WITH_EDITOR
}

void UDlgNode::GetAssociatedParticipants(TArray<FName>& OutArray) const
{
	if (OwnerName != NAME_None)
	{
		OutArray.AddUnique(OwnerName);
	}
}

UDlgDialogue* UDlgNode::GetDialogue() const
{
	return CastChecked<UDlgDialogue>(GetOuter());
}

USoundWave* UDlgNode::GetNodeVoiceSoundWave() const
{
	return Cast<USoundWave>(GetNodeVoiceSoundBase());
}

// End own functions


#include <cstring>
#include <functional>
#include <vector>

#include "util/model_runner.h"

{
    using hannk::ModelRunner;

    ModelRunner runner;

    // Default the exernal delegate to disabled, since it may
    // need extra setup to work (eg LD_LIBRARY_PATH or --external_delegate_path)
    runner.do_run[ModelRunner::kExternalDelegate] = false;

    std::vector<std::string> files_to_process;
    int r = runner.parse_flags(argc, argv, files_to_process);
    if (r != 0) {
        return r;
    }

    runner.status();

    for (auto f : files_to_process) {
        runner.run(f);
        halide_profiler_report(nullptr);
        halide_profiler_reset();
    }
    return 0;
}


#include <cstring>
#include <functional>
#include <vector>

#include "util/model_runner.h"

int main(int argc, char **argv) {
    using hannk::ModelRunner;

    ModelRunner runner;

    // Default the exernal delegate to disabled, since it may
    // need extra setup to work (eg LD_LIBRARY_PATH or --external_delegate_path)
    runner.do_run[ModelRunner::kExternalDelegate] = false;

    std::vector<std::string> files_to_process;
    int r = runner.parse_flags(argc, argv, files_to_process);
    if (r != 0) {
        return r;
    }

    runner.status();

    for (auto f : files_to_process) {
        runner.run(f);
        halide_profiler_report(nullptr);
        halide_profiler_reset();
    }
    ret


bool GLSLProgram::SetUniformValue(std::string uniformName, const unsigned char size, GLsizei count, const GLint* value)

#include <cstdio>
#include <cstring>
#include <fstream>
#include <algorithm>
#include <cctype>
#include <sstream>

#include "System/VFS.h" // for PathDelimiter

#include "GLSLProgram.h"

using namespace GemRB;

std::string GLSLProgram::errMessage;

GLSLProgram::~GLSLProgram()
{
	if (program != 0) glDeleteProgram(program);
}

GLSLProgram* GLSLProgram::Create(std::string vertexSource, std::string fragmentSource)
{
	GLSLProgram* program = new GLSLProgram();
	if (!program->buildProgram(vertexSource, fragmentSource)) {
		delete program;
		return NULL;
	}
	return program;
}

GLSLProgram* GLSLProgram::CreateFromFiles(std::string vertexSourceFileName, std::string fragmentSourceFileName)
{
	std::string vertexContent;
	std::string fragmentContent;

	// first check the build dir then fallback to DATA_DIR
	std::ifstream fileStream(vertexSourceFileName.c_str());
#if __APPLE__
	if (!fileStream.is_open()) {
		char bundleShaderPath[_MAX_PATH];
		CopyBundlePath(bundleShaderPath, sizeof(bundleShaderPath), RESOURCES);
		PathAppend(bundleShaderPath, vertexSourceFileName.c_str());
		ResolveFilePath(bundleShaderPath);
		fileStream.open(bundleShaderPath);
	}
#elif defined DATA_DIR
	if (!fileStream.is_open()) {
		vertexSourceFileName.insert(0, 1, PathDelimiter);
		vertexSourceFileName.insert(0, DATA_DIR);
		fileStream.open(vertexSourceFileName.c_str());
	}
#endif
	if (!fileStream.is_open()) {
		GLSLProgram::errMessage = "GLSLProgram error: Can't open file: " + vertexSourceFileName;
		return NULL;
	}
	std::string line = "";
	while (!fileStream.eof()) {
		std::getline(fileStream, line);
		line.erase(0, line.find_first_not_of("\n\t\r "));
#if USE_OPENGL_API
		// remove precisions when using OpenGL and not GLES
		if (line.find("precision") == 0) continue;
#endif
		vertexContent.append(line + "\n");
	}
	fileStream.close();

	std::ifstream fileStream2(fragmentSourceFileName.c_str());
#if __APPLE__
	if (!fileStream2.is_open()) {
		char bundleShaderPath[_MAX_PATH];
		CopyBundlePath(bundleShaderPath, sizeof(bundleShaderPath), RESOURCES);
		PathAppend(bundleShaderPath, fragmentSourceFileName.c_str());
		ResolveFilePath(bundleShaderPath);
		fileStream2.open(bundleShaderPath);
	}
#elif defined DATA_DIR
	if (!fileStream2.is_open()) {
		fragmentSourceFileName.insert(0, 1, PathDelimiter);
		fragmentSourceFileName.insert(0, DATA_DIR);
		fileStream2.open(fragmentSourceFileName.c_str());
	}
#endif
	if (!fileStream2.is_open()) {
		GLSLProgram::errMessage = "GLSLProgram error: Can't open file: " + fragmentSourceFileName;
		return NULL;
	}
	while (!fileStream2.eof()) {
		std::getline(fileStream2, line);
		line.erase(0, line.find_first_not_of("\n\t\r "));
#if USE_OPENGL_API
		// remove precisions
		if (line.find("precision") == 0) continue;
#endif
		fragmentContent.append(line + "\n");
	}
	fileStream2.close();
	return GLSLProgram::Create(vertexContent, fragmentContent);
}

GLuint GLSLProgram::buildShader(GLenum type, std::string shader_source)
{
#if USE_OPENGL_API
	shader_source.insert(0, "#version 110\n");
#else
	shader_source.insert(0, "#version 100\n");
#endif
	GLuint id = glCreateShader(type);
	const char* src = shader_source.c_str();
	glShaderSource(id, 1, &src, 0);
	glCompileShader(id);
	GLint result = GL_FALSE;
	glGetShaderiv(id, GL_COMPILE_STATUS, &result);
	if (result != GL_TRUE) {
		char buff[512];
		glGetShaderInfoLog(id, sizeof(buff), 0, buff);
		GLSLProgram::errMessage = std::string(buff);
		glDeleteShader(id);
		return 0;
	}
	return id;
}

bool GLSLProgram::buildProgram(std::string vertexSource, const std::string& fragmentSource)
{
	program = 0;
	program = glCreateProgram();
	if (program == 0) {
		GLSLProgram::errMessage = "GLSLProgram error: glCreateProgram failed";
		glDeleteProgram(program);
		return false;
	}

	GLuint vertexId = buildShader(GL_VERTEX_SHADER, vertexSource);
	if (vertexId == 0) {
		glDeleteProgram(program);
		return false;
	}
	GLuint fragmentId = buildShader(GL_FRAGMENT_SHADER, fragmentSource);
	if (fragmentId == 0) {
		glDeleteProgram(program);
		return false;
	}

	glAttachShader(program, vertexId);
	glAttachShader(program, fragmentId);

	glLinkProgram(program);
	GLint result = GL_FALSE;
	glGetProgramiv(program, GL_LINK_STATUS, &result);
	if (result != GL_TRUE) {
		char buff[512];
		glGetProgramInfoLog(program, sizeof(buff), 0, buff);
		GLSLProgram::errMessage = std::string(buff);
		glDeleteProgram(program);
		program = 0;
	}
	glDeleteShader(vertexId);
	glDeleteShader(fragmentId);
	if (program != 0) {
		int count = -1;
		glGetProgramiv(program, GL_ACTIVE_UNIFORMS, &count);
		for (int i = 0; i < count; i++) {
			int name_len = -1, num = -1;
			GLenum type = GL_ZERO;
			char name[64];
			glGetActiveUniform(program, GLuint(i), sizeof(name) - 1, &name_len, &num, &type, name);
			name[name_len] = 0;
			storeUniformLocation(std::string(name));
		}
	}
	return program != 0;
}

GLint GLSLProgram::getUniformLocation(std::string uniformName)
{
	if (uniforms.find(uniformName) == uniforms.end()) {
		GLSLProgram::errMessage = "GLSLProgram error: Invalid uniform location";
		return -1;
	}
	return uniforms.at(uniformName);
}

const std::string GLSLProgram::GetLastError()
{
	return GLSLProgram::errMessage;
}

bool GLSLProgram::storeUniformLocation(std::string uniformName)
{
	if (uniforms.find(uniformName) == uniforms.end()) {
		GLint location = glGetUniformLocation(program, uniformName.c_str());
		if (location == -1)
		{
			GLSLProgram::errMessage = "GLSLProgram error: Invalid uniform location";
			return false;
		}
		uniforms[uniformName] = location;
		return true;
	}
	else
	{
		GLSLProgram::errMessage = "GLSLProgram error: Uniform already stored";
		return true;
	}
}

bool GLSLProgram::SetUniformValue(std::string uniformName, const unsigned char size, GLsizei count, const GLfloat* value)
{
	GLint location = getUniformLocation(uniformName);
	if (location == -1) return false;
	switch (size) {
		case 1:
			glUniform1fv(location, count, value);
			return true;
		case 2:
			glUniform2fv(location, count, value);
			return true;
		case 3:
			glUniform3fv(location, count, value);
			return true;
		case 4:
			glUniform4fv(location, count, value);
			return true;
		default:
			GLSLProgram::errMessage = "GLSLProgram error: Invalid uniform size";
			return false;
	}
}

{
	GLint location = getUniformLocation(uniformName);
	if (location == -1) return false;
	switch (size) {
		case 1:
			glUniform1iv(location, count, value);
			return true;
		case 2:
			glUniform2iv(location, count, value);
			return true;
		case 3:
			glUniform3iv(location, count, value);
			return true;
		case 4:
			glUniform4iv(location, count, value);
			return true;
		default:
			GLSLProgram::errMessage = "GLSLProgram error: Invalid uniform size";
			return false;
	}
}

bool GLSLProgram::SetUniformMatrixValue(std::string uniformName, const unsigned char size, GLsizei count, const GLfloat* value)
{
	GLint location = getUniformLocation(uniformName);
	if (location == -1) return false;
	switch (size)
	{
		case 2:
			glUniformMatrix2fv(location, count, GL_FALSE, value);
			return true;
		case 3:
			glUniformMatrix3fv(location, count, GL_FALSE, value);
			return true;
		case 4:
			glUniformMatrix4fv(location, count, GL_FALSE, value);
			return true;
		default:
			GLSLProgram::errMessage = "GLSLProgram error: Invalid uniform size";
			return false;
	}
}

bool GLSLProgram::SetUniformValue(std::string uniformName, const unsigned char size, GLfloat value1, GLfloat value2, GLfloat value3, GLfloat value4)
{
	GLint location = getUniformLocation(uniformName);
	if (location == -1) return false;
	switch (size) {
		case 1:
			glUniform1f(location, value1);
			return true;
		case 2:
			glUniform2f(location, value1, value2);
			return true;
		case 3:
			glUniform3f(location, value1, value2, value3);
			return true;
		case 4:
			glUniform4f(location, value1, value2, value3, value4);
			return true;
		default:
			GLSLProgram::errMessage = "GLSLProgram error: Invalid uniform size";
			return false;
	}
}

bool GLSLProgram::SetUniformValue(std::string uniformName, const unsigned char size, GLint value1, GLint value2, GLint value3, GLint value4)
{
	GLint location = getUniformLocation(uniformName);
	if (location == -1) return false;
	switch (size) {
		case 1:
			glUniform1i(location, value1);
			return true;
		case 2:
			glUniform2i(location, value1, value2);
			return true;
		case 3:
			glUniform3i(location, value1, value2, value3);
			return true;
		case 4:
			glUniform4i(location, value1, value2, value3, value4);
			return true;
		default:
			GLSLProgram::errMessage = "GLSLProgram error: Invalid uniform size";
			return false;
	}
}

void GLSLProgram::Use() const
{
	glUseProgram(program);
}

void GLSLProgram::UnUse()
{
	glUseProgram(0);
}

GLint GLSLProgram::GetAttribLocation(std::string attribName) const
{
	return glGetAttribLocation(program, attribName.c_str());
}


{
	if (program != 0) glDeleteProgram(program);
}

#include <cstdio>
#include <cstring>
#include <fstream>
#include <algorithm>
#include <cctype>
#include <sstream>

#include "System/VFS.h" // for PathDelimiter

#include "GLSLProgram.h"

using namespace GemRB;

std::string GLSLProgram::errMessage;

GLSLProgram::~GLSLProgram()

GLSLProgram* GLSLProgram::Create(std::string vertexSource, std::string fragmentSource)
{
	GLSLProgram* program = new GLSLProgram();
	if (!program->buildProgram(vertexSource, fragmentSource)) {
		delete program;
		return NULL;
	}
	return program;
}

GLSLProgram* GLSLProgram::CreateFromFiles(std::string vertexSourceFileName, std::string fragmentSourceFileName)
{
	std::string vertexContent;
	std::string fragmentContent;

	// first check the build dir then fallback to DATA_DIR
	std::ifstream fileStream(vertexSourceFileName.c_str());
#if __APPLE__
	if (!fileStream.is_open()) {
		char bundleShaderPath[_MAX_PATH];
		CopyBundlePath(bundleShaderPath, sizeof(bundleShaderPath), RESOURCES);
		PathAppend(bundleShaderPath, vertexSourceFileName.c_str());
		ResolveFilePath(bundleShaderPath);
		fileStream.open(bundleShaderPath);
	}
#elif defined DATA_DIR
	if (!fileStream.is_open()) {
		vertexSourceFileName.insert(0, 1, PathDelimiter);
		vertexSourceFileName.insert(0, DATA_DIR);
		fileStream.open(vertexSourceFileName.c_str());
	}
#endif
	if (!fileStream.is_open()) {
		GLSLProgram::errMessage = "GLSLProgram error: Can't open file: " + vertexSourceFileName;
		return NULL;
	}
	std::string line = "";
	while (!fileStream.eof()) {
		std::getline(fileStream, line);
		line.erase(0, line.find_first_not_of("\n\t\r "));
#if USE_OPENGL_API
		// remove precisions when using OpenGL and not GLES
		if (line.find("precision") == 0) continue;
#endif
		vertexContent.append(line + "\n");
	}
	fileStream.close();

	std::ifstream fileStream2(fragmentSourceFileName.c_str());
#if __APPLE__
	if (!fileStream2.is_open()) {
		char bundleShaderPath[_MAX_PATH];
		CopyBundlePath(bundleShaderPath, sizeof(bundleShaderPath), RESOURCES);
		PathAppend(bundleShaderPath, fragmentSourceFileName.c_str());
		ResolveFilePath(bundleShaderPath);
		fileStream2.open(bundleShaderPath);
	}
#elif defined DATA_DIR
	if (!fileStream2.is_open()) {
		fragmentSourceFileName.insert(0, 1, PathDelimiter);
		fragmentSourceFileName.insert(0, DATA_DIR);
		fileStream2.open(fragmentSourceFileName.c_str());
	}
#endif
	if (!fileStream2.is_open()) {
		GLSLProgram::errMessage = "GLSLProgram error: Can't open file: " + fragmentSourceFileName;
		return NULL;
	}
	while (!fileStream2.eof()) {
		std::getline(fileStream2, line);
		line.erase(0, line.find_first_not_of("\n\t\r "));
#if USE_OPENGL_API
		// remove precisions
		if (line.find("precision") == 0) continue;
#endif
		fragmentContent.append(line + "\n");
	}
	fileStream2.close();
	return GLSLProgram::Create(vertexContent, fragmentContent);
}

GLuint GLSLProgram::buildShader(GLenum type, std::string shader_source)
{
#if USE_OPENGL_API
	shader_source.insert(0, "#version 110\n");
#else
	shader_source.insert(0, "#version 100\n");
#endif
	GLuint id = glCreateShader(type);
	const char* src = shader_source.c_str();
	glShaderSource(id, 1, &src, 0);
	glCompileShader(id);
	GLint result = GL_FALSE;
	glGetShaderiv(id, GL_COMPILE_STATUS, &result);
	if (result != GL_TRUE) {
		char buff[512];
		glGetShaderInfoLog(id, sizeof(buff), 0, buff);
		GLSLProgram::errMessage = std::string(buff);
		glDeleteShader(id);
		return 0;
	}
	return id;
}

bool GLSLProgram::buildProgram(std::string vertexSource, const std::string& fragmentSource)
{
	program = 0;
	program = glCreateProgram();
	if (program == 0) {
		GLSLProgram::errMessage = "GLSLProgram error: glCreateProgram failed";
		glDeleteProgram(program);
		return false;
	}

	GLuint vertexId = buildShader(GL_VERTEX_SHADER, vertexSource);
	if (vertexId == 0) {
		glDeleteProgram(program);
		return false;
	}
	GLuint fragmentId = buildShader(GL_FRAGMENT_SHADER, fragmentSource);
	if (fragmentId == 0) {
		glDeleteProgram(program);
		return false;
	}

	glAttachShader(program, vertexId);
	glAttachShader(program, fragmentId);

	glLinkProgram(program);
	GLint result = GL_FALSE;
	glGetProgramiv(program, GL_LINK_STATUS, &result);
	if (result != GL_TRUE) {
		char buff[512];
		glGetProgramInfoLog(program, sizeof(buff), 0, buff);
		GLSLProgram::errMessage = std::string(buff);
		glDeleteProgram(program);
		program = 0;
	}
	glDeleteShader(vertexId);
	glDeleteShader(fragmentId);
	if (program != 0) {
		int count = -1;
		glGetProgramiv(program, GL_ACTIVE_UNIFORMS, &count);
		for (int i = 0; i < count; i++) {
			int name_len = -1, num = -1;
			GLenum type = GL_ZERO;
			char name[64];
			glGetActiveUniform(program, GLuint(i), sizeof(name) - 1, &name_len, &num, &type, name);
			name[name_len] = 0;
			storeUniformLocation(std::string(name));
		}
	}
	return program != 0;
}

GLint GLSLProgram::getUniformLocation(std::string uniformName)
{
	if (uniforms.find(uniformName) == uniforms.end()) {
		GLSLProgram::errMessage = "GLSLProgram error: Invalid uniform location";
		return -1;
	}
	return uniforms.at(uniformName);
}

const std::string GLSLProgram::GetLastError()
{
	return GLSLProgram::errMessage;
}

bool GLSLProgram::storeUniformLocation(std::string uniformName)
{
	if (uniforms.find(uniformName) == uniforms.end()) {
		GLint location = glGetUniformLocation(program, uniformName.c_str());
		if (location == -1)
		{
			GLSLProgram::errMessage = "GLSLProgram error: Invalid uniform location";
			return false;
		}
		uniforms[uniformName] = location;
		return true;
	}
	else
	{
		GLSLProgram::errMessage = "GLSLProgram error: Uniform already stored";
		return true;
	}
}

bool GLSLProgram::SetUniformValue(std::string uniformName, const unsigned char size, GLsizei count, const GLfloat* value)
{
	GLint location = getUniformLocation(uniformName);
	if (location == -1) return false;
	switch (size) {
		case 1:
			glUniform1fv(location, count, value);
			return true;
		case 2:
			glUniform2fv(location, count, value);
			return true;
		case 3:
			glUniform3fv(location, count, value);
			return true;
		case 4:
			glUniform4fv(location, count, value);
			return true;
		default:
			GLSLProgram::errMessage = "GLSLProgram error: Invalid uniform size";
			return false;
	}
}

bool GLSLProgram::SetUniformValue(std::string uniformName, const unsigned char size, GLsizei count, const GLint* value)
{
	GLint location = getUniformLocation(uniformName);
	if (location == -1) return false;
	switch (size) {
		case 1:
			glUniform1iv(location, count, value);
			return true;
		case 2:
			glUniform2iv(location, count, value);
			return true;
		case 3:
			glUniform3iv(location, count, value);
			return true;
		case 4:
			glUniform4iv(location, count, value);
			return true;
		default:
			GLSLProgram::errMessage = "GLSLProgram error: Invalid uniform size";
			return false;
	}
}

bool GLSLProgram::SetUniformMatrixValue(std::string uniformName, const unsigned char size, GLsizei count, const GLfloat* value)
{
	GLint location = getUniformLocation(uniformName);
	if (location == -1) return false;
	switch (size)
	{
		case 2:
			glUniformMatrix2fv(location, count, GL_FALSE, value);
			return true;
		case 3:
			glUniformMatrix3fv(location, count, GL_FALSE, value);
			return true;
		case 4:
			glUniformMatrix4fv(location, count, GL_FALSE, value);
			return true;
		default:
			GLSLProgram::errMessage = "GLSLProgram error: Invalid uniform size";
			return false;
	}
}

bool GLSLProgram::SetUniformValue(std::string uniformName, const unsigned char size, GLfloat value1, GLfloat value2, GLfloat value3, GLfloat value4)
{
	GLint location = getUniformLocation(uniformName);
	if (location == -1) return false;
	switch (size) {
		case 1:
			glUniform1f(location, value1);
			return true;
		case 2:
			glUniform2f(location, value1, value2);
			return true;
		case 3:
			glUniform3f(location, value1, value2, value3);
			return true;
		case 4:
			glUniform4f(location, value1, value2, value3, value4);
			return true;
		default:
			GLSLProgram::errMessage = "GLSLProgram error: Invalid uniform size";
			return false;
	}
}

bool GLSLProgram::SetUniformValue(std::string uniformName, const unsigned char size, GLint value1, GLint value2, GLint value3, GLint value4)
{
	GLint location = getUniformLocation(uniformName);
	if (location == -1) return false;
	switch (size) {
		case 1:
			glUniform1i(location, value1);
			return true;
		case 2:
			glUniform2i(location, value1, value2);
			return true;
		case 3:
			glUniform3i(location, value1, value2, value3);
			return true;
		case 4:
			glUniform4i(location, value1, value2, value3, value4);
			return true;
		default:
			GLSLProgram::errMessage = "GLSLProgram error: Invalid uniform size";
			return false;
	}
}

void GLSLProgram::Use() const
{
	glUseProgram(program);
}

void GLSLProgram::UnUse()
{
	glUseProgram(0);
}

GLint GLSLProgram::GetAttribLocation(std::string attribName) const
{
	return glGetAttribLocation(program, attribName.c_str());
}


#include <cstdio>
#include <cstring>
#include <fstream>
#include <algorithm>
#include <cctype>
#include <sstream>

#include "System/VFS.h" // for PathDelimiter

#include "GLSLProgram.h"

using namespace GemRB;

std::string GLSLProgram::errMessage;

GLSLProgram::~GLSLProgram()
{
	if (program != 0) glDeleteProgram(program);
}

GLSLProgram* GLSLProgram::Create(std::string vertexSource, std::string fragmentSource)
{
	GLSLProgram* program = new GLSLProgram();
	if (!program->buildProgram(vertexSource, fragmentSource)) {
		delete program;
		return NULL;
	}
	return program;
}

GLSLProgram* GLSLProgram::CreateFromFiles(std::string vertexSourceFileName, std::string fragmentSourceFileName)
{
	std::string vertexContent;
	std::string fragmentContent;

	// first check the build dir then fallback to DATA_DIR
	std::ifstream fileStream(vertexSourceFileName.c_str());
#if __APPLE__
	if (!fileStream.is_open()) {
		char bundleShaderPath[_MAX_PATH];
		CopyBundlePath(bundleShaderPath, sizeof(bundleShaderPath), RESOURCES);
		PathAppend(bundleShaderPath, vertexSourceFileName.c_str());
		ResolveFilePath(bundleShaderPath);
		fileStream.open(bundleShaderPath);
	}
#elif defined DATA_DIR
	if (!fileStream.is_open()) {
		vertexSourceFileName.insert(0, 1, PathDelimiter);
		vertexSourceFileName.insert(0, DATA_DIR);
		fileStream.open(vertexSourceFileName.c_str());
	}
#endif
	if (!fileStream.is_open()) {
		GLSLProgram::errMessage = "GLSLProgram error: Can't open file: " + vertexSourceFileName;
		return NULL;
	}
	std::string li

	while (!fileStream.eof()) {
		std::getline(fileStream, line);
		line.erase(0, line.find_first_not_of("\n\t\r "));
#if USE_OPENGL_API
		// remove precisions when using OpenGL and not GLES
		if (line.find("precision") == 0) continue;
#endif
		vertexContent.append(line + "\n");
	}
	fileStream.close();

	std::ifstream fileStream2(fragmentSourceFileName.c_str());
#if __APPLE__
	if (!fileStream2.is_open()) {
		char bundleShaderPath[_MAX_PATH];
		CopyBundlePath(bundleShaderPath, sizeof(bundleShaderPath), RESOURCES);
		PathAppend(bundleShaderPath, fragmentSourceFileName.c_str());
		ResolveFilePath(bundleShaderPath);
		fileStream2.open(bundleShaderPath);
	}
#elif defined DATA_DIR
	if (!fileStream2.is_open()) {
		fragmentSourceFileName.insert(0, 1, PathDelimiter);
		fragmentSourceFileName.insert(0, DATA_DIR);
		fileStream2.open(fragmentSourceFileName.c_str());
	}
#endif
	if (!fileStream2.is_open()) {
		GLSLProgram::errMessage = "GLSLProgram error: Can't open file: " + fragmentSourceFileName;
		return NULL;
	}
	while (!fileStream2.eof()) {
		std::getline(fileStream2, line);
		line.erase(0, line.find_first_not_of("\n\t\r "));
#if USE_OPENGL_API
		// remove precisions
		if (line.find("precision") == 0) continue;
#endif
		fragmentContent.append(line + "\n");
	}
	fileStream2.close();
	return GLSLProgram::Create(vertexContent, fragmentContent);
}

GLuint GLSLProgram::buildShader(GLenum type, std::string shader_source)
{
#if USE_OPENGL_API
	shader_source.insert(0, "#version 110\n");
#else
	shader_source.insert(0, "#version 100\n");
#endif
	GLuint id = glCreateShader(type);
	const char* src = shader_source.c_str();
	glShaderSource(id, 1, &src, 0);
	glCompileShader(id);
	GLint result = GL_FALSE;
	glGetShaderiv(id, GL_COMPILE_STATUS, &result);
	if (result != GL_TRUE) {
		char buff[512];
		glGetShaderInfoLog(id, sizeof(buff), 0, buff);
		GLSLProgram::errMessage = std::string(buff);
		glDeleteShader(id);
		return 0;
	}
	return id;
}

bool GLSLProgram::buildProgram(std::string vertexSource, const std::string& fragmentSource)
{
	program = 0;
	program = glCreateProgram();
	if (program == 0) {
		GLSLProgram::errMessage = "GLSLProgram error: glCreateProgram failed";
		glDeleteProgram(program);
		return false;
	}

	GLuint vertexId = buildShader(GL_VERTEX_SHADER, vertexSource);
	if (vertexId == 0) {
		glDeleteProgram(program);
		return false;
	}
	GLuint fragmentId = buildShader(GL_FRAGMENT_SHADER, fragmentSource);
	if (fragmentId == 0) {
		glDeleteProgram(program);
		return false;
	}

	glAttachShader(program, vertexId);
	glAttachShader(program, fragmentId);

	glLinkProgram(program);
	GLint result = GL_FALSE;
	glGetProgramiv(program, GL_LINK_STATUS, &result);
	if (result != GL_TRUE) {
		char buff[512];
		glGetProgramInfoLog(program, sizeof(buff), 0, buff);
		GLSLProgram::errMessage = std::string(buff);
		glDeleteProgram(program);
		program = 0;
	}
	glDeleteShader(vertexId);
	glDeleteShader(fragmentId);
	if (program != 0) {
		int count = -1;
		glGetProgramiv(program, GL_ACTIVE_UNIFORMS, &count);
		for (int i = 0; i < count; i++) {
			int name_len = -1, num = -1;
			GLenum type = GL_ZERO;
			char name[64];
			glGetActiveUniform(program, GLuint(i), sizeof(name) - 1, &name_len, &num, &type, name);
			name[name_len] = 0;
			storeUniformLocation(std::string(name));
		}
	}
	return program != 0;
}

GLint GLSLProgram::getUniformLocation(std::string uniformName)
{
	if (uniforms.find(uniformName) == uniforms.end()) {
		GLSLProgram::errMessage = "GLSLProgram error: Invalid uniform location";
		return -1;
	}
	return uniforms.at(uniformName);
}

const std::string GLSLProgram::GetLastError()
{
	return GLSLProgram::errMessage;
}

bool GLSLProgram::storeUniformLocation(std::string uniformName)
{
	if (uniforms.find(uniformName) == uniforms.end()) {
		GLint location = glGetUniformLocation(program, uniformName.c_str());
		if (location == -1)
		{
			GLSLProgram::errMessage = "GLSLProgram error: Invalid uniform location";
			return false;
		}
		uniforms[uniformName] = location;
		return true;
	}
	else
	{
		GLSLProgram::errMessage = "GLSLProgram error: Uniform already stored";
		return true;
	}
}

bool GLSLProgram::SetUniformValue(std::string uniformName, const unsigned char size, GLsizei count, const GLfloat* value)
{
	GLint location = getUniformLocation(uniformName);
	if (location == -1) return false;
	switch (size) {
		case 1:
			glUniform1fv(location, count, value);
			return true;
		case 2:
			glUniform2fv(location, count, value);
			return true;
		case 3:
			glUniform3fv(location, count, value);
			return true;
		case 4:
			glUniform4fv(location, count, value);
			return true;
		default:
			GLSLProgram::errMessage = "GLSLProgram error: Invalid uniform size";
			return false;
	}
}

bool GLSLProgram::SetUniformValue(std::string uniformName, const unsigned char size, GLsizei count, const GLint* value)
{
	GLint location = getUniformLocation(uniformName);
	if (location == -1) return false;
	switch (size) {
		case 1:
			glUniform1iv(location, count, value);
			return true;
		case 2:
			glUniform2iv(location, count, value);
			return true;
		case 3:
			glUniform3iv(location, count, value);
			return true;
		case 4:
			glUniform4iv(location, count, value);
			return true;
		default:
			GLSLProgram::errMessage = "GLSLProgram error: Invalid uniform size";
			return false;
	}
}

bool GLSLProgram::SetUniformMatrixValue(std::string uniformName, const unsigned char size, GLsizei count, const GLfloat* value)
{
	GLint location = getUniformLocation(uniformName);
	if (location == -1) return false;
	switch (size)
	{
		case 2:
			glUniformMatrix2fv(location, count, GL_FALSE, value);
			return true;
		case 3:
			glUniformMatrix3fv(location, count, GL_FALSE, value);
			return true;
		case 4:
			glUniformMatrix4fv(location, count, GL_FALSE, value);
			return true;
		default:
			GLSLProgram::errMessage = "GLSLProgram error: Invalid uniform size";
			return false;
	}
}

bool GLSLProgram::SetUniformValue(std::string uniformName, const unsigned char size, GLfloat value1, GLfloat value2, GLfloat value3, GLfloat value4)
{
	GLint location = getUniformLocation(uniformName);
	if (location == -1) return false;
	switch (size) {
		case 1:
			glUniform1f(location, value1);
			return true;
		case 2:
			glUniform2f(location, value1, value2);
			return true;
		case 3:
			glUniform3f(location, value1, value2, value3);
			return true;
		case 4:
			glUniform4f(location, value1, value2, value3, value4);
			return true;
		default:
			GLSLProgram::errMessage = "GLSLProgram error: Invalid uniform size";
			return false;
	}
}

bool GLSLProgram::SetUniformValue(std::string uniformName, const unsigned char size, GLint value1, GLint value2, GLint value3, GLint value4)
{
	GLint location = getUniformLocation(uniformName);
	if (location == -1) return false;
	switch (size) {
		case 1:
			glUniform1i(location, value1);
			return true;
		case 2:
			glUniform2i(location, value1, value2);
			return true;
		case 3:
			glUniform3i(location, value1, value2, value3);
			return true;
		case 4:
			glUniform4i(location, value1, value2, value3, value4);
			return true;
		default:
			GLSLProgram::errMessage = "GLSLProgram error: Invalid uniform size";
			return false;
	}
}

void GLSLProgram::Use() const
{
	glUseProgram(program);
}

void GLSLProgram::UnUse()
{
	glUseProgram(0);
}

GLint GLSLProgram::GetAttribLocation(std::string attribName) const
{
	return glGetAttribLocation(program, attribName.c_str());
}


switch (size)
	{
		case 2:
			glUniformMatrix2fv(location, count, GL_FALSE, value);
			return true;
		case 3:
			glUniformMatrix3fv(location, count, GL_FALSE, value);
			return true;
		case 4:
			glUniformMatrix4fv(location, count, GL_FALSE, value);
			return true;
		default:
			GLSLProgram::errMessage = "GLSLProgram error: Invalid uniform size";
			return false;
	}

#include <cstdio>
#include <cstring>
#include <fstream>
#include <algorithm>
#include <cctype>
#include <sstream>

#include "System/VFS.h" // for PathDelimiter

#include "GLSLProgram.h"

using namespace GemRB;

std::string GLSLProgram::errMessage;

GLSLProgram::~GLSLProgram()
{
	if (program != 0) glDeleteProgram(program);
}

GLSLProgram* GLSLProgram::Create(std::string vertexSource, std::string fragmentSource)
{
	GLSLProgram* program = new GLSLProgram();
	if (!program->buildProgram(vertexSource, fragmentSource)) {
		delete program;
		return NULL;
	}
	return program;
}

GLSLProgram* GLSLProgram::CreateFromFiles(std::string vertexSourceFileName, std::string fragmentSourceFileName)
{
	std::string vertexContent;
	std::string fragmentContent;

	// first check the build dir then fallback to DATA_DIR
	std::ifstream fileStream(vertexSourceFileName.c_str());
#if __APPLE__
	if (!fileStream.is_open()) {
		char bundleShaderPath[_MAX_PATH];
		CopyBundlePath(bundleShaderPath, sizeof(bundleShaderPath), RESOURCES);
		PathAppend(bundleShaderPath, vertexSourceFileName.c_str());
		ResolveFilePath(bundleShaderPath);
		fileStream.open(bundleShaderPath);
	}
#elif defined DATA_DIR
	if (!fileStream.is_open()) {
		vertexSourceFileName.insert(0, 1, PathDelimiter);
		vertexSourceFileName.insert(0, DATA_DIR);
		fileStream.open(vertexSourceFileName.c_str());
	}
#endif
	if (!fileStream.is_open()) {
		GLSLProgram::errMessage = "GLSLProgram error: Can't open file: " + vertexSourceFileName;
		return NULL;
	}
	std::string line = "";
	while (!fileStream.eof()) {
		std::getline(fileStream, line);
		line.erase(0, line.find_first_not_of("\n\t\r "));
#if USE_OPENGL_API
		// remove precisions when using OpenGL and not GLES
		if (line.find("precision") == 0) continue;
#endif
		vertexContent.append(line + "\n");
	}
	fileStream.close();

	std::ifstream fileStream2(fragmentSourceFileName.c_str());
#if __APPLE__
	if (!fileStream2.is_open()) {
		char bundleShaderPath[_MAX_PATH];
		CopyBundlePath(bundleShaderPath, sizeof(bundleShaderPath), RESOURCES);
		PathAppend(bundleShaderPath, fragmentSourceFileName.c_str());
		ResolveFilePath(bundleShaderPath);
		fileStream2.open(bundleShaderPath);
	}
#elif defined DATA_DIR
	if (!fileStream2.is_open()) {
		fragmentSourceFileName.insert(0, 1, PathDelimiter);
		fragmentSourceFileName.insert(0, DATA_DIR);
		fileStream2.open(fragmentSourceFileName.c_str());
	}
#endif
	if (!fileStream2.is_open()) {
		GLSLProgram::errMessage = "GLSLProgram error: Can't open file: " + fragmentSourceFileName;
		return NULL;
	}
	while (!fileStream2.eof()) {
		std::getline(fileStream2, line);
		line.erase(0, line.find_first_not_of("\n\t\r "));
#if USE_OPENGL_API
		// remove precisions
		if (line.find("precision") == 0) continue;
#endif
		fragmentContent.append(line + "\n");
	}
	fileStream2.close();
	return GLSLProgram::Create(vertexContent, fragmentContent);
}

GLuint GLSLProgram::buildShader(GLenum type, std::string shader_source)
{
#if USE_OPENGL_API
	shader_source.insert(0, "#version 110\n");
#else
	shader_source.insert(0, "#version 100\n");
#endif
	GLuint id = glCreateShader(type);
	const char* src = shader_source.c_str();
	glShaderSource(id, 1, &src, 0);
	glCompileShader(id);
	GLint result = GL_FALSE;
	glGetShaderiv(id, GL_COMPILE_STATUS, &result);
	if (result != GL_TRUE) {
		char buff[512];
		glGetShaderInfoLog(id, sizeof(buff), 0, buff);
		GLSLProgram::errMessage = std::string(buff);
		glDeleteShader(id);
		return 0;
	}
	return id;
}

bool GLSLProgram::buildProgram(std::string vertexSource, const std::string& fragmentSource)
{
	program = 0;
	program = glCreateProgram();
	if (program == 0) {
		GLSLProgram::errMessage = "GLSLProgram error: glCreateProgram failed";
		glDeleteProgram(program);
		return false;
	}

	GLuint vertexId = buildShader(GL_VERTEX_SHADER, vertexSource);
	if (vertexId == 0) {
		glDeleteProgram(program);
		return false;
	}
	GLuint fragmentId = buildShader(GL_FRAGMENT_SHADER, fragmentSource);
	if (fragmentId == 0) {
		glDeleteProgram(program);
		return false;
	}

	glAttachShader(program, vertexId);
	glAttachShader(program, fragmentId);

	glLinkProgram(program);
	GLint result = GL_FALSE;
	glGetProgramiv(program, GL_LINK_STATUS, &result);
	if (result != GL_TRUE) {
		char buff[512];
		glGetProgramInfoLog(program, sizeof(buff), 0, buff);
		GLSLProgram::errMessage = std::string(buff);
		glDeleteProgram(program);
		program = 0;
	}
	glDeleteShader(vertexId);
	glDeleteShader(fragmentId);
	if (program != 0) {
		int count = -1;
		glGetProgramiv(program, GL_ACTIVE_UNIFORMS, &count);
		for (int i = 0; i < count; i++) {
			int name_len = -1, num = -1;
			GLenum type = GL_ZERO;
			char name[64];
			glGetActiveUniform(program, GLuint(i), sizeof(name) - 1, &name_len, &num, &type, name);
			name[name_len] = 0;
			storeUniformLocation(std::string(name));
		}
	}
	return program != 0;
}

GLint GLSLProgram::getUniformLocation(std::string uniformName)
{
	if (uniforms.find(uniformName) == uniforms.end()) {
		GLSLProgram::errMessage = "GLSLProgram error: Invalid uniform location";
		return -1;
	}
	return uniforms.at(uniformName);
}

const std::string GLSLProgram::GetLastError()
{
	return GLSLProgram::errMessage;
}

bool GLSLProgram::storeUniformLocation(std::string uniformName)
{
	if (uniforms.find(uniformName) == uniforms.end()) {
		GLint location = glGetUniformLocation(program, uniformName.c_str());
		if (location == -1)
		{
			GLSLProgram::errMessage = "GLSLProgram error: Invalid uniform location";
			return false;
		}
		uniforms[uniformName] = location;
		return true;
	}
	else
	{
		GLSLProgram::errMessage = "GLSLProgram error: Uniform already stored";
		return true;
	}
}

bool GLSLProgram::SetUniformValue(std::string uniformName, const unsigned char size, GLsizei count, const GLfloat* value)
{
	GLint location = getUniformLocation(uniformName);
	if (location == -1) return false;
	switch (size) {
		case 1:
			glUniform1fv(location, count, value);
			return true;
		case 2:
			glUniform2fv(location, count, value);
			return true;
		case 3:
			glUniform3fv(location, count, value);
			return true;
		case 4:
			glUniform4fv(location, count, value);
			return true;
		default:
			GLSLProgram::errMessage = "GLSLProgram error: Invalid uniform size";
			return false;
	}
}

bool GLSLProgram::SetUniformValue(std::string uniformName, const unsigned char size, GLsizei count, const GLint* value)
{
	GLint location = getUniformLocation(uniformName);
	if (location == -1) return false;
	switch (size) {
		case 1:
			glUniform1iv(location, count, value);
			return true;
		case 2:
			glUniform2iv(location, count, value);
			return true;
		case 3:
			glUniform3iv(location, count, value);
			return true;
		case 4:
			glUniform4iv(location, count, value);
			return true;
		default:
			GLSLProgram::errMessage = "GLSLProgram error: Invalid uniform size";
			return false;
	}
}

bool GLSLProgram::SetUniformMatrixValue(std::string uniformName, const unsigned char size, GLsizei count, const GLfloat* value)
{
	GLint location = getUniformLocation(uniformName);
	if (location == -1) return false;

}

bool GLSLProgram::SetUniformValue(std::string uniformName, const unsigned char size, GLfloat value1, GLfloat value2, GLfloat value3, GLfloat value4)
{
	GLint location = getUniformLocation(uniformName);
	if (location == -1) return false;
	switch (size) {
		case 1:
			glUniform1f(location, value1);
			return true;
		case 2:
			glUniform2f(location, value1, value2);
			return true;
		case 3:
			glUniform3f(location, value1, value2, value3);
			return true;
		case 4:
			glUniform4f(location, value1, value2, value3, value4);
			return true;
		default:
			GLSLProgram::errMessage = "GLSLProgram error: Invalid uniform size";
			return false;
	}
}

bool GLSLProgram::SetUniformValue(std::string uniformName, const unsigned char size, GLint value1, GLint value2, GLint value3, GLint value4)
{
	GLint location = getUniformLocation(uniformName);
	if (location == -1) return false;
	switch (size) {
		case 1:
			glUniform1i(location, value1);
			return true;
		case 2:
			glUniform2i(location, value1, value2);
			return true;
		case 3:
			glUniform3i(location, value1, value2, value3);
			return true;
		case 4:
			glUniform4i(location, value1, value2, value3, value4);
			return true;
		default:
			GLSLProgram::errMessage = "GLSLProgram error: Invalid uniform size";
			return false;
	}
}

void GLSLProgram::Use() const
{
	glUseProgram(program);
}

void GLSLProgram::UnUse()
{
	glUseProgram(0);
}

GLint GLSLProgram::GetAttribLocation(std::string attribName) const
{
	return glGetAttribLocation(program, attribName.c_str());
}


for (int i = 0; i < count; i++) {
			int name_len = -1, num = -1;
			GLenum type = GL_ZERO;
			char name[64];
			glGetActiveUniform(program, GLuint(i), sizeof(name) - 1, &name_len, &num, &type, name);
			name[name_len] = 0;
			storeUniformLocation(std::string(name));
		}

#include <cstdio>
#include <cstring>
#include <fstream>
#include <algorithm>
#include <cctype>
#include <sstream>

#include "System/VFS.h" // for PathDelimiter

#include "GLSLProgram.h"

using namespace GemRB;

std::string GLSLProgram::errMessage;

GLSLProgram::~GLSLProgram()
{
	if (program != 0) glDeleteProgram(program);
}

GLSLProgram* GLSLProgram::Create(std::string vertexSource, std::string fragmentSource)
{
	GLSLProgram* program = new GLSLProgram();
	if (!program->buildProgram(vertexSource, fragmentSource)) {
		delete program;
		return NULL;
	}
	return program;
}

GLSLProgram* GLSLProgram::CreateFromFiles(std::string vertexSourceFileName, std::string fragmentSourceFileName)
{
	std::string vertexContent;
	std::string fragmentContent;

	// first check the build dir then fallback to DATA_DIR
	std::ifstream fileStream(vertexSourceFileName.c_str());
#if __APPLE__
	if (!fileStream.is_open()) {
		char bundleShaderPath[_MAX_PATH];
		CopyBundlePath(bundleShaderPath, sizeof(bundleShaderPath), RESOURCES);
		PathAppend(bundleShaderPath, vertexSourceFileName.c_str());
		ResolveFilePath(bundleShaderPath);
		fileStream.open(bundleShaderPath);
	}
#elif defined DATA_DIR
	if (!fileStream.is_open()) {
		vertexSourceFileName.insert(0, 1, PathDelimiter);
		vertexSourceFileName.insert(0, DATA_DIR);
		fileStream.open(vertexSourceFileName.c_str());
	}
#endif
	if (!fileStream.is_open()) {
		GLSLProgram::errMessage = "GLSLProgram error: Can't open file: " + vertexSourceFileName;
		return NULL;
	}
	std::string line = "";
	while (!fileStream.eof()) {
		std::getline(fileStream, line);
		line.erase(0, line.find_first_not_of("\n\t\r "));
#if USE_OPENGL_API
		// remove precisions when using OpenGL and not GLES
		if (line.find("precision") == 0) continue;
#endif
		vertexContent.append(line + "\n");
	}
	fileStream.close();

	std::ifstream fileStream2(fragmentSourceFileName.c_str());
#if __APPLE__
	if (!fileStream2.is_open()) {
		char bundleShaderPath[_MAX_PATH];
		CopyBundlePath(bundleShaderPath, sizeof(bundleShaderPath), RESOURCES);
		PathAppend(bundleShaderPath, fragmentSourceFileName.c_str());
		ResolveFilePath(bundleShaderPath);
		fileStream2.open(bundleShaderPath);
	}
#elif defined DATA_DIR
	if (!fileStream2.is_open()) {
		fragmentSourceFileName.insert(0, 1, PathDelimiter);
		fragmentSourceFileName.insert(0, DATA_DIR);
		fileStream2.open(fragmentSourceFileName.c_str());
	}
#endif
	if (!fileStream2.is_open()) {
		GLSLProgram::errMessage = "GLSLProgram error: Can't open file: " + fragmentSourceFileName;
		return NULL;
	}
	while (!fileStream2.eof()) {
		std::getline(fileStream2, line);
		line.erase(0, line.find_first_not_of("\n\t\r "));
#if USE_OPENGL_API
		// remove precisions
		if (line.find("precision") == 0) continue;
#endif
		fragmentContent.append(line + "\n");
	}
	fileStream2.close();
	return GLSLProgram::Create(vertexContent, fragmentContent);
}

GLuint GLSLProgram::buildShader(GLenum type, std::string shader_source)
{
#if USE_OPENGL_API
	shader_source.insert(0, "#version 110\n");
#else
	shader_source.insert(0, "#version 100\n");
#endif
	GLuint id = glCreateShader(type);
	const char* src = shader_source.c_str();
	glShaderSource(id, 1, &src, 0);
	glCompileShader(id);
	GLint result = GL_FALSE;
	glGetShaderiv(id, GL_COMPILE_STATUS, &result);
	if (result != GL_TRUE) {
		char buff[512];
		glGetShaderInfoLog(id, sizeof(buff), 0, buff);
		GLSLProgram::errMessage = std::string(buff);
		glDeleteShader(id);
		return 0;
	}
	return id;
}

bool GLSLProgram::buildProgram(std::string vertexSource, const std::string& fragmentSource)
{
	program = 0;
	program = glCreateProgram();
	if (program == 0) {
		GLSLProgram::errMessage = "GLSLProgram error: glCreateProgram failed";
		glDeleteProgram(program);
		return false;
	}

	GLuint vertexId = buildShader(GL_VERTEX_SHADER, vertexSource);
	if (vertexId == 0) {
		glDeleteProgram(program);
		return false;
	}
	GLuint fragmentId = buildShader(GL_FRAGMENT_SHADER, fragmentSource);
	if (fragmentId == 0) {
		glDeleteProgram(program);
		return false;
	}

	glAttachShader(program, vertexId);
	glAttachShader(program, fragmentId);

	glLinkProgram(program);
	GLint result = GL_FALSE;
	glGetProgramiv(program, GL_LINK_STATUS, &result);
	if (result != GL_TRUE) {
		char buff[512];
		glGetProgramInfoLog(program, sizeof(buff), 0, buff);
		GLSLProgram::errMessage = std::string(buff);
		glDeleteProgram(program);
		program = 0;
	}
	glDeleteShader(vertexId);
	glDeleteShader(fragmentId);
	if (program != 0) {
		int count = -1;
		glGetProgramiv(program, GL_ACTIVE_UNIFORMS, &count);

	}
	return program != 0;
}

GLint GLSLProgram::getUniformLocation(std::string uniformName)
{
	if (uniforms.find(uniformName) == uniforms.end()) {
		GLSLProgram::errMessage = "GLSLProgram error: Invalid uniform location";
		return -1;
	}
	return uniforms.at(uniformName);
}

const std::string GLSLProgram::GetLastError()
{
	return GLSLProgram::errMessage;
}

bool GLSLProgram::storeUniformLocation(std::string uniformName)
{
	if (uniforms.find(uniformName) == uniforms.end()) {
		GLint location = glGetUniformLocation(program, uniformName.c_str());
		if (location == -1)
		{
			GLSLProgram::errMessage = "GLSLProgram error: Invalid uniform location";
			return false;
		}
		uniforms[uniformName] = location;
		return true;
	}
	else
	{
		GLSLProgram::errMessage = "GLSLProgram error: Uniform already stored";
		return true;
	}
}

bool GLSLProgram::SetUniformValue(std::string uniformName, const unsigned char size, GLsizei count, const GLfloat* value)
{
	GLint location = getUniformLocation(uniformName);
	if (location == -1) return false;
	switch (size) {
		case 1:
			glUniform1fv(location, count, value);
			return true;
		case 2:
			glUniform2fv(location, count, value);
			return true;
		case 3:
			glUniform3fv(location, count, value);
			return true;
		case 4:
			glUniform4fv(location, count, value);
			return true;
		default:
			GLSLProgram::errMessage = "GLSLProgram error: Invalid uniform size";
			return false;
	}
}

bool GLSLProgram::SetUniformValue(std::string uniformName, const unsigned char size, GLsizei count, const GLint* value)
{
	GLint location = getUniformLocation(uniformName);
	if (location == -1) return false;
	switch (size) {
		case 1:
			glUniform1iv(location, count, value);
			return true;
		case 2:
			glUniform2iv(location, count, value);
			return true;
		case 3:
			glUniform3iv(location, count, value);
			return true;
		case 4:
			glUniform4iv(location, count, value);
			return true;
		default:
			GLSLProgram::errMessage = "GLSLProgram error: Invalid uniform size";
			return false;
	}
}

bool GLSLProgram::SetUniformMatrixValue(std::string uniformName, const unsigned char size, GLsizei count, const GLfloat* value)
{
	GLint location = getUniformLocation(uniformName);
	if (location == -1) return false;
	switch (size)
	{
		case 2:
			glUniformMatrix2fv(location, count, GL_FALSE, value);
			return true;
		case 3:
			glUniformMatrix3fv(location, count, GL_FALSE, value);
			return true;
		case 4:
			glUniformMatrix4fv(location, count, GL_FALSE, value);
			return true;
		default:
			GLSLProgram::errMessage = "GLSLProgram error: Invalid uniform size";
			return false;
	}
}

bool GLSLProgram::SetUniformValue(std::string uniformName, const unsigned char size, GLfloat value1, GLfloat value2, GLfloat value3, GLfloat value4)
{
	GLint location = getUniformLocation(uniformName);
	if (location == -1) return false;
	switch (size) {
		case 1:
			glUniform1f(location, value1);
			return true;
		case 2:
			glUniform2f(location, value1, value2);
			return true;
		case 3:
			glUniform3f(location, value1, value2, value3);
			return true;
		case 4:
			glUniform4f(location, value1, value2, value3, value4);
			return true;
		default:
			GLSLProgram::errMessage = "GLSLProgram error: Invalid uniform size";
			return false;
	}
}

bool GLSLProgram::SetUniformValue(std::string uniformName, const unsigned char size, GLint value1, GLint value2, GLint value3, GLint value4)
{
	GLint location = getUniformLocation(uniformName);
	if (location == -1) return false;
	switch (size) {
		case 1:
			glUniform1i(location, value1);
			return true;
		case 2:
			glUniform2i(location, value1, value2);
			return true;
		case 3:
			glUniform3i(location, value1, value2, value3);
			return true;
		case 4:
			glUniform4i(location, value1, value2, value3, value4);
			return true;
		default:
			GLSLProgram::errMessage = "GLSLProgram error: Invalid uniform size";
			return false;
	}
}

void GLSLProgram::Use() const
{
	glUseProgram(program);
}

void GLSLProgram::UnUse()
{
	glUseProgram(0);
}

GLint GLSLProgram::GetAttribLocation(std::string attribName) const
{
	return glGetAttribLocation(program, attribName.c_str());
}


while (!fileStream.eof()) {
		std::getline(fileStream, line);
		line.erase(0, line.find_first_not_of("\n\t\r "));
#if USE_OPENGL_API
		// remove precisions when using OpenGL and not GLES
		if (line.find("precision") == 0) continue;
#endif
		vertexContent.append(line + "\n");
	}

#include <cstdio>
#include <cstring>
#include <fstream>
#include <algorithm>
#include <cctype>
#include <sstream>

#include "System/VFS.h" // for PathDelimiter

#include "GLSLProgram.h"

using namespace GemRB;

std::string GLSLProgram::errMessage;

GLSLProgram::~GLSLProgram()
{
	if (program != 0) glDeleteProgram(program);
}

GLSLProgram* GLSLProgram::Create(std::string vertexSource, std::string fragmentSource)
{
	GLSLProgram* program = new GLSLProgram();
	if (!program->buildProgram(vertexSource, fragmentSource)) {
		delete program;
		return NULL;
	}
	return program;
}

GLSLProgram* GLSLProgram::CreateFromFiles(std::string vertexSourceFileName, std::string fragmentSourceFileName)
{
	std::string vertexContent;
	std::string fragmentContent;

	// first check the build dir then fallback to DATA_DIR
	std::ifstream fileStream(vertexSourceFileName.c_str());
#if __APPLE__
	if (!fileStream.is_open()) {
		char bundleShaderPath[_MAX_PATH];
		CopyBundlePath(bundleShaderPath, sizeof(bundleShaderPath), RESOURCES);
		PathAppend(bundleShaderPath, vertexSourceFileName.c_str());
		ResolveFilePath(bundleShaderPath);
		fileStream.open(bundleShaderPath);
	}
#elif defined DATA_DIR
	if (!fileStream.is_open()) {
		vertexSourceFileName.insert(0, 1, PathDelimiter);
		vertexSourceFileName.insert(0, DATA_DIR);
		fileStream.open(vertexSourceFileName.c_str());
	}
#endif
	if (!fileStream.is_open()) {
		GLSLProgram::errMessage = "GLSLProgram error: Can't open file: " + vertexSourceFileName;
		return NULL;
	}
	std::string line = "";

	fileStream.close();

	std::ifstream fileStream2(fragmentSourceFileName.c_str());
#if __APPLE__
	if (!fileStream2.is_open()) {
		char bundleShaderPath[_MAX_PATH];
		CopyBundlePath(bundleShaderPath, sizeof(bundleShaderPath), RESOURCES);
		PathAppend(bundleShaderPath, fragmentSourceFileName.c_str());
		ResolveFilePath(bundleShaderPath);
		fileStream2.open(bundleShaderPath);
	}
#elif defined DATA_DIR
	if (!fileStream2.is_open()) {
		fragmentSourceFileName.insert(0, 1, PathDelimiter);
		fragmentSourceFileName.insert(0, DATA_DIR);
		fileStream2.open(fragmentSourceFileName.c_str());
	}
#endif
	if (!fileStream2.is_open()) {
		GLSLProgram::errMessage = "GLSLProgram error: Can't open file: " + fragmentSourceFileName;
		return NULL;
	}
	while (!fileStream2.eof()) {
		std::getline(fileStream2, line);
		line.erase(0, line.find_first_not_of("\n\t\r "));
#if USE_OPENGL_API
		// remove precisions
		if (line.find("precision") == 0) continue;
#endif
		fragmentContent.append(line + "\n");
	}
	fileStream2.close();
	return GLSLProgram::Create(vertexContent, fragmentContent);
}

GLuint GLSLProgram::buildShader(GLenum type, std::string shader_source)
{
#if USE_OPENGL_API
	shader_source.insert(0, "#version 110\n");
#else
	shader_source.insert(0, "#version 100\n");
#endif
	GLuint id = glCreateShader(type);
	const char* src = shader_source.c_str();
	glShaderSource(id, 1, &src, 0);
	glCompileShader(id);
	GLint result = GL_FALSE;
	glGetShaderiv(id, GL_COMPILE_STATUS, &result);
	if (result != GL_TRUE) {
		char buff[512];
		glGetShaderInfoLog(id, sizeof(buff), 0, buff);
		GLSLProgram::errMessage = std::string(buff);
		glDeleteShader(id);
		return 0;
	}
	return id;
}

bool GLSLProgram::buildProgram(std::string vertexSource, const std::string& fragmentSource)
{
	program = 0;
	program = glCreateProgram();
	if (program == 0) {
		GLSLProgram::errMessage = "GLSLProgram error: glCreateProgram failed";
		glDeleteProgram(program);
		return false;
	}

	GLuint vertexId = buildShader(GL_VERTEX_SHADER, vertexSource);
	if (vertexId == 0) {
		glDeleteProgram(program);
		return false;
	}
	GLuint fragmentId = buildShader(GL_FRAGMENT_SHADER, fragmentSource);
	if (fragmentId == 0) {
		glDeleteProgram(program);
		return false;
	}

	glAttachShader(program, vertexId);
	glAttachShader(program, fragmentId);

	glLinkProgram(program);
	GLint result = GL_FALSE;
	glGetProgramiv(program, GL_LINK_STATUS, &result);
	if (result != GL_TRUE) {
		char buff[512];
		glGetProgramInfoLog(program, sizeof(buff), 0, buff);
		GLSLProgram::errMessage = std::string(buff);
		glDeleteProgram(program);
		program = 0;
	}
	glDeleteShader(vertexId);
	glDeleteShader(fragmentId);
	if (program != 0) {
		int count = -1;
		glGetProgramiv(program, GL_ACTIVE_UNIFORMS, &count);
		for (int i = 0; i < count; i++) {
			int name_len = -1, num = -1;
			GLenum type = GL_ZERO;
			char name[64];
			glGetActiveUniform(program, GLuint(i), sizeof(name) - 1, &name_len, &num, &type, name);
			name[name_len] = 0;
			storeUniformLocation(std::string(name));
		}
	}
	return program != 0;
}

GLint GLSLProgram::getUniformLocation(std::string uniformName)
{
	if (uniforms.find(uniformName) == uniforms.end()) {
		GLSLProgram::errMessage = "GLSLProgram error: Invalid uniform location";
		return -1;
	}
	return uniforms.at(uniformName);
}

const std::string GLSLProgram::GetLastError()
{
	return GLSLProgram::errMessage;
}

bool GLSLProgram::storeUniformLocation(std::string uniformName)
{
	if (uniforms.find(uniformName) == uniforms.end()) {
		GLint location = glGetUniformLocation(program, uniformName.c_str());
		if (location == -1)
		{
			GLSLProgram::errMessage = "GLSLProgram error: Invalid uniform location";
			return false;
		}
		uniforms[uniformName] = location;
		return true;
	}
	else
	{
		GLSLProgram::errMessage = "GLSLProgram error: Uniform already stored";
		return true;
	}
}

bool GLSLProgram::SetUniformValue(std::string uniformName, const unsigned char size, GLsizei count, const GLfloat* value)
{
	GLint location = getUniformLocation(uniformName);
	if (location == -1) return false;
	switch (size) {
		case 1:
			glUniform1fv(location, count, value);
			return true;
		case 2:
			glUniform2fv(location, count, value);
			return true;
		case 3:
			glUniform3fv(location, count, value);
			return true;
		case 4:
			glUniform4fv(location, count, value);
			return true;
		default:
			GLSLProgram::errMessage = "GLSLProgram error: Invalid uniform size";
			return false;
	}
}

bool GLSLProgram::SetUniformValue(std::string uniformName, const unsigned char size, GLsizei count, const GLint* value)
{
	GLint location = getUniformLocation(uniformName);
	if (location == -1) return false;
	switch (size) {
		case 1:
			glUniform1iv(location, count, value);
			return true;
		case 2:
			glUniform2iv(location, count, value);
			return true;
		case 3:
			glUniform3iv(location, count, value);
			return true;
		case 4:
			glUniform4iv(location, count, value);
			return true;
		default:
			GLSLProgram::errMessage = "GLSLProgram error: Invalid uniform size";
			return false;
	}
}

bool GLSLProgram::SetUniformMatrixValue(std::string uniformName, const unsigned char size, GLsizei count, const GLfloat* value)
{
	GLint location = getUniformLocation(uniformName);
	if (location == -1) return false;
	switch (size)
	{
		case 2:
			glUniformMatrix2fv(location, count, GL_FALSE, value);
			return true;
		case 3:
			glUniformMatrix3fv(location, count, GL_FALSE, value);
			return true;
		case 4:
			glUniformMatrix4fv(location, count, GL_FALSE, value);
			return true;
		default:
			GLSLProgram::errMessage = "GLSLProgram error: Invalid uniform size";
			return false;
	}
}

bool GLSLProgram::SetUniformValue(std::string uniformName, const unsigned char size, GLfloat value1, GLfloat value2, GLfloat value3, GLfloat value4)
{
	GLint location = getUniformLocation(uniformName);
	if (location == -1) return false;
	switch (size) {
		case 1:
			glUniform1f(location, value1);
			return true;
		case 2:
			glUniform2f(location, value1, value2);
			return true;
		case 3:
			glUniform3f(location, value1, value2, value3);
			return true;
		case 4:
			glUniform4f(location, value1, value2, value3, value4);
			return true;
		default:
			GLSLProgram::errMessage = "GLSLProgram error: Invalid uniform size";
			return false;
	}
}

bool GLSLProgram::SetUniformValue(std::string uniformName, const unsigned char size, GLint value1, GLint value2, GLint value3, GLint value4)
{
	GLint location = getUniformLocation(uniformName);
	if (location == -1) return false;
	switch (size) {
		case 1:
			glUniform1i(location, value1);
			return true;
		case 2:
			glUniform2i(location, value1, value2);
			return true;
		case 3:
			glUniform3i(location, value1, value2, value3);
			return true;
		case 4:
			glUniform4i(location, value1, value2, value3, value4);
			return true;
		default:
			GLSLProgram::errMessage = "GLSLProgram error: Invalid uniform size";
			return false;
	}
}

void GLSLProgram::Use() const
{
	glUseProgram(program);
}

void GLSLProgram::UnUse()
{
	glUseProgram(0);
}

GLint GLSLProgram::GetAttribLocation(std::string attribName) const
{
	return glGetAttribLocation(program, attribName.c_str());
}