// Copyright (c) 2012-2020 Wojciech Figat. All rights reserved.

#include "VisjectGraph.h"
#include "GraphUtilities.h"
#include "Engine/Core/Random.h"
#include "Engine/Core/Math/Vector4.h"
#include "Engine/Core/Math/Transform.h"
#include "Engine/Engine/GameplayGlobals.h"
#include "Engine/Scripting/Scripting.h"
#include "Engine/Level/Actor.h"

#define RAND Random::Rand()

VisjectExecutor::VisjectExecutor()
{
    // Register per group type processing events
    // Note: index must match group id
#if BUILD_DEBUG
    Platform::MemoryClear(&_perGroupProcessCall, sizeof(_perGroupProcessCall));
#endif
    _perGroupProcessCall[2] = &VisjectExecutor::ProcessGroupConstants;
    _perGroupProcessCall[3] = &VisjectExecutor::ProcessGroupMath;
    _perGroupProcessCall[4] = &VisjectExecutor::ProcessGroupPacking;
    _perGroupProcessCall[7] = &VisjectExecutor::ProcessGroupTools;
    _perGroupProcessCall[10] = &VisjectExecutor::ProcessGroupBoolean;
    _perGroupProcessCall[11] = &VisjectExecutor::ProcessGroupBitwise;
    _perGroupProcessCall[12] = &VisjectExecutor::ProcessGroupComparisons;
    _perGroupProcessCall[14] = &VisjectExecutor::ProcessGroupParticles;
}

VisjectExecutor::~VisjectExecutor()
{
}

void VisjectExecutor::OnError(Node* node, Box* box, const StringView& message)
{
    Error(node, box, message);
}

void VisjectExecutor::ProcessGroupConstants(Box* box, Node* node, Value& value)
{
    switch (node->TypeID)
    {
        // Constant value
    case 1:
    case 2:
    case 3:
    case 12:
        value = node->Values[0];
        break;
    case 4:
    {
        const Value& cv = node->Values[0];
        if (box->ID == 0)
            value = cv;
        else if (box->ID == 1)
            value = cv.AsVector2().X;
        else if (box->ID == 2)
            value = cv.AsVector2().Y;
        break;
    }
    case 5:
    {
        const Value& cv = node->Values[0];
        if (box->ID == 0)
            value = cv;
        else if (box->ID == 1)
            value = cv.AsVector3().X;
        else if (box->ID == 2)
            value = cv.AsVector3().Y;
        else if (box->ID == 3)
            value = cv.AsVector3().Z;
        break;
    }
    case 6:
    case 7:
    {
        const Value& cv = node->Values[0];
        if (box->ID == 0)
            value = cv;
        else if (box->ID == 1)
            value = cv.AsVector4().X;
        else if (box->ID == 2)
            value = cv.AsVector4().Y;
        else if (box->ID == 3)
            value = cv.AsVector4().Z;
        else if (box->ID == 4)
            value = cv.AsVector4().W;
        break;
    }
    case 8:
    {
        const float pitch = (float)node->Values[0];
        const float yaw = (float)node->Values[1];
        const float roll = (float)node->Values[2];
        value = Quaternion::Euler(pitch, yaw, roll);
        break;
    }
    case 9:
    {
        value = node->Values[0];
        break;
    }
        // PI
    case 10:
        value = PI;
        break;

    default:
        break;
    }
}

