var payloadJson = System.Text.Encoding.UTF8.GetString(System.Convert.FromBase64String("{payload_base64}"));
var payload = Newtonsoft.Json.Linq.JObject.Parse(payloadJson);
var assetPath = (string)payload["asset_path"];
var temporaryAssetPath = (string)payload["temporary_asset_path"];
var template = (Newtonsoft.Json.Linq.JObject)payload["template"];
var nodes = (Newtonsoft.Json.Linq.JArray)payload["nodes"];
var connections = (Newtonsoft.Json.Linq.JArray)payload["connections"];
var previousWindow = AmplifyShaderEditor.UIUtils.CurrentWindow;
var previousSelection = UnityEditor.Selection.activeObject;
AmplifyShaderEditor.AmplifyShaderEditorWindow win = null;
bool committed = false;
bool success = false;
bool stateRestored = false;
System.Exception failure = null;
try
{
    if ((int)payload["version"] != 1)
        throw new System.Exception("unsupported EditorGraphSpec version");
    string aseVersion = AmplifyShaderEditor.VersionInfo.StaticToString();
    if (aseVersion != "1.9.6.2")
        throw new System.Exception("unsupported ASE version: " + aseVersion);
    if (string.IsNullOrEmpty(assetPath) || !assetPath.StartsWith("Assets/") ||
        !assetPath.EndsWith(".shader") || assetPath.Contains("..") || assetPath.Contains("\\"))
        throw new System.Exception("unsafe target asset path");
    if (string.IsNullOrEmpty(temporaryAssetPath) || !temporaryAssetPath.StartsWith("Assets/") ||
        !temporaryAssetPath.EndsWith(".shader") || temporaryAssetPath.Contains("..") ||
        temporaryAssetPath.Contains("\\") || temporaryAssetPath == assetPath)
        throw new System.Exception("unsafe temporary asset path");
    string targetDirectory = System.IO.Path.GetDirectoryName(assetPath).Replace("\\", "/");
    string temporaryDirectory = System.IO.Path.GetDirectoryName(temporaryAssetPath).Replace("\\", "/");
    if (targetDirectory != temporaryDirectory ||
        !System.IO.Path.GetFileName(temporaryAssetPath).StartsWith("ASECLI-Temp-"))
        throw new System.Exception("temporary asset must be a controlled sibling of the target");
    if (UnityEditor.AssetDatabase.LoadAssetAtPath<UnityEngine.Object>(assetPath) != null ||
        System.IO.File.Exists(System.IO.Path.GetFullPath(assetPath)))
        throw new System.Exception("target asset already exists");
    if (UnityEditor.AssetDatabase.LoadAssetAtPath<UnityEngine.Object>(temporaryAssetPath) != null ||
        System.IO.File.Exists(System.IO.Path.GetFullPath(temporaryAssetPath)))
        throw new System.Exception("temporary asset already exists");

    var publicInstance = System.Reflection.BindingFlags.Public | System.Reflection.BindingFlags.Instance;
    var publicStatic = System.Reflection.BindingFlags.Public | System.Reflection.BindingFlags.Static;
    var privateInstance = System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Instance;
    var createTemplateMethod = typeof(AmplifyShaderEditor.AmplifyShaderEditorWindow).GetMethod(
        "CreateNewTemplateShader", publicStatic, null,
        new System.Type[] { typeof(string), typeof(string), typeof(string) }, null);
    var createNodeMethod = typeof(AmplifyShaderEditor.AmplifyShaderEditorWindow).GetMethod(
        "CreateNode", publicInstance, null,
        new System.Type[] { typeof(System.Type), typeof(UnityEngine.Vector2), typeof(AmplifyShaderFunction), typeof(bool) }, null);
    var createConnectionMethod = typeof(AmplifyShaderEditor.ParentGraph).GetMethod(
        "CreateConnection", publicInstance, null,
        new System.Type[] { typeof(int), typeof(int), typeof(int), typeof(int), typeof(bool) }, null);
    var saveMethod = typeof(AmplifyShaderEditor.AmplifyShaderEditorWindow).GetMethod(
        "SaveToDisk", publicInstance, null, new System.Type[] { typeof(bool) }, null);
    var loadMethod = typeof(AmplifyShaderEditor.AmplifyShaderEditorWindow).GetMethod(
        "LoadFromDisk", publicInstance, null,
        new System.Type[] { typeof(string), typeof(AmplifyShaderFunction) }, null);
    var expressionNameField = typeof(AmplifyShaderEditor.CustomExpressionNode).GetField("m_customExpressionName", privateInstance);
    var expressionItemsField = typeof(AmplifyShaderEditor.CustomExpressionNode).GetField("m_items", privateInstance);
    var expressionCodeField = typeof(AmplifyShaderEditor.CustomExpressionNode).GetField("m_code", privateInstance);
    var expressionOutputField = typeof(AmplifyShaderEditor.CustomExpressionNode).GetField("m_outputTypeIdx", privateInstance);
    var expressionFunctionField = typeof(AmplifyShaderEditor.CustomExpressionNode).GetField("m_functionMode", privateInstance);
    var rawPropertyNameMethod = typeof(AmplifyShaderEditor.PropertyNode).GetMethod(
        "SetRawPropertyName", publicInstance, null, new System.Type[] { typeof(string) }, null);
    var propertyInspectorProperty = typeof(AmplifyShaderEditor.PropertyNode).GetProperty(
        "PropertyInspectorName", publicInstance);
    var precisionField = typeof(AmplifyShaderEditor.ParentNode).GetField(
        "m_currentPrecisionType", privateInstance);
    var rangedMinField = typeof(AmplifyShaderEditor.RangedFloatNode).GetField(
        "m_min", privateInstance);
    var rangedMaxField = typeof(AmplifyShaderEditor.RangedFloatNode).GetField(
        "m_max", privateInstance);
    if (createTemplateMethod == null || createNodeMethod == null || createConnectionMethod == null ||
        saveMethod == null || loadMethod == null || expressionNameField == null ||
        expressionItemsField == null || expressionCodeField == null ||
        expressionOutputField == null || expressionFunctionField == null ||
        rawPropertyNameMethod == null || propertyInspectorProperty == null ||
        propertyInspectorProperty.GetSetMethod() == null || precisionField == null ||
        rangedMinField == null || rangedMaxField == null)
        throw new System.Exception("required ASE API member is missing");

    var primitiveTypes = new System.Collections.Generic.Dictionary<string, System.Type> {
        { "add", typeof(AmplifyShaderEditor.SimpleAddOpNode) }, { "sub", typeof(AmplifyShaderEditor.SimpleSubtractOpNode) },
        { "mul", typeof(AmplifyShaderEditor.SimpleMultiplyOpNode) }, { "div", typeof(AmplifyShaderEditor.SimpleDivideOpNode) },
        { "min", typeof(AmplifyShaderEditor.SimpleMinOpNode) }, { "max", typeof(AmplifyShaderEditor.SimpleMaxOpNode) },
        { "remainder", typeof(AmplifyShaderEditor.SimpleRemainderNode) }, { "saturate", typeof(AmplifyShaderEditor.SaturateNode) },
        { "one_minus", typeof(AmplifyShaderEditor.OneMinusNode) }, { "negate", typeof(AmplifyShaderEditor.NegateNode) },
        { "abs", typeof(AmplifyShaderEditor.AbsOpNode) }, { "floor", typeof(AmplifyShaderEditor.FloorOpNode) },
        { "ceil", typeof(AmplifyShaderEditor.CeilOpNode) }, { "fract", typeof(AmplifyShaderEditor.FractNode) },
        { "round", typeof(AmplifyShaderEditor.RoundOpNode) }, { "trunc", typeof(AmplifyShaderEditor.TruncOpNode) },
        { "sign", typeof(AmplifyShaderEditor.SignOpNode) }, { "sqrt", typeof(AmplifyShaderEditor.SqrtOpNode) },
        { "rsqrt", typeof(AmplifyShaderEditor.RSqrtOpNode) }, { "rcp", typeof(AmplifyShaderEditor.ReciprocalOpNode) },
        { "normalize", typeof(AmplifyShaderEditor.NormalizeNode) }, { "length", typeof(AmplifyShaderEditor.LengthOpNode) },
        { "clamp", typeof(AmplifyShaderEditor.ClampOpNode) }, { "step", typeof(AmplifyShaderEditor.StepOpNode) },
        { "smoothstep", typeof(AmplifyShaderEditor.SmoothstepOpNode) }, { "lerp", typeof(AmplifyShaderEditor.LerpOp) },
        { "pow", typeof(AmplifyShaderEditor.PowerNode) }, { "exp", typeof(AmplifyShaderEditor.ExpOpNode) },
        { "exp2", typeof(AmplifyShaderEditor.Exp2OpNode) }, { "log", typeof(AmplifyShaderEditor.LogOpNode) },
        { "log2", typeof(AmplifyShaderEditor.Log2OpNode) }, { "log10", typeof(AmplifyShaderEditor.Log10OpNode) },
        { "sin", typeof(AmplifyShaderEditor.SinOpNode) }, { "cos", typeof(AmplifyShaderEditor.CosOpNode) },
        { "tan", typeof(AmplifyShaderEditor.TanOpNode) }, { "asin", typeof(AmplifyShaderEditor.ASinOpNode) },
        { "acos", typeof(AmplifyShaderEditor.ACosOpNode) }, { "atan", typeof(AmplifyShaderEditor.ATanOpNode) },
        { "atan2", typeof(AmplifyShaderEditor.ATan2OpNode) }, { "sinh", typeof(AmplifyShaderEditor.SinhOpNode) },
        { "cosh", typeof(AmplifyShaderEditor.CoshOpNode) }, { "tanh", typeof(AmplifyShaderEditor.TanhOpNode) },
        { "radians", typeof(AmplifyShaderEditor.RadiansOpNode) }, { "degrees", typeof(AmplifyShaderEditor.DegreesOpNode) },
        { "fmod", typeof(AmplifyShaderEditor.FmodOpNode) }, { "remap", typeof(AmplifyShaderEditor.TFHCRemapNode) },
        { "dot", typeof(AmplifyShaderEditor.DotProductOpNode) }, { "cross", typeof(AmplifyShaderEditor.CrossProductOpNode) },
        { "distance", typeof(AmplifyShaderEditor.DistanceOpNode) }, { "reflect", typeof(AmplifyShaderEditor.ReflectOpNode) },
        { "refract", typeof(AmplifyShaderEditor.RefractOpVec) }, { "rotate_axis", typeof(AmplifyShaderEditor.RotateAboutAxisNode) },
        { "append", typeof(AmplifyShaderEditor.DynamicAppendNode) }, { "swizzle", typeof(AmplifyShaderEditor.SwizzleNode) },
        { "component_mask", typeof(AmplifyShaderEditor.ComponentMaskNode) }, { "split", typeof(AmplifyShaderEditor.BreakToComponentsNode) },
        { "luminance", typeof(AmplifyShaderEditor.LuminanceNode) }, { "desaturate", typeof(AmplifyShaderEditor.DesaturateOpNode) },
        { "contrast", typeof(AmplifyShaderEditor.SimpleContrastOpNode) }, { "posterize", typeof(AmplifyShaderEditor.PosterizeNode) },
        { "rgb_to_hsv", typeof(AmplifyShaderEditor.RGBToHSVNode) }, { "hsv_to_rgb", typeof(AmplifyShaderEditor.HSVToRGBNode) },
        { "gamma_to_linear", typeof(AmplifyShaderEditor.GammaToLinearNode) }, { "linear_to_gamma", typeof(AmplifyShaderEditor.LinearToGammaNode) },
        { "not", typeof(AmplifyShaderEditor.NotNode) }, { "nand", typeof(AmplifyShaderEditor.NandNode) },
        { "and", typeof(AmplifyShaderEditor.AllOpNode) }, { "or", typeof(AmplifyShaderEditor.AnyOpNode) },
        { "select", typeof(AmplifyShaderEditor.StaticSwitch) }, { "texcoord", typeof(AmplifyShaderEditor.TextureCoordinatesNode) },
        { "world_position", typeof(AmplifyShaderEditor.WorldPosInputsNode) }, { "time", typeof(AmplifyShaderEditor.SimpleTimeNode) },
        { "vertex_color", typeof(AmplifyShaderEditor.VertexColorNode) }, { "vertex_id", typeof(AmplifyShaderEditor.VertexIdVariableNode) },
        { "instance_id", typeof(AmplifyShaderEditor.InstanceIdNode) }, { "world_normal", typeof(AmplifyShaderEditor.WorldNormalVector) },
    };
    System.Func<string, System.Type> primitiveClass = delegate (string op) {
        System.Type found = null;
        primitiveTypes.TryGetValue(op, out found);
        return found;
    };
    System.Func<string, System.Type> constClass = delegate (string value) {
        string s = value.Trim();
        if (System.Text.RegularExpressions.Regex.IsMatch(s, @"^[+-]?(\d+(\.\d*)?|\.\d+)([eE][+-]?\d+)?$"))
            return typeof(AmplifyShaderEditor.RangedFloatNode);
        if (s.StartsWith("float2")) return typeof(AmplifyShaderEditor.Vector2Node);
        if (s.StartsWith("float3")) return typeof(AmplifyShaderEditor.Vector3Node);
        if (s.StartsWith("float4")) return typeof(AmplifyShaderEditor.Vector4Node);
        return null;
    };
    System.Func<string, float[]> parseFloats = delegate (string value) {
        int a = value.IndexOf('(');
        int b = value.IndexOf(')');
        if (a < 0 || b < 0 || b <= a) return new float[0];
        string[] parts = value.Substring(a + 1, b - a - 1).Split(',');
        float[] result = new float[parts.Length];
        for (int i = 0; i < parts.Length; i++)
            result[i] = float.Parse(parts[i].Trim(), System.Globalization.CultureInfo.InvariantCulture);
        return result;
    };
    System.Action<AmplifyShaderEditor.ParentNode, string> writeConst = delegate (AmplifyShaderEditor.ParentNode node, string value) {
        if (node is AmplifyShaderEditor.RangedFloatNode)
            ((AmplifyShaderEditor.RangedFloatNode)node).Value = float.Parse(value.Trim(), System.Globalization.CultureInfo.InvariantCulture);
        else if (node is AmplifyShaderEditor.Vector2Node) { float[] f = parseFloats(value); ((AmplifyShaderEditor.Vector2Node)node).Value = new UnityEngine.Vector2(f[0], f[1]); }
        else if (node is AmplifyShaderEditor.Vector3Node) { float[] f = parseFloats(value); ((AmplifyShaderEditor.Vector3Node)node).Value = new UnityEngine.Vector3(f[0], f[1], f[2]); }
        else if (node is AmplifyShaderEditor.Vector4Node) { float[] f = parseFloats(value); ((AmplifyShaderEditor.Vector4Node)node).Value = new UnityEngine.Vector4(f[0], f[1], f[2], f[3]); }
    };

    string templateGuid = (string)template["guid"];
    string shaderName = (string)template["shader_name"];
    var shader = AmplifyShaderEditor.AmplifyShaderEditorWindow.CreateNewTemplateShader(
        templateGuid, temporaryAssetPath, shaderName + ".shader");
    win = AmplifyShaderEditor.UIUtils.CurrentWindow as AmplifyShaderEditor.AmplifyShaderEditorWindow;
    if (shader == null || win == null || win.CurrentGraph == null || win.CurrentGraph.CurrentMasterNode == null)
        throw new System.Exception("ASE template graph was not created");
    win.Show();
    var layoutEvent = new UnityEngine.Event();
    layoutEvent.type = UnityEngine.EventType.Layout;
    win.SendEvent(layoutEvent);
    var repaintEvent = new UnityEngine.Event();
    repaintEvent.type = UnityEngine.EventType.Repaint;
    win.SendEvent(repaintEvent);

    var graph = win.CurrentGraph;
    var aliasIds = new System.Collections.Generic.Dictionary<string, int>();
    var recipePrimitiveIds = new System.Collections.Generic.Dictionary<string, System.Collections.Generic.Dictionary<string, int>>();
    var recipePrimitiveClasses = new System.Collections.Generic.Dictionary<string, System.Collections.Generic.Dictionary<string, string>>();
    var recipeOutputIds = new System.Collections.Generic.Dictionary<string, int>();
    var recipeInputTargets = new System.Collections.Generic.Dictionary<string, System.Collections.Generic.Dictionary<int, System.Collections.Generic.List<System.Tuple<int, int>>>>();
    int masterId = graph.CurrentMasterNode.UniqueId;
    foreach (Newtonsoft.Json.Linq.JObject nodeSpec in nodes)