void VisjectExecutor::ProcessGroupMath(Box* box, Node* node, Value& value)
{
    switch (node->TypeID)
    {
        // Add, Subtract, Multiply, Divide, Modulo, Max, Min, Pow, Fmod, Atan2
    case 1:
    case 2:
    case 3:
    case 4:
    case 5:
    case 21:
    case 22:
    case 23:
    case 40:
    case 41:
    {
        Value v1 = tryGetValue(node->GetBox(0), 0, Value::Zero);
        Value v2 = tryGetValue(node->GetBox(1), 1, Value::Zero).Cast(v1.Type);
        GraphUtilities::ApplySomeMathHere(node->TypeID, value, v1, v2);
        break;
    }
        // Absolute Value, Ceil, Cosine, Floor, Round, Saturate, Sine, Sqrt, Tangent, Negate, 1 - Value, Asine, Acosine, Atan, Trunc, Frac, Degrees, Radians
    case 7:
    case 8:
    case 9:
    case 10:
    case 13:
    case 14:
    case 15:
    case 16:
    case 17:
    case 27:
    case 28:
    case 33:
    case 34:
    case 35:
    case 38:
    case 39:
    case 43:
    case 44:
    {
        Value v1 = tryGetValue(node->GetBox(0), Value::Zero);
        GraphUtilities::ApplySomeMathHere(node->TypeID, value, v1);
        break;
    }
        // Length, Normalize
    case 11:
    case 12:
    {
        Value v1 = tryGetValue(node->GetBox(0), Value::Zero);
        switch (node->TypeID)
        {
        case 11:
        {
            switch (v1.Type.Type)
            {
            case VariantType::Vector2:
                value = v1.AsVector2().Length();
                break;
            case VariantType::Vector3:
                value = v1.AsVector3().Length();
                break;
            case VariantType::Vector4:
                value = Vector3(v1.AsVector4()).Length();
                break;
            default: CRASH;
                break;
            }
        }
        case 12:
            switch (v1.Type.Type)
            {
            case VariantType::Int:
                value = Math::Saturate(v1.AsInt);
                break;
            case VariantType::Uint:
                value = Math::Saturate(v1.AsUint);
                break;
            case VariantType::Float:
                value = Math::Saturate(v1.AsFloat);
                break;
            case VariantType::Vector2:
                value = Vector2::Normalize(v1.AsVector2());
                break;
            case VariantType::Vector3:
                value = Vector3::Normalize(v1.AsVector3());
                break;
            case VariantType::Vector4:
                value = Vector4(Vector3::Normalize(Vector3(v1.AsVector4())), 0.0f);
                break;
            default: CRASH;
                break;
            }
        }
        break;
    }
        // Cross, Distance, Dot
    case 18:
    case 19:
    case 20:
    {
        Value v1 = tryGetValue(node->GetBox(0), Value::Zero);
        Value v2 = tryGetValue(node->GetBox(1), Value::Zero).Cast(v1.Type);
        switch (node->TypeID)
        {
        case 18:
            switch (v1.Type.Type)
            {
            case VariantType::Vector3:
                value = Vector3::Cross(v1.AsVector3(), v2.AsVector3());
                break;
            default: CRASH;
                break;
            }
            break;
        case 19:
            switch (v1.Type.Type)
            {
            case VariantType::Vector2:
                value = Vector2::Distance(v1.AsVector2(), v2.AsVector2());
                break;
            case VariantType::Vector3:
                value = Vector3::Distance(v1.AsVector3(), v2.AsVector3());
                break;
            case VariantType::Vector4:
            case VariantType::Color:
                value = Vector3::Distance((Vector3)v1, (Vector3)v2);
                break;
            default: CRASH;
                break;
            }
            break;
        case 20:
            switch (v1.Type.Type)
            {
            case VariantType::Vector2:
                value = Vector2::Dot(v1.AsVector2(), v2.AsVector2());
                break;
            case VariantType::Vector3:
                value = Vector3::Dot(v1.AsVector3(), v2.AsVector3());
                break;
            case VariantType::Vector4:
            case VariantType::Color:
                value = Vector3::Dot((Vector3)v1, (Vector3)v2);
                break;
            default: CRASH;
                break;
            }
            break;
        }
        break;
    }
        // Clamp
    case 24:
    {
        Value v1 = tryGetValue(node->GetBox(0), Value::Zero);
        Value v2 = tryGetValue(node->GetBox(1), 0, Value::Zero).Cast(v1.Type);
        Value v3 = tryGetValue(node->GetBox(2), 1, Value::One).Cast(v1.Type);
        GraphUtilities::ApplySomeMathHere(value, v1, v2, v3, [](float a, float b, float c)
        {
            return Math::Clamp(a, b, c);
        });
        break;
    }
        // Lerp
    case 25:
    {
        Value a = tryGetValue(node->GetBox(0), 0, Value::Zero);
        Value b = tryGetValue(node->GetBox(1), 1, Value::One).Cast(a.Type);
        Value alpha = tryGetValue(node->GetBox(2), 2, Value::Zero).Cast(ValueType(ValueType::Float));
        value = Value::Lerp(a, b, alpha.AsFloat);
        break;
    }
        // Reflect
    case 26:
    {
        Value v1 = tryGetValue(node->GetBox(0), Value::Zero);
        Value v2 = tryGetValue(node->GetBox(1), Value::Zero).Cast(v1.Type);
        switch (v1.Type.Type)
        {
        case VariantType::Vector2:
            value = v1.AsVector2() - 2 * v2.AsVector2() * Vector2::Dot(v1.AsVector2(), v2.AsVector2());
            break;
        case VariantType::Vector3:
            value = v1.AsVector3() - 2 * v2.AsVector3() * Vector3::Dot(v1.AsVector3(), v2.AsVector3());
            break;
        case VariantType::Vector4:
            value = Vector4(v1.AsVector4() - 2 * v2.AsVector4() * Vector3::Dot((Vector3)v1, (Vector3)v2));
            break;
        default: CRASH;
            break;
        }
        break;
    }
        // Mad
    case 31:
    {
        Value v1 = tryGetValue(node->GetBox(0), Value::Zero);
        Value v2 = tryGetValue(node->GetBox(1), 0, Value::One).Cast(v1.Type);
        Value v3 = tryGetValue(node->GetBox(2), 1, Value::Zero).Cast(v1.Type);
        GraphUtilities::ApplySomeMathHere(value, v1, v2, v3, [](float a, float b, float c)
        {
            return (a * b) + c;
        });
        break;
    }
        // Extract Largest Component
    case 32:
    {
        const auto v1 = (Vector3)tryGetValue(node->GetBox(0), Value::Zero);
        value = Math::ExtractLargestComponent(v1);
        break;
    }
        // Bias and Scale
    case 36:
    {
        ASSERT(node->Values.Count() == 2 && node->Values[0].Type == VariantType::Float && node->Values[1].Type == VariantType::Float);
        const auto bias = node->Values[0].AsFloat;
        const auto scale = node->Values[1].AsFloat;
        const auto input = (Vector3)tryGetValue(node->GetBox(0), Value::Zero);
        value = (input + bias) * scale;
        break;
    }
        // Rotate About Axis
    case 37:
    {
        const auto normalizedRotationAxis = (Vector3)tryGetValue(node->GetBox(0), Value::Zero);
        const auto rotationAngle = (float)tryGetValue(node->GetBox(1), Value::Zero);
        const auto pivotPoint = (Vector3)tryGetValue(node->GetBox(2), Value::Zero);
        const auto position = (Vector3)tryGetValue(node->GetBox(3), Value::Zero);
        value = Math::RotateAboutAxis(normalizedRotationAxis, rotationAngle, pivotPoint, position);
        break;
    }
        // Near Equal
    case 42:
    {
        const Value a = tryGetValue(node->GetBox(0), node->Values[0]);
        const Value b = tryGetValue(node->GetBox(1), node->Values[1]).Cast(a.Type);
        const float epsilon = (float)tryGetValue(node->GetBox(2), node->Values[2]);
        value = Value::NearEqual(a, b, epsilon);
        break;
    }
        // Enum Value
    case 45:
        value = (uint64)tryGetValue(node->GetBox(0), Value::Zero);
        break;
        // Enum AND
    case 46:
        value = tryGetValue(node->GetBox(0), Value::Zero);
        if (value.Type.Type == VariantType::Enum)
            value.AsUint64 = value.AsUint64 & (uint64)tryGetValue(node->GetBox(1), Value::Zero);
        break;
        // Enum OR
    case 47:
        value = tryGetValue(node->GetBox(0), Value::Zero);
        if (value.Type.Type == VariantType::Enum)
            value.AsUint64 = value.AsUint64 | (uint64)tryGetValue(node->GetBox(1), Value::Zero);
        break;
    default:
        break;
    }
}

void VisjectExecutor::ProcessGroupPacking(Box* box, Node* node, Value& value)
{
    switch (node->TypeID)
    {
        // Pack
    case 20:
    {
        float vX = (float)tryGetValue(node->GetBox(1), node->Values[0]);
        float vY = (float)tryGetValue(node->GetBox(2), node->Values[1]);
        value = Vector2(vX, vY);
        break;
    }
    case 21:
    {
        float vX = (float)tryGetValue(node->GetBox(1), node->Values[0]);
        float vY = (float)tryGetValue(node->GetBox(2), node->Values[1]);
        float vZ = (float)tryGetValue(node->GetBox(3), node->Values[2]);
        value = Vector3(vX, vY, vZ);
        break;
    }
    case 22:
    {
        float vX = (float)tryGetValue(node->GetBox(1), node->Values[0]);
        float vY = (float)tryGetValue(node->GetBox(2), node->Values[1]);
        float vZ = (float)tryGetValue(node->GetBox(3), node->Values[2]);
        float vW = (float)tryGetValue(node->GetBox(4), node->Values[3]);
        value = Vector4(vX, vY, vZ, vW);
        break;
    }
    case 23:
    {
        float vX = (float)tryGetValue(node->GetBox(1), node->Values[0]);
        float vY = (float)tryGetValue(node->GetBox(2), node->Values[1]);
        float vZ = (float)tryGetValue(node->GetBox(3), node->Values[2]);
        value = Quaternion::Euler(vX, vY, vZ);
        break;
    }
    case 24:
    {
        const Vector3 vX = (Vector3)tryGetValue(node->GetBox(1), Vector3::Zero);
        const Quaternion vY = (Quaternion)tryGetValue(node->GetBox(2), Quaternion::Identity);
        const Vector3 vZ = (Vector3)tryGetValue(node->GetBox(3), Vector3::One);
        value = Variant(Transform(vX, vY, vZ));
        break;
    }
    case 25:
    {
        const Vector3 vX = (Vector3)tryGetValue(node->GetBox(1), Vector3::Zero);
        const Vector3 vY = (Vector3)tryGetValue(node->GetBox(2), Vector3::Zero);
        value = Variant(BoundingBox(vX, vY));
        break;
    }
        // Unpack
    case 30:
    {
        Vector2 v = (Vector2)tryGetValue(node->GetBox(0), Vector2::Zero);
        int32 subIndex = box->ID - 1;
        ASSERT(subIndex >= 0 && subIndex < 2);
        value = v.Raw[subIndex];
        break;
    }
    case 31:
    {
        Vector3 v = (Vector3)tryGetValue(node->GetBox(0), Vector3::Zero);
        int32 subIndex = box->ID - 1;
        ASSERT(subIndex >= 0 && subIndex < 3);
        value = v.Raw[subIndex];
        break;
    }
    case 32:
    {
        Vector4 v = (Vector4)tryGetValue(node->GetBox(0), Vector4::Zero);
        int32 subIndex = box->ID - 1;
        ASSERT(subIndex >= 0 && subIndex < 4);
        value = v.Raw[subIndex];
        break;
    }
    case 33:
    {
        const Vector3 v = ((Quaternion)tryGetValue(node->GetBox(0), Quaternion::Identity)).GetEuler();
        const int32 subIndex = box->ID - 1;
        ASSERT(subIndex >= 0 && subIndex < 3);
        value = v.Raw[subIndex];
        break;
    }
    case 34:
    {
        const Transform v = (Transform)tryGetValue(node->GetBox(0), Variant::Zero);
        switch (box->ID)
        {
        case 1:
            value = v.Translation;
            break;
        case 2:
            value = v.Orientation;
            break;
        case 3:
            value = v.Scale;
            break;
        }
        break;
    }
    case 35:
    {
        const BoundingBox v = (BoundingBox)tryGetValue(node->GetBox(0), Variant::Zero);
        switch (box->ID)
        {
        case 1:
            value = v.Minimum;
            break;
        case 2:
            value = v.Maximum;
            break;
        }
        break;
    }
        // Mask X, Y, Z, W
    case 40:
    case 41:
    case 42:
    case 43:
    {
        const Vector4 v = (Vector4)tryGetValue(node->GetBox(0), Vector4::Zero);
        value = v.Raw[node->TypeID - 40];
        break;
    }
        // Mask XY, YZ, XZ,...
    case 44:
    {
        value = (Vector2)tryGetValue(node->GetBox(0), Vector2::Zero);
        break;
    }
    case 45:
    {
        const Vector4 v = (Vector4)tryGetValue(node->GetBox(0), Vector4::Zero);
        value = Vector2(v.X, v.Z);
        break;
    }
    case 46:
    {
        const Vector4 v = (Vector4)tryGetValue(node->GetBox(0), Vector4::Zero);
        value = Vector2(v.Y, v.Z);
        break;
    }
        // Mask XYZ
    case 70:
    {
        value = (Vector3)tryGetValue(node->GetBox(0), Vector3::Zero);
        break;
    }
        // Append
    case 100:
    {
        auto in0 = node->GetBox(0);
        auto in1 = node->GetBox(1);
        if (!in0->HasConnection() || !in1->HasConnection())
        {
            value = Value::Zero;
            break;
        }

        auto value0 = eatBox(in0->GetParent<Node>(), in0->FirstConnection());
        auto value1 = eatBox(in1->GetParent<Node>(), in1->FirstConnection());

        auto count0 = GraphUtilities::CountComponents(value0.Type.Type);
        auto count1 = GraphUtilities::CountComponents(value1.Type.Type);

        auto count = count0 + count1;
        switch (count)
        {
        case 1:
            value = count0 ? value0 : value1;
            break;
        case 2:
            value = Vector2((float)value0, (float)value1);
            break;
        case 3:
            if (count0 == 1)
                value = Vector3((float)value0, value1.AsVector2().X, value1.AsVector2().Y);
            else
                value = Vector3((Vector2)value0, (float)value1);
            break;
        case 4:
            if (count0 == 1)
                value = Vector4((float)value0, value1.AsVector3().X, value1.AsVector3().Y, value1.AsVector3().Z);
            else if (count0 == 2)
                value = Vector4(value0.AsVector2().X, value0.AsVector2().Y, value1.AsVector2().X, value1.AsVector2().Y);
            else
                value = Vector4((Vector3)value0, (float)value1);
            break;
        default:
            value = Value::Zero;
            break;
        }
        break;
    }
    default:
        break;
    }
}

void VisjectExecutor::ProcessGroupTools(Box* box, Node* node, Value& value)
{
    switch (node->TypeID)
    {
        // Color Gradient
    case 10:
    {
        float time, prevTime, curTime;
        Color prevColor, curColor;
        const int32 count = (int32)node->Values[0];
        switch (count)
        {
        case 0:
            // No sample
            value = Value::Zero;
            break;
        case 1:
            // Single sample
            value = (Color)node->Values[2];
            break;
        case 2:
            // Linear blend between 2 samples
            time = (float)tryGetValue(node->GetBox(0), Value::Zero);
            prevTime = (float)node->Values[1];
            prevColor = (Color)node->Values[2];
            curTime = (float)node->Values[3];
            curColor = (Color)node->Values[4];
            value = Color::Lerp(prevColor, curColor, Math::Saturate((time - prevTime) / (curTime - prevTime)));
            break;
        default:
            time = (float)tryGetValue(node->GetBox(0), Value::Zero);
            if (time >= node->Values[1 + count * 2 - 2].AsFloat)
            {
                // Outside the range
                value = (Color)node->Values[1 + count * 2 - 2 + 1];
            }
            else
            {
                // Find 2 samples to blend between them
                prevTime = (float)node->Values[1];
                prevColor = (Color)node->Values[2];
                for (int32 i = 1; i < count; i++)
                {
                    curTime = (float)node->Values[i * 2 + 1];
                    curColor = (Color)node->Values[i * 2 + 2];

                    if (time <= curTime)
                    {
                        value = Color::Lerp(prevColor, curColor, Math::Saturate((time - prevTime) / (curTime - prevTime)));
                        break;
                    }

                    prevTime = curTime;
                    prevColor = curColor;
                }
            }
            break;
        }
        break;
    }
        // Curve
#define SAMPLE_CURVE(id, curves, type, graphType) \
		case id: \
		{ \
			const auto& curve = GetCurrentGraph()->curves[node->Data.Curve.CurveIndex]; \
			const float time = (float)tryGetValue(node->GetBox(0), Value::Zero); \
			value.Type = VariantType(VariantType::graphType); \
			curve.Evaluate(*(type*)value.AsData, time, false); \
			break; \
		}
    SAMPLE_CURVE(12, FloatCurves, float, Float)
    SAMPLE_CURVE(13, Vector2Curves, Vector2, Vector2)
    SAMPLE_CURVE(14, Vector3Curves, Vector3, Vector3)
    SAMPLE_CURVE(15, Vector4Curves, Vector4, Vector4)
#undef SETUP_CURVE
        // Get Gameplay Global
    case 16:
    {
        const auto asset = node->Assets[0].As<GameplayGlobals>();
        if (asset)
        {
            const StringView& name = (StringView)node->Values[1];
            const auto e = asset->Variables.Find(name);
            value = e != asset->Variables.End() ? e->Value.Value : Value::Zero;
        }
        else
        {
            value = Value::Zero;
        }
        break;
    }
        // Platform Switch
    case 17:
    {
        int32 boxId = 1;
        switch (PLATFORM_TYPE)
        {
        case PlatformType::Windows:
            boxId = 2;
            break;
        case PlatformType::XboxOne:
            boxId = 3;
            break;
        case PlatformType::UWP:
            boxId = 4;
            break;
        case PlatformType::Linux:
            boxId = 5;
            break;
        case PlatformType::PS4:
            boxId = 6;
            break;
        case PlatformType::XboxScarlett:
            boxId = 7;
            break;
        default: ;
        }
        value = tryGetValue(node->GetBox(node->GetBox(boxId)->HasConnection() ? boxId : 1), Value::Zero);
        break;
    }
        // Asset Reference
    case 18:
    {
        value = Content::LoadAsync<Asset>((Guid)node->Values[0]);
        break;
    }
        // To String
    case 20:
    {
        value.SetString(tryGetValue(node->GetBox(1), Value(StringView::Empty)).ToString());
        break;
    }
        // Actor Reference
    case 21:
    {
        value = Scripting::FindObject<Actor>((Guid)node->Values[0]);
        break;
    }
    default:
        break;
    }
}

void VisjectExecutor::ProcessGroupBoolean(Box* box, Node* node, Value& value)
{
    switch (node->TypeID)
    {
        // NOT
    case 1:
    {
        const bool a = (bool)tryGetValue(node->GetBox(0), Value::False);
        value = !a;
        break;
    }
        // AND, OR, XOR, NOR, NAND
    case 2:
    case 3:
    case 4:
    case 5:
    case 6:
    {
        const bool a = (bool)tryGetValue(node->GetBox(0), 0, node->Values[0]);
        const bool b = (bool)tryGetValue(node->GetBox(1), 1, node->Values[1]);
        bool result = false;
        switch (node->TypeID)
        {
            // AND
        case 2:
            result = a && b;
            break;
            // OR
        case 3:
            result = a || b;
            break;
            // XOR
        case 4:
            result = !a != !b;
            break;
            // NOR
        case 5:
            result = !(a || b);
            break;
            // NAND
        case 6:
            result = !(a && b);
            break;
        }
        value = result;
        break;
    }
    default:
        break;
    }
}

void VisjectExecutor::ProcessGroupBitwise(Box* box, Node* node, Value& value)
{
    switch (node->TypeID)
    {
        // NOT
    case 1:
    {
        const int32 a = (int32)tryGetValue(node->GetBox(0), Value(0));
        value = !a;
        break;
    }
        // AND, OR, XOR
    case 2:
    case 3:
    case 4:
    {
        const int32 a = (int32)tryGetValue(node->GetBox(0), 0, node->Values[0]);
        const int32 b = (int32)tryGetValue(node->GetBox(1), 1, node->Values[1]);
        int32 result = 0;
        switch (node->TypeID)
        {
            // AND
        case 2:
            result = a & b;
            break;
            // OR
        case 3:
            result = a | b;
            break;
            // XOR
        case 4:
            result = a ^ b;
            break;
        }
        value = result;
        break;
    }
    default:
        break;
    }
}

void VisjectExecutor::ProcessGroupComparisons(Box* box, Node* node, Value& value)
{
    switch (node->TypeID)
    {
        // ==, !=, >, <=, >=
    case 1:
    case 2:
    case 3:
    case 4:
    case 5:
    case 6:
    {
        const Value a = tryGetValue(node->GetBox(0), 0, node->Values[0]);
        const Value b = tryGetValue(node->GetBox(1), 1, node->Values[1]).Cast(a.Type);
        bool result = false;
        switch (node->TypeID)
        {
        case 1:
            result = a == b;
            break;
        case 2:
            result = a != b;
            break;
        case 3:
            result = a > b;
            break;
        case 4:
            result = a < b;
            break;
        case 5:
            result = a <= b;
            break;
        case 6:
            result = a >= b;
            break;
        }
        value = result;
        break;
    }
        // Switch On Bool
    case 7:
    {
        const Value condition = tryGetValue(node->GetBox(0), Value::False);
        if (condition)
            value = tryGetValue(node->GetBox(2), 1, Value::Zero);
        else
            value = tryGetValue(node->GetBox(1), 0, Value::Zero);
        break;
    }
    }
}

void VisjectExecutor::ProcessGroupParticles(Box* box, Node* node, Value& value)
{
    switch (node->TypeID)
    {
        // Random Float
    case 208:
    {
        value = RAND;
        break;
    }
        // Random Vector2
    case 209:
    {
        value = Vector2(RAND, RAND);
        break;
    }
        // Random Vector3
    case 210:
    {
        value = Vector3(RAND, RAND, RAND);
        break;
    }
        // Random Vector4
    case 211:
    {
        value = Vector4(RAND, RAND, RAND, RAND);
        break;
    }
        // Random Float Range
    case 213:
    {
        auto& a = node->Values[0].AsFloat;
        auto& b = node->Values[1].AsFloat;
        value = Math::Lerp(a, b, RAND);
        break;
    }
        // Random Vector2 Range
    case 214:
    {
        auto a = (Vector2)node->Values[0];
        auto b = (Vector2)node->Values[1];
        value = Vector2(
            Math::Lerp(a.X, b.X, RAND),
            Math::Lerp(a.Y, b.Y, RAND)
        );
        break;
    }
        // Random Vector3 Range
    case 215:
    {
        auto a = (Vector3)node->Values[0];
        auto b = (Vector3)node->Values[1];
        value = Vector3(
            Math::Lerp(a.X, b.X, RAND),
            Math::Lerp(a.Y, b.Y, RAND),
            Math::Lerp(a.Z, b.Z, RAND)
        );
        break;
    }
        // Random Vector4 Range
    case 216:
    {
        auto a = (Vector4)node->Values[0];
        auto b = (Vector4)node->Values[1];
        value = Vector4(
            Math::Lerp(a.X, b.X, RAND),
            Math::Lerp(a.Y, b.Y, RAND),
            Math::Lerp(a.Z, b.Z, RAND),
            Math::Lerp(a.W, b.W, RAND)
        );
        break;
    }
    default:
        break;
    }
}

VisjectExecutor::Value VisjectExecutor::tryGetValue(Box* box, int32 defaultValueBoxIndex, const Value& defaultValue)
{
    const auto parentNode = box->GetParent<Node>();
    if (box->HasConnection())
        return eatBox(parentNode, box->FirstConnection());
    if (parentNode->Values.Count() > defaultValueBoxIndex)
        return Value(parentNode->Values[defaultValueBoxIndex]);
    return defaultValue;
}

VisjectExecutor::Value VisjectExecutor::tryGetValue(Box* box, const Value& defaultValue)
{
    return box && box->HasConnection() ? eatBox(box->GetParent<Node>(), box->FirstConnection()) : defaultValue;
}
