// Amplify Shader Editor - Visual Shader Editing Tool
// Copyright (c) Amplify Creations, Lda <info@amplify.pt>

using UnityEngine;
using UnityEditor;
using UnityEditor.Callbacks;
using System;
using System.Collections;
using System.Threading;
using System.Globalization;
using System.Collections.Generic;
using UnityEngine.Networking;

namespace AmplifyShaderEditor
{
	// Disabling Substance Deprecated warning

	public class AmplifyShaderEditorWindow : SearchableEditorWindow, ISerializationCallbackReceiver
	{
		public const string PreviewSizeGlobalVariable = "_ASEPreviewSize";

		public const double InactivitySaveTime = 1.0;

		public const string CopyCommand = "Copy";
		public const string PasteCommand = "Paste";
		public const string SelectAll = "SelectAll";
		public const string Duplicate = "Duplicate";
		public const string ObjectSelectorClosed = "ObjectSelectorClosed";
		public const string LiveShaderError = "Live Shader only works with an assigned Master Node on the graph";

		public const string AsyncMessage = "Detected Asynchronous Shader Compilation. This can cause slowdowns when saving not only ASE shaders but other shaders as well.\n" +
											"Please consider turning it off ( Project Settings > Editor > Asynchronous Shader Compilation ) if detecting big slowdowns on shader save.\n" +
											"This message can be turned off via Preferences > Amplify Shader Editor > Show Async Message.";
		//public Texture2D MasterNodeOnTexture = null;
		//public Texture2D MasterNodeOffTexture = null;

		//public Texture2D GPUInstancedOnTexture = null;
		//public Texture2D GPUInstancedOffTexture = null;

		private bool m_initialized = false;
		private bool m_checkInvalidConnections = false;
		private bool m_afterDeserializeFlag = true;


		[SerializeField]
		private ParentGraph m_customGraph = null;

		// UI 
		private Rect m_graphArea;
		private Texture2D m_graphBgTexture;
		private Texture2D m_graphFgTexture;
		private GUIStyle m_graphFontStyle;
		//private GUIStyle _borderStyle;
		private Texture2D m_wireTexture;

		[SerializeField]
		TemplatesManager m_templatesManager;

		[SerializeField]
		private InnerWindowEditorVariables m_innerEditorVariables;

		[SerializeField]
		private string m_lastpath;

		[SerializeField]
		private ASESelectionMode m_selectionMode = ASESelectionMode.Shader;

		[SerializeField]
		private DuplicatePreventionBuffer m_duplicatePreventionBuffer;

		[SerializeField]
		private double m_inactivityTime = 0;

		// Prevent save ops every tick when on live mode
		[SerializeField]
		private double m_lastTimeSaved = 0;

		[SerializeField]
		private bool m_cacheSaveOp = false;
		private const double SaveTime = 1;

		private bool m_markedToSave = false;

		// Graph logic
		[SerializeField]
		private ParentGraph m_mainGraphInstance;

		// Camera control
		[SerializeField]
		private Vector2 m_cameraOffset;

		private float m_cameraSpeed = 1;

		private Rect m_cameraInfo;

		[SerializeField]
		private float m_cameraZoom;

		[SerializeField]
		private Vector2 m_minNodePos;

		[SerializeField]
		private Vector2 m_maxNodePos;

		[SerializeField]
		private bool m_isDirty;

		[SerializeField]
		private bool m_saveIsDirty;

		[SerializeField]
		private bool m_repaintIsDirty;

		[SerializeField]
		private bool m_liveShaderEditing = false;

		[SerializeField]
		private bool m_shaderIsModified = false;

		[SerializeField]
		private string m_lastOpenedLocation = string.Empty;

		[SerializeField]
		private bool m_zoomChanged = true;

		[SerializeField]
		private float m_lastWindowWidth = 0;

		[SerializeField]
		private int m_graphCount = 0;

		[SerializeField]
		private double m_currentInactiveTime = 0;

		private bool m_ctrlSCallback = false;

		private bool m_altBoxSelection = false;
		private bool m_altDragStarted = false;
		private bool m_altPressDown = false;
		private bool m_altAvailable = true;

		// Events
		private Vector3 m_currentMousePos;
		private Vector2 m_keyEvtMousePos2D;
		private Vector2 m_currentMousePos2D;
		private Event m_currentEvent;
		private string m_currentCommandName = string.Empty;
		private bool m_insideEditorWindow;

		private bool m_lostFocus = false;
		// Selection box for multiple node selection 
		private bool m_multipleSelectionActive = false;
		private bool m_lmbPressed = false;
		private Vector2 m_multipleSelectionStart;
		private Rect m_multipleSelectionArea = new Rect( 0, 0, 0, 0 );
		private bool m_autoPanDirActive = false;
		private bool m_forceAutoPanDir = false;
		private bool m_refreshOnUndo = false;
		private bool m_loadShaderOnSelection = false;
		private bool m_refreshAvailableNodes = false;
		private double m_time;

		//Context Menu
		private Vector2 m_rmbStartPos;
		private Vector2 m_altKeyStartPos;
		private GraphContextMenu m_contextMenu;
		private ShortcutsManager m_shortcutManager;

		[SerializeField]
		private NodeAvailability m_currentNodeAvailability = NodeAvailability.SurfaceShader;
		//Clipboard
		private Clipboard m_clipboard;

		//Node Parameters Window
		[SerializeField]
		private bool m_nodeParametersWindowMaximized = true;
		private NodeParametersWindow m_nodeParametersWindow;

		// Tools Window
		private ToolsWindow m_toolsWindow;

		private ConsoleLogWindow m_consoleLogWindow;

		//Editor Options
		private OptionsWindow m_optionsWindow;

		// Mode Window
		private ShaderEditorModeWindow m_modeWindow;

		// Tools Window
		private TipsWindow m_tipsWindow;

		//Palette Window
		[SerializeField]
		private bool m_paletteWindowMaximized = true;
		private PaletteWindow m_paletteWindow;

		private ContextPalette m_contextPalette;
		private PalettePopUp m_palettePopup;
		private System.Type m_paletteChosenType;
		private AmplifyShaderFunction m_paletteChosenFunction;

		// In-Editor Message System
		GenericMessageUI m_genericMessageUI;
		private GUIContent m_genericMessageContent;

		// Drag&Drop Tool 
		private DragAndDropTool m_dragAndDropTool;

		//Custom Styles
		//private CustomStylesContainer m_customStyles;

		private AmplifyShaderFunction m_previousShaderFunction;

		private List<MenuParent> m_registeredMenus;

		private PreMadeShaders m_preMadeShaders;

		private AutoPanData[] m_autoPanArea;

		private DrawInfo m_drawInfo;
		private KeyCode m_lastKeyPressed = KeyCode.None;
		private System.Type m_commentaryTypeNode;

		private int m_onLoadDone = 0;

		private float m_copyPasteDeltaMul = 0;
		private Vector2 m_copyPasteInitialPos = Vector2.zero;
		private Vector2 m_copyPasteDeltaPos = Vector2.zero;

		private int m_repaintCount = 0;
		private bool m_forceUpdateFromMaterialFlag = false;

		private UnityEngine.Object m_delayedLoadObject = null;
		private double m_focusOnSelectionTimestamp;
		private double m_focusOnMasterNodeTimestamp;
		private double m_wiredDoubleTapTimestamp;

		private bool m_globalPreview = false;
		private bool m_globalShowInternalData = true;

		private const double AutoZoomTime = 0.25;
		private const double ToggleTime = 0.25;
		private const double WiredDoubleTapTime = 0.25;
		private const double DoubleClickTime = 0.25;

		private Material m_delayedMaterialSet = null;

		private bool m_mouseDownOnValidArea = false;

		private bool m_removedKeyboardFocus = false;

		private int m_lastHotControl = -1;

		[SerializeField]
		private bool m_isShaderFunctionWindow = false;

		private string m_currentTitle = string.Empty;
		private bool m_currentTitleMod = false;

		//private Material m_maskingMaterial = null;
		//private int m_cachedProjectInLinearId = -1;
		private int m_cachedEditorTimeId = -1;
		private int m_cachedEditorDeltaTimeId = -1;
		//private float m_repaintFrequency = 15;
		//private double m_repaintTimestamp = 0;

		// Smooth Zoom
		private bool m_smoothZoom = false;
		private float m_targetZoom;
		private double m_zoomTime;
		private Vector2 m_zoomPivot;
		private float m_targetZoomIncrement;
		private float m_zoomVelocity = 0;

		// Smooth Pan
		private bool m_smoothOffset = false;
		private double m_offsetTime;
		private Vector2 m_targetOffset;
		private Vector2 m_camVelocity = Vector2.zero;

		// Auto-Compile samples 
		private bool m_forcingMaterialUpdateFlag = false;
		private bool m_forcingMaterialUpdateOp = false;
		private List<Material> m_materialsToUpdate = new List<Material>();

		private NodeExporterUtils m_nodeExporterUtils;
		private bool m_performFullUndoRegister = true;

		[SerializeField]
		private AmplifyShaderFunction m_openedShaderFunction;

		[SerializeField]
		private bool m_openedAssetFromNode = false;

		private bool m_nodesLoadedCorrectly = false;
		private GUIContent NodesExceptionMessage = new GUIContent( "ASE is unable to load correctly due to some faulty other classes/plugin in your project. We advise to review all your imported plugins." );


		private bool m_outdatedShaderFromTemplateLoaded = false;
		private bool m_replaceMasterNode = false;
		private AvailableShaderTypes m_replaceMasterNodeType;
		private string m_replaceMasterNodeData;
		private bool m_replaceMasterNodeDataFromCache;
		private NodeWireReferencesUtils m_wireReferenceUtils = new NodeWireReferencesUtils();

		private ParentNode m_nodeToFocus = null;
		private float m_zoomToFocus = 1.0f;
		private bool m_selectNodeToFocus = true;

		[NonSerialized]
		public Dictionary<string, bool> VisitedChanged = new Dictionary<string, bool>();

		[SerializeField]
		private List<Toast> m_messages = new List<Toast>();

		[SerializeField]
		private float m_maxMsgWidth = 100;

		[SerializeField]
		private bool m_maximizeMessages = false;

		[NonSerialized]
		private Dictionary<string, OutputPort> m_savedList = new Dictionary<string, OutputPort>();

		public int m_frameCounter = 0;
		public double m_fpsTime = 0;
		public string m_fpsDisplay = string.Empty;

#if UNITY_EDITOR_WIN
		// ScreenShot vars
		IntPtr m_aseHandle;
		private Rect m_prevWindowRect;
		private Vector2 m_prevCameraOffset;
		private float m_prevCameraZoom;
		private bool m_openSavedFolder = false;
		private bool m_takeScreenShot = false;
#endif
		public bool CheckFunctions = false;

		// Unity Menu item
		[MenuItem( "Window/Amplify Shader Editor/Open Canvas", false, 1000 )]
		static void OpenMainShaderGraph()
		{
			if( IOUtils.AllOpenedWindows.Count > 0 )
			{
				AmplifyShaderEditorWindow currentWindow = CreateTab( "Empty", UIUtils.ShaderIcon );
				UIUtils.CurrentWindow = currentWindow;
				currentWindow.CreateNewGraph( "Empty" );
				currentWindow.Show();
			}
			else
			{
				AmplifyShaderEditorWindow currentWindow = OpenWindow( "Empty", UIUtils.ShaderIcon );
				currentWindow.CreateNewGraph( "Empty" );
				//currentWindow.Show();
			}
		}

		public static string GenerateTabTitle( string original, bool modified = false )
		{
			GUIContent content = new GUIContent( original );
			GUIStyle tabStyle = new GUIStyle( (GUIStyle)"dragtabdropwindow" );//  GUI.skin.FindStyle( "dragtabdropwindow" );
			string finalTitle = string.Empty;
			bool addEllipsis = false;
			for( int i = 1; i <= original.Length; i++ )
			{
				content.text = original.Substring( 0, i );
				Vector2 titleSize = tabStyle.CalcSize( content );
				int maxSize = modified ? 62 : 69;
				if( titleSize.x > maxSize )
				{
					addEllipsis = true;
					break;
				}
				else
				{
					finalTitle = content.text;
				}
			}
			if( addEllipsis )
				finalTitle += "..";
			if( modified )
				finalTitle += "*";
			return finalTitle;
		}

		public static void ConvertShaderToASE( Shader shader )
		{
			if( UIUtils.IsUnityNativeShader( shader ) )
			{
				Debug.LogWarningFormat( "Action not allowed. Attempting to load the native {0} shader into Amplify Shader Editor", shader.name );
				return;
			}

			string guid = AssetDatabase.AssetPathToGUID( AssetDatabase.GetAssetPath( shader ) );
			if( IOUtils.AllOpenedWindows.Count > 0 )
			{
				AmplifyShaderEditorWindow openedTab = null;
				for( int i = 0; i < IOUtils.AllOpenedWindows.Count; i++ )
				{
					//if( AssetDatabase.GetAssetPath( shader ).Equals( IOUtils.AllOpenedWindows[ i ].LastOpenedLocation ) )
					if( guid.Equals( IOUtils.AllOpenedWindows[ i ].GUID ) )
					{
						openedTab = IOUtils.AllOpenedWindows[ i ];
						break;
					}
				}

				if( openedTab != null )
				{
					openedTab.wantsMouseMove = true;
					openedTab.ShowTab();
					UIUtils.CurrentWindow = openedTab;
				}
				else
				{
					EditorWindow openedWindow = AmplifyShaderEditorWindow.GetWindow<AmplifyShaderEditorWindow>();
					AmplifyShaderEditorWindow currentWindow = CreateTab();
					WindowHelper.AddTab( openedWindow, currentWindow );
					UIUtils.CurrentWindow = currentWindow;
				}
			}
			else
			{
				AmplifyShaderEditorWindow currentWindow = OpenWindow( shader.name, UIUtils.ShaderIcon );
				UIUtils.CurrentWindow = currentWindow;
			}

			if( IOUtils.IsASEShader( shader ) )
			{
				UIUtils.CurrentWindow.LoadProjectSelected( shader );
			}
			else
			{
				UIUtils.CreateEmptyFromInvalid( shader );
				UIUtils.ShowMessage( "Trying to open shader not created on ASE!\nBEWARE, old data will be lost if saving it here!", MessageSeverity.Warning );
				if( UIUtils.CurrentWindow.LiveShaderEditing )
				{
					UIUtils.ShowMessage( "Disabling Live Shader Editing. Must manually re-enable it.", MessageSeverity.Warning );
					UIUtils.CurrentWindow.LiveShaderEditing = false;
				}
			}
		}

		public static void LoadMaterialToASE( Material material )
		{
			string guid = AssetDatabase.AssetPathToGUID( AssetDatabase.GetAssetPath( material.shader ) );

			if( IOUtils.AllOpenedWindows.Count > 0 )
			{
				AmplifyShaderEditorWindow openedTab = null;
				for( int i = 0; i < IOUtils.AllOpenedWindows.Count; i++ )
				{
					//if( AssetDatabase.GetAssetPath( material.shader ).Equals( IOUtils.AllOpenedWindows[ i ].LastOpenedLocation ) )
					if( guid.Equals( IOUtils.AllOpenedWindows[ i ].GUID ) )
					{
						openedTab = IOUtils.AllOpenedWindows[ i ];
						break;
					}
				}

				if( openedTab != null )
				{
					openedTab.wantsMouseMove = true;
					openedTab.ShowTab();
					UIUtils.CurrentWindow = openedTab;
				}
				else
				{
					EditorWindow openedWindow = AmplifyShaderEditorWindow.GetWindow<AmplifyShaderEditorWindow>();
					AmplifyShaderEditorWindow currentWindow = CreateTab();
					WindowHelper.AddTab( openedWindow, currentWindow );
					UIUtils.CurrentWindow = currentWindow;
				}
			}
			else
			{
				AmplifyShaderEditorWindow currentWindow = OpenWindow( material.name, UIUtils.MaterialIcon );
				UIUtils.CurrentWindow = currentWindow;
			}

			if( IOUtils.IsASEShader( material.shader ) )
			{
				UIUtils.CurrentWindow.LoadProjectSelected( material );
			}
			else
			{
				UIUtils.CreateEmptyFromInvalid( material.shader );
				UIUtils.SetDelayedMaterialMode( material );
			}
		}

		public static void LoadShaderFunctionToASE( AmplifyShaderFunction shaderFunction, bool openedAssetFromNode )
		{
			string guid = AssetDatabase.AssetPathToGUID( AssetDatabase.GetAssetPath( shaderFunction ) );

			if( IOUtils.AllOpenedWindows.Count > 0 )
			{
				AmplifyShaderEditorWindow openedTab = null;
				for( int i = 0; i < IOUtils.AllOpenedWindows.Count; i++ )
				{
					//if( AssetDatabase.GetAssetPath( shaderFunction ).Equals( IOUtils.AllOpenedWindows[ i ].LastOpenedLocation ) )
					if( guid.Equals( IOUtils.AllOpenedWindows[ i ].GUID ) )
					{
						openedTab = IOUtils.AllOpenedWindows[ i ];
						break;
					}
				}

				if( openedTab != null )
				{
					openedTab.wantsMouseMove = true;
					openedTab.ShowTab();
					UIUtils.CurrentWindow = openedTab;
				}
				else
				{
					EditorWindow openedWindow = AmplifyShaderEditorWindow.GetWindow<AmplifyShaderEditorWindow>();
					AmplifyShaderEditorWindow currentWindow = CreateTab();
					WindowHelper.AddTab( openedWindow, currentWindow );
					UIUtils.CurrentWindow = currentWindow;
				}
			}
			else
			{
				AmplifyShaderEditorWindow currentWindow = OpenWindow( shaderFunction.FunctionName, UIUtils.ShaderFunctionIcon );
				UIUtils.CurrentWindow = currentWindow;
			}

			UIUtils.CurrentWindow.OpenedAssetFromNode = openedAssetFromNode;
			if( IOUtils.IsShaderFunction( shaderFunction.FunctionInfo ) )
			{
				UIUtils.CurrentWindow.LoadProjectSelected( shaderFunction );
			}
			else
			{
				UIUtils.CurrentWindow.titleContent.text = GenerateTabTitle( shaderFunction.FunctionName );
				UIUtils.CurrentWindow.titleContent.image = UIUtils.ShaderFunctionIcon;
				UIUtils.CreateEmptyFunction( shaderFunction );
			}
		}

		public static void LoadAndSaveList( string[] assetList )
		{
			EditorPrefs.SetString( "ASEfileList", string.Join( ",", assetList ) );
			if( assetList[ 0 ].EndsWith( ".asset" ) )
			{
				var obj = AssetDatabase.LoadAssetAtPath<AmplifyShaderFunction>( assetList[ 0 ] );
				AmplifyShaderEditorWindow.LoadShaderFunctionToASE( obj, false );
			}
			else
			{
				var obj = AssetDatabase.LoadAssetAtPath<Shader>( assetList[ 0 ] );
				AmplifyShaderEditorWindow.ConvertShaderToASE( obj );
			}

			UIUtils.CurrentWindow.State = AmplifyShaderEditorWindow.OpenSaveState.OPEN;
			UIUtils.CurrentWindow.Repaint();
		}

		public static AmplifyShaderEditorWindow OpenWindow( string title = null, Texture icon = null )
		{
			AmplifyShaderEditorWindow currentWindow = (AmplifyShaderEditorWindow)AmplifyShaderEditorWindow.GetWindow( typeof( AmplifyShaderEditorWindow ), false );
			currentWindow.minSize = new Vector2( ( Constants.MINIMIZE_WINDOW_LOCK_SIZE - 150 ), 270 );
			currentWindow.wantsMouseMove = true;
			if( title != null )
				currentWindow.titleContent.text = GenerateTabTitle( title );
			if( icon != null )
				currentWindow.titleContent.image = icon;
			return currentWindow;
		}

		public static AmplifyShaderEditorWindow CreateTab( string title = null, Texture icon = null )
		{
			AmplifyShaderEditorWindow currentWindow = EditorWindow.CreateInstance<AmplifyShaderEditorWindow>();
			currentWindow.minSize = new Vector2( ( Constants.MINIMIZE_WINDOW_LOCK_SIZE - 150 ), 270 );
			currentWindow.wantsMouseMove = true;
			if( title != null )
				currentWindow.titleContent.text = GenerateTabTitle( title );
			if( icon != null )
				currentWindow.titleContent.image = icon;
			return currentWindow;
		}

		public double CalculateInactivityTime()
		{
			double currTime = EditorApplication.timeSinceStartup;
			switch( Event.current.type )
			{
				case EventType.MouseDown:
				case EventType.MouseUp:
				//case EventType.MouseMove:
				case EventType.MouseDrag:
				case EventType.KeyDown:
				case EventType.KeyUp:
				case EventType.ScrollWheel:
				case EventType.DragUpdated:
				case EventType.DragPerform:
				case EventType.DragExited:
				case EventType.ValidateCommand:
				case EventType.ExecuteCommand:
				{
					m_inactivityTime = currTime;
					return 0;
				}
			}
			return currTime - m_inactivityTime;
		}

		public void ActivatePreviews( bool value )
		{
			m_mainGraphInstance.ActivatePreviews( value );
		}

		// Shader Graph window
		public override void OnEnable()
		{
			base.OnEnable();

			Preferences.LoadDefaults();

#if UNITY_2018_3_OR_NEWER
			ASEPackageManagerHelper.RequestInfo();
			ASEPackageManagerHelper.Update();
#endif

			Shader.SetGlobalVector( PreviewSizeGlobalVariable, new Vector4( Constants.PreviewSize , Constants.PreviewSize , 0, 0 ) );

			if( m_templatesManager == null )
			{
				m_templatesManager = IOUtils.FirstValidTemplatesManager;
				if( m_templatesManager == null )
				{
					m_templatesManager = ScriptableObject.CreateInstance<TemplatesManager>();
					m_templatesManager.Init();
					if( TemplatesManager.ShowDebugMessages )
						Debug.Log( "Creating Manager" );
				}
				else
				{
					if( TemplatesManager.ShowDebugMessages )
						Debug.Log( "Assigning Manager" );
				}
			}
			else if( !m_templatesManager.Initialized )
			{
				if( TemplatesManager.ShowDebugMessages )
					Debug.Log( "Re-Initializing Manager" );
				m_templatesManager.Init();
			}
            TemplatePostProcessor.Destroy();
			if( m_innerEditorVariables == null )
			{
				m_innerEditorVariables = new InnerWindowEditorVariables();
				m_innerEditorVariables.Initialize();
			}

			if( m_mainGraphInstance == null )
			{
				m_mainGraphInstance = CreateInstance<ParentGraph>();
				m_mainGraphInstance.Init();
				m_mainGraphInstance.ParentWindow = this;
				m_mainGraphInstance.SetGraphId( 0 );
			}
			m_mainGraphInstance.ResetEvents();
			m_mainGraphInstance.OnNodeEvent += OnNodeStoppedMovingEvent;
			m_mainGraphInstance.OnMaterialUpdatedEvent += OnMaterialUpdated;
			m_mainGraphInstance.OnShaderUpdatedEvent += OnShaderUpdated;
			m_mainGraphInstance.OnEmptyGraphDetectedEvt += OnEmptyGraphDetected;
			m_mainGraphInstance.OnNodeRemovedEvent += m_toolsWindow.OnNodeRemovedFromGraph;
			GraphCount = 1;

			IOUtils.Init();
			IOUtils.AllOpenedWindows.Add( this );

			// Only runs once for multiple windows
			EditorApplication.update -= IOUtils.UpdateIO;
			EditorApplication.update += IOUtils.UpdateIO;

			//EditorApplication.update -= UpdateTime;
			EditorApplication.update -= UpdateNodePreviewListAndTime;
			//EditorApplication.update += UpdateTime;

			EditorApplication.update += UpdateNodePreviewListAndTime;


			if( CurrentSelection == ASESelectionMode.ShaderFunction )
			{
				IsShaderFunctionWindow = true;
			}
			else
			{
				IsShaderFunctionWindow = false;
			}

			m_optionsWindow = new OptionsWindow( this );
			m_optionsWindow.Init();

			m_contextMenu = new GraphContextMenu( m_mainGraphInstance );
			m_nodesLoadedCorrectly = m_contextMenu.CorrectlyLoaded;

			m_paletteWindow = new PaletteWindow( this )
			{
				Resizable = true
			};
			m_paletteWindow.OnPaletteNodeCreateEvt += OnPaletteNodeCreate;
			m_registeredMenus.Add( m_paletteWindow );

			m_contextPalette = new ContextPalette( this );
			m_contextPalette.OnPaletteNodeCreateEvt += OnContextPaletteNodeCreate;
			m_registeredMenus.Add( m_contextPalette );

			m_genericMessageUI = new GenericMessageUI();
			m_genericMessageUI.OnMessageDisplayEvent += ShowMessageImmediately;

			Selection.selectionChanged += OnProjectSelectionChanged;
#if UNITY_2018_1_OR_NEWER
			EditorApplication.projectChanged += OnProjectWindowChanged;
#else
			EditorApplication.projectWindowChanged += OnProjectWindowChanged;
#endif
			m_focusOnSelectionTimestamp = EditorApplication.timeSinceStartup;
			m_focusOnMasterNodeTimestamp = EditorApplication.timeSinceStartup;

			m_nodeParametersWindow.IsMaximized = m_innerEditorVariables.NodeParametersMaximized;
			if( DebugConsoleWindow.UseShaderPanelsInfo )
				m_nodeParametersWindow.IsMaximized = m_nodeParametersWindowMaximized;

			m_paletteWindow.IsMaximized = m_innerEditorVariables.NodePaletteMaximized;
			if( DebugConsoleWindow.UseShaderPanelsInfo )
				m_paletteWindow.IsMaximized = m_paletteWindowMaximized;

			m_shortcutManager = new ShortcutsManager();
			// REGISTER NODE SHORTCUTS
			foreach( KeyValuePair<KeyCode, ShortcutKeyData> kvp in m_contextMenu.NodeShortcuts )
			{
				m_shortcutManager.RegisterNodesShortcuts( kvp.Key, kvp.Value.Name );
			}

			// REGISTER EDITOR SHORTCUTS

			m_shortcutManager.RegisterEditorShortcut( true, EventModifiers.FunctionKey, KeyCode.F1, "Open Selected Node Wiki page", () =>
			{
				List<ParentNode> selectedNodes = m_mainGraphInstance.SelectedNodes;
				if( selectedNodes != null && selectedNodes.Count == 1 )
				{
					Application.OpenURL( selectedNodes[ 0 ].Attributes.NodeUrl );
				}
			} );


			m_shortcutManager.RegisterEditorShortcut( true, KeyCode.C, "Create Commentary", () =>
			{
				// Create commentary
				ParentNode[] selectedNodes = m_mainGraphInstance.SelectedNodes.ToArray();
				UIUtils.MarkUndoAction();
				Undo.RegisterCompleteObjectUndo( this, "Adding Commentary Node" );
				CommentaryNode node = m_mainGraphInstance.CreateNode( m_commentaryTypeNode, true, -1, false ) as CommentaryNode;
				node.CreateFromSelectedNodes( TranformedMousePos, selectedNodes );
				node.Focus();
				m_mainGraphInstance.DeSelectAll();
				m_mainGraphInstance.SelectNode( node, false, false );
				SetSaveIsDirty();
				ForceRepaint();
			} );


			m_shortcutManager.RegisterEditorShortcut( true, KeyCode.F, "Focus On Selection", () =>
			{
				OnToolButtonPressed( ToolButtonType.FocusOnSelection );
				ForceRepaint();
			} );

			//m_shortcutManager.RegisterEditorShortcut( true, EventModifiers.None, KeyCode.B, "New Master Node", () =>
			//{
			//	OnToolButtonPressed( ToolButtonType.MasterNode );
			//	ForceRepaint();
			//} );

			m_shortcutManager.RegisterEditorShortcut( true, EventModifiers.None, KeyCode.Space, "Open Node Palette", null, () =>
			{
				m_contextPalette.Show( m_currentMousePos2D, m_cameraInfo );
			} );


			m_shortcutManager.RegisterEditorShortcut( true, KeyCode.W, "Toggle Colored Line Mode", () =>
			{
				m_optionsWindow.ColoredPorts = !m_optionsWindow.ColoredPorts;
				ForceRepaint();

			} );

			m_shortcutManager.RegisterEditorShortcut( true, EventModifiers.Control, KeyCode.W, "Toggle Multi-Line Mode", () =>
			{
				m_optionsWindow.MultiLinePorts = !m_optionsWindow.MultiLinePorts;
				ForceRepaint();
			} );

			m_shortcutManager.RegisterEditorShortcut( true, KeyCode.P, "Global Preview", () =>
			{
				GlobalPreview = !GlobalPreview;
				EditorPrefs.SetBool( "GlobalPreview", GlobalPreview );

				ForceRepaint();
			} );

			GlobalShowInternalData = EditorPrefs.GetBool( "ASEGlobalShowInternalData", true );
			m_shortcutManager.RegisterEditorShortcut( true, KeyCode.I, "Global Show Internal Data", () =>
			{
				GlobalShowInternalData = !GlobalShowInternalData;
				EditorPrefs.SetBool( "ASEGlobalShowInternalData", GlobalShowInternalData );
				ForceRepaint();
			} );

			m_shortcutManager.RegisterEditorShortcut( true, EventModifiers.FunctionKey, KeyCode.Delete, "Delete selected nodes", DeleteSelectedNodeWithRepaint );
			m_shortcutManager.RegisterEditorShortcut( true, EventModifiers.FunctionKey, KeyCode.Backspace, "Delete selected nodes", DeleteSelectedNodeWithRepaint );

			m_liveShaderEditing = m_innerEditorVariables.LiveMode;

			UpdateLiveUI();
		}


		public AmplifyShaderEditorWindow()
		{
			m_minNodePos = new Vector2( float.MaxValue, float.MaxValue );
			m_maxNodePos = new Vector2( float.MinValue, float.MinValue );

			m_duplicatePreventionBuffer = new DuplicatePreventionBuffer();
			m_commentaryTypeNode = typeof( CommentaryNode );
			titleContent = new GUIContent( "Shader Editor" );
			autoRepaintOnSceneChange = true;

			m_currentMousePos = new Vector3( 0, 0, 0 );
			m_keyEvtMousePos2D = new Vector2( 0, 0 );
			m_multipleSelectionStart = new Vector2( 0, 0 );
			m_initialized = false;
			m_graphBgTexture = null;
			m_graphFgTexture = null;

			m_cameraOffset = new Vector2( 0, 0 );
			CameraZoom = 1;

			m_registeredMenus = new List<MenuParent>();

			m_nodeParametersWindow = new NodeParametersWindow( this )
			{
				Resizable = true
			};
			m_registeredMenus.Add( m_nodeParametersWindow );

			m_modeWindow = new ShaderEditorModeWindow( this );
			//_registeredMenus.Add( _modeWindow );

			m_toolsWindow = new ToolsWindow( this );
			m_toolsWindow.ToolButtonPressedEvt += OnToolButtonPressed;

			m_consoleLogWindow = new ConsoleLogWindow( this );

			m_tipsWindow = new TipsWindow( this );

			m_registeredMenus.Add( m_toolsWindow );
			//m_registeredMenus.Add( m_consoleLogWindow );

			m_palettePopup = new PalettePopUp();

			m_clipboard = new Clipboard();

			m_genericMessageContent = new GUIContent();
			m_dragAndDropTool = new DragAndDropTool();
			m_dragAndDropTool.OnValidDropObjectEvt += OnValidObjectsDropped;

			//_confirmationWindow = new ConfirmationWindow( 100, 100, 300, 100 );

			m_saveIsDirty = false;

			m_preMadeShaders = new PreMadeShaders();

			Undo.undoRedoPerformed += UndoRedoPerformed;

			float autoPanSpeed = 2;
			m_autoPanArea = new AutoPanData[ 4 ];
			m_autoPanArea[ 0 ] = new AutoPanData( AutoPanLocation.TOP, 25, autoPanSpeed * Vector2.up );
			m_autoPanArea[ 1 ] = new AutoPanData( AutoPanLocation.BOTTOM, 25, autoPanSpeed * Vector2.down );
			m_autoPanArea[ 2 ] = new AutoPanData( AutoPanLocation.LEFT, 25, autoPanSpeed * Vector2.right );
			m_autoPanArea[ 3 ] = new AutoPanData( AutoPanLocation.RIGHT, 25, autoPanSpeed * Vector2.left );

			m_drawInfo = new DrawInfo();
			UIUtils.CurrentWindow = this;

			m_nodeExporterUtils = new NodeExporterUtils( this );
			m_repaintIsDirty = false;
			m_initialized = false;
		}

		public void SetStandardShader()
		{
			m_mainGraphInstance.ReplaceMasterNode( AvailableShaderTypes.SurfaceShader );
			m_mainGraphInstance.FireMasterNodeReplacedEvent();
		}

		public void SetTemplateShader( string templateName, bool writeDefaultData )
		{
			TemplateDataParent templateData = m_templatesManager.GetTemplate( ( string.IsNullOrEmpty( templateName ) ? "6e114a916ca3e4b4bb51972669d463bf" : templateName ) );
			m_mainGraphInstance.ReplaceMasterNode( AvailableShaderTypes.Template, writeDefaultData, templateData );
		}

		public void DeleteSelectedNodeWithRepaint()
		{
			DeleteSelectedNodes();
			SetSaveIsDirty();
		}


		void UndoRedoPerformed()
		{
			m_repaintIsDirty = true;
			m_saveIsDirty = true;
			m_removedKeyboardFocus = true;
			m_refreshOnUndo = true;
		}

		void Destroy()
		{
			Undo.ClearUndo( this );

			m_initialized = false;

			m_nodeExporterUtils.Destroy();
			m_nodeExporterUtils = null;

			m_delayedMaterialSet = null;

			m_materialsToUpdate.Clear();
			m_materialsToUpdate = null;

			GLDraw.Destroy();

			UIUtils.Destroy();
			m_preMadeShaders.Destroy();
			m_preMadeShaders = null;

			m_registeredMenus.Clear();
			m_registeredMenus = null;

			m_mainGraphInstance.Destroy();
			ScriptableObject.DestroyImmediate( m_mainGraphInstance );
			m_mainGraphInstance = null;

			Resources.UnloadAsset( m_graphBgTexture );
			m_graphBgTexture = null;

			Resources.UnloadAsset( m_graphFgTexture );
			m_graphFgTexture = null;

			Resources.UnloadAsset( m_wireTexture );
			m_wireTexture = null;

			m_contextMenu.Destroy();
			m_contextMenu = null;

			m_shortcutManager.Destroy();
			m_shortcutManager = null;

			m_nodeParametersWindow.Destroy();
			m_nodeParametersWindow = null;


			m_modeWindow.Destroy();
			m_modeWindow = null;

			m_toolsWindow.Destroy();
			m_toolsWindow = null;

			m_consoleLogWindow.Destroy();
			m_consoleLogWindow = null;

			m_tipsWindow.Destroy();
			m_tipsWindow = null;

			m_optionsWindow.Destroy();
			m_optionsWindow = null;

			m_paletteWindow.Destroy();
			m_paletteWindow = null;

			m_palettePopup.Destroy();
			m_palettePopup = null;

			m_contextPalette.Destroy();
			m_contextPalette = null;

			m_clipboard.ClearClipboard();
			m_clipboard = null;

			m_genericMessageUI.Destroy();
			m_genericMessageUI = null;
			m_genericMessageContent = null;

			m_dragAndDropTool.Destroy();
			m_dragAndDropTool = null;

			m_openedShaderFunction = null;

			UIUtils.CurrentWindow = null;
			m_duplicatePreventionBuffer.ReleaseAllData();
			m_duplicatePreventionBuffer = null;
#if UNITY_2018_1_OR_NEWER
			EditorApplication.projectChanged -= OnProjectWindowChanged;
#else
			EditorApplication.projectWindowChanged -= OnProjectWindowChanged;
#endif
			Selection.selectionChanged -= OnProjectSelectionChanged;

			IOUtils.AllOpenedWindows.Remove( this );

			if( IOUtils.AllOpenedWindows.Count == 0 )
			{
				m_templatesManager.Destroy();
				ScriptableObject.DestroyImmediate( m_templatesManager );
			}
			m_templatesManager = null;

			IOUtils.Destroy();

			Resources.UnloadUnusedAssets();
			GC.Collect();
		}

		void Init()
		{
			// = AssetDatabase.LoadAssetAtPath( Constants.ASEPath + "", typeof( Texture2D ) ) as Texture2D;
			m_graphBgTexture = AssetDatabase.LoadAssetAtPath( AssetDatabase.GUIDToAssetPath( IOUtils.GraphBgTextureGUID ), typeof( Texture2D ) ) as Texture2D;
			if( m_graphBgTexture != null )
			{
				m_graphFgTexture = AssetDatabase.LoadAssetAtPath( AssetDatabase.GUIDToAssetPath( IOUtils.GraphFgTextureGUID ), typeof( Texture2D ) ) as Texture2D;

				//Setup usable area
				m_cameraInfo = position;
				m_graphArea = new Rect( 0, 0, m_cameraInfo.width, m_cameraInfo.height );

				// Creating style state to show current selected object
				m_graphFontStyle = new GUIStyle()
				{
					fontSize = 32,
					alignment = TextAnchor.MiddleCenter,
					fixedWidth = m_cameraInfo.width,
					fixedHeight = 50,
					stretchWidth = true,
					stretchHeight = true
				};
				m_graphFontStyle.normal.textColor = Color.white;

				m_wireTexture = AssetDatabase.LoadAssetAtPath( AssetDatabase.GUIDToAssetPath( IOUtils.WireTextureGUID ), typeof( Texture2D ) ) as Texture2D;

				m_initialized = m_graphBgTexture != null &&
								m_graphFgTexture != null &&
								m_wireTexture != null;
			}
		}
#if UNITY_2018_3_OR_NEWER
		

#endif
		[OnOpenAssetAttribute()]
		static bool OnOpenAsset( int instanceID, int line )
		{
			if( line > -1 )
			{
				return false;
			}
			Preferences.LoadDefaults();
#if UNITY_2018_3_OR_NEWER
			ASEPackageManagerHelper.RequestInfo();
			ASEPackageManagerHelper.Update();
			if( ASEPackageManagerHelper.IsProcessing )
			{
				Shader selectedShader = Selection.activeObject as Shader;
				if( selectedShader != null )
				{
					if( IOUtils.IsASEShader( selectedShader ) )
					{
						ASEPackageManagerHelper.SetupLateShader( selectedShader );
						return true;
					}
				}
				else
				{
					Material mat = Selection.activeObject as Material;
					if( mat != null )
					{
						if( IOUtils.IsASEShader( mat.shader ) )
						{
							ASEPackageManagerHelper.SetupLateMaterial( mat );
							return true;
						}
					}
					else
					{
						AmplifyShaderFunction shaderFunction = Selection.activeObject as AmplifyShaderFunction;
						if( shaderFunction != null )
						{
							if( IOUtils.IsShaderFunction( shaderFunction.FunctionInfo ) )
							{
								ASEPackageManagerHelper.SetupLateShaderFunction( shaderFunction );
								return true;
							}
						}
					}
				}
			}
			else
#endif
			{
				Shader selectedShader = Selection.activeObject as Shader;
				if( selectedShader != null )
				{
					if( IOUtils.IsASEShader( selectedShader ) )
					{
						ConvertShaderToASE( selectedShader );
						return true;
					}
				}
				else
				{
					Material mat = Selection.activeObject as Material;
					if( mat != null )
					{
						if( IOUtils.IsASEShader( mat.shader ) )
						{
							LoadMaterialToASE( mat );
							return true;
						}
					}
					else
					{
						AmplifyShaderFunction shaderFunction = Selection.activeObject as AmplifyShaderFunction;
						if( shaderFunction != null )
						{
							if( IOUtils.IsShaderFunction( shaderFunction.FunctionInfo ) )
							{
								LoadShaderFunctionToASE( shaderFunction, false );
								return true;
							}
						}
					}
				}
			}

			return false;
		}

		[MenuItem( "Assets/Create/Amplify Shader/Surface", false, 84 )]
		[MenuItem( "Assets/Create/Shader/Amplify Surface Shader" )]
		static void CreateConfirmationStandardShader()
		{
			//string path = AssetDatabase.GetAssetPath( Selection.activeObject );
			//if( path == "" )
			//{
			//	path = "Assets";
			//}
			//else if( System.IO.Path.GetExtension( path ) != "" )
			//{
			//	path = path.Replace( System.IO.Path.GetFileName( AssetDatabase.GetAssetPath( Selection.activeObject ) ), "" );
			//}

			//string assetPathAndName = AssetDatabase.GenerateUniqueAssetPath( path + "/New Amplify Shader.shader" );
			var endNameEditAction = ScriptableObject.CreateInstance<DoCreateStandardShader>();
			ProjectWindowUtil.StartNameEditingIfProjectWindowExists( 0, endNameEditAction, "New Amplify Shader.shader"/*assetPathAndName*/, AssetPreview.GetMiniTypeThumbnail( typeof( Shader ) ), null );
		}
		//static void CreateNewShader(  )
		//{
		//	CreateNewShader( null, null );
		//}

		static void CreateNewShader( string customPath , string customShaderName )
		{

			string path = string.Empty;
			if( string.IsNullOrEmpty( customPath ) )
			{
				if( Selection.activeObject != null )
				{
					path = ( IOUtils.dataPath + AssetDatabase.GetAssetPath( Selection.activeObject ) );
				}
				else
				{
					UnityEngine.Object[] selection = Selection.GetFiltered( typeof( UnityEngine.Object ), SelectionMode.DeepAssets );
					if( selection.Length > 0 && selection[ 0 ] != null )
					{
						path = ( IOUtils.dataPath + AssetDatabase.GetAssetPath( selection[ 0 ] ) );
					}
					else
					{
						path = Application.dataPath;
					}

				}

				if( path.IndexOf( '.' ) > -1 )
				{
					path = path.Substring( 0, path.LastIndexOf( '/' ) );
				}
				path += "/";
			}
			else
			{
				path = customPath;
			}

			if( IOUtils.AllOpenedWindows.Count > 0 )
			{
				EditorWindow openedWindow = AmplifyShaderEditorWindow.GetWindow<AmplifyShaderEditorWindow>();
				AmplifyShaderEditorWindow currentWindow = CreateTab();
				WindowHelper.AddTab( openedWindow, currentWindow );
				UIUtils.CurrentWindow = currentWindow;
				Shader shader = UIUtils.CreateNewEmpty( path, customShaderName );
				Selection.activeObject = shader;
			}
			else
			{
				AmplifyShaderEditorWindow currentWindow = OpenWindow();
				UIUtils.CurrentWindow = currentWindow;
				Shader shader = UIUtils.CreateNewEmpty( path, customShaderName );
				Selection.activeObject = shader;
			}
			//Selection.objects = new UnityEngine.Object[] { shader };
		}

		public static void CreateConfirmationTemplateShader( string templateGuid )
		{
			UIUtils.NewTemplateGUID = templateGuid;
			//string path = AssetDatabase.GetAssetPath( Selection.activeObject );
			//if( path == "" )
			//{
			//	path = "Assets";
			//}
			//else if( System.IO.Path.GetExtension( path ) != "" )
			//{
			//	path = path.Replace( System.IO.Path.GetFileName( AssetDatabase.GetAssetPath( Selection.activeObject ) ), "" );
			//}

			//string assetPathAndName = AssetDatabase.GenerateUniqueAssetPath( path + "/New Amplify Shader.shader" );
			var endNameEditAction = ScriptableObject.CreateInstance<DoCreateTemplateShader>();
			ProjectWindowUtil.StartNameEditingIfProjectWindowExists( 0, endNameEditAction, "New Amplify Shader.shader"/*assetPathAndName*/, AssetPreview.GetMiniTypeThumbnail( typeof( Shader ) ), null );
		}

		public static Shader CreateNewTemplateShader( string templateGUID , string customPath = null, string customShaderName = null )
		{
			string path = string.Empty;
			if( string.IsNullOrEmpty( customPath ) )
			{
				path = Selection.activeObject == null ? Application.dataPath : ( IOUtils.dataPath + AssetDatabase.GetAssetPath( Selection.activeObject ) );
				if( path.IndexOf( '.' ) > -1 )
				{
					path = path.Substring( 0, path.LastIndexOf( '/' ) );
				}
				path += "/";
			}
			else
			{
				path = customPath;
			}
			Shader shader = null;
			if( IOUtils.AllOpenedWindows.Count > 0 )
			{
				EditorWindow openedWindow = AmplifyShaderEditorWindow.GetWindow<AmplifyShaderEditorWindow>();
				AmplifyShaderEditorWindow currentWindow = CreateTab();
				WindowHelper.AddTab( openedWindow, currentWindow );
				UIUtils.CurrentWindow = currentWindow;
				shader = UIUtils.CreateNewEmptyTemplate( templateGUID, path, customShaderName );
				Selection.activeObject = shader;
			}
			else
			{
				AmplifyShaderEditorWindow currentWindow = OpenWindow();
				UIUtils.CurrentWindow = currentWindow;
				shader = UIUtils.CreateNewEmptyTemplate( templateGUID, path, customShaderName );
				Selection.activeObject = shader;
			}

			//Selection.objects = new UnityEngine.Object[] { shader };
			return shader;
		}

		[MenuItem( "Assets/Create/Amplify Shader Function", false, 84 )]
		[MenuItem( "Assets/Create/Shader/Amplify Shader Function" )]
		static void CreateNewShaderFunction()
		{
			AmplifyShaderFunction asset = ScriptableObject.CreateInstance<AmplifyShaderFunction>();

			//string path = AssetDatabase.GetAssetPath( Selection.activeObject );
			//if( path == "" )
			//{
			//	path = "Assets";
			//}
			//else if( System.IO.Path.GetExtension( path ) != "" )
			//{
			//	path = path.Replace( System.IO.Path.GetFileName( AssetDatabase.GetAssetPath( Selection.activeObject ) ), "" );
			//}

			//string assetPathAndName = AssetDatabase.GenerateUniqueAssetPath( path + "/New ShaderFunction.asset" );

			var endNameEditAction = ScriptableObject.CreateInstance<DoCreateFunction>();
			ProjectWindowUtil.StartNameEditingIfProjectWindowExists( asset.GetInstanceID(), endNameEditAction, "New ShaderFunction.asset"/*assetPathAndName*/, AssetPreview.GetMiniThumbnail( asset ), null );
		}

		public void UpdateTabTitle( string newTitle, bool modified )
		{
			string[] titleArray = newTitle.Split( '/' );
			newTitle = titleArray[ titleArray.Length - 1 ];

			if( !( m_currentTitle.Equals( newTitle ) && m_currentTitleMod == modified ) )
			{
				this.titleContent.text = GenerateTabTitle( newTitle, modified );
			}
			m_currentTitle = newTitle;
			m_currentTitleMod = modified;
		}

		public void OnProjectWindowChanged()
		{
			Shader selectedShader = Selection.activeObject as Shader;
			if( selectedShader != null )
			{
				if( m_mainGraphInstance != null && m_mainGraphInstance.CurrentMasterNode != null && selectedShader == m_mainGraphInstance.CurrentMasterNode.CurrentShader )
				{
					m_lastOpenedLocation = AssetDatabase.GetAssetPath( selectedShader );
				}
			}
		}

		public void LoadProjectSelected( UnityEngine.Object selectedObject = null )
		{
			bool hasFocus = true;
			if( EditorWindow.focusedWindow != this )
			{
				hasFocus = false;
			}

			if( hasFocus && m_mainGraphInstance != null && m_mainGraphInstance.CurrentMasterNode != null )
			{
				LoadObject( selectedObject ?? Selection.activeObject );
			}
			else
			{
				m_delayedLoadObject = selectedObject ?? Selection.activeObject;
			}

			if( !hasFocus )
				Focus();
		}

		public void LoadObject( UnityEngine.Object objToLoad )
		{
			Shader selectedShader = objToLoad as Shader;
			Material selectedMaterial = objToLoad as Material;
			AmplifyShaderFunction selectedFunction = objToLoad as AmplifyShaderFunction;

			if( selectedFunction != null )
			{
				IsShaderFunctionWindow = true;
				m_mainGraphInstance.CurrentCanvasMode = NodeAvailability.ShaderFunction;
			}
			else
			{
				IsShaderFunctionWindow = false;
			}

			ASESelectionMode selectedFileType = ASESelectionMode.Shader;
			if( selectedShader != null )
			{
				selectedFileType = ASESelectionMode.Shader;
			}
			else if( selectedMaterial != null )
			{
				selectedFileType = ASESelectionMode.Material;
			}
			else if( selectedFunction != null )
			{
				selectedFileType = ASESelectionMode.ShaderFunction;
			}


			switch( CurrentSelection )
			{
				case ASESelectionMode.Shader:
				{
					if( ShaderIsModified )
					{
						Shader currShader = m_mainGraphInstance.CurrentMasterNode.CurrentShader;
						bool savePrevious = UIUtils.DisplayDialog( AssetDatabase.GetAssetPath( currShader ) );
						OnSaveShader( savePrevious, currShader, null, null );
					}
				}
				break;
				case ASESelectionMode.Material:
				{
					if( ShaderIsModified )
					{
						Shader currShader = m_mainGraphInstance.CurrentMasterNode.CurrentShader;
						bool savePrevious = UIUtils.DisplayDialog( AssetDatabase.GetAssetPath( currShader ) );
						OnSaveShader( savePrevious, currShader, m_mainGraphInstance.CurrentMasterNode.CurrentMaterial, null );
					}
				}
				break;
				case ASESelectionMode.ShaderFunction:
				{
					if( ShaderIsModified )
					{
						bool savePrevious = UIUtils.DisplayDialog( AssetDatabase.GetAssetPath( m_openedShaderFunction ) );
						OnSaveShader( savePrevious, null, null, selectedFunction );
					}
				}
				break;
			}

			switch( selectedFileType )
			{
				case ASESelectionMode.Shader:
				{
					LoadDroppedObject( true, selectedShader, null );
				}
				break;
				case ASESelectionMode.Material:
				{
					LoadDroppedObject( true, selectedMaterial.shader, selectedMaterial );
				}
				break;
				case ASESelectionMode.ShaderFunction:
				{
					LoadDroppedObject( true, null, null, selectedFunction );
				}
				break;
			}

			m_openedShaderFunction = m_mainGraphInstance.CurrentShaderFunction;

			//Need to force one graph draw because it wont call OnGui propertly since its focuses somewhere else
			// Focus() doesn't fix this since it only changes keyboard focus
			m_drawInfo.InvertedZoom = 1 / m_cameraZoom;

			m_mainGraphInstance.IsLoading = true;
			m_mainGraphInstance.Draw( m_drawInfo );
			m_mainGraphInstance.IsLoading = false;
			ShaderIsModified = false;
			Focus();
			Repaint();
		}

		public void OnProjectSelectionChanged()
		{
			if( m_loadShaderOnSelection )
			{
				LoadProjectSelected();
			}
		}

		ShaderLoadResult OnSaveShader( bool value, Shader shader, Material material, AmplifyShaderFunction function )
		{
			if( value )
			{
				SaveToDisk( false );
			}

			return value ? ShaderLoadResult.LOADED : ShaderLoadResult.FILE_NOT_FOUND;
		}

		public void ResetCameraSettings()
		{
			m_cameraInfo = position;
			m_cameraOffset = new Vector2( m_cameraInfo.width * 0.5f, m_cameraInfo.height * 0.5f );
			CameraZoom = 1;
		}

		public void Reset()
		{
			if( m_mainGraphInstance == null )
			{
				m_mainGraphInstance = CreateInstance<ParentGraph>();
				m_mainGraphInstance.Init();
				m_mainGraphInstance.ParentWindow = this;
				m_mainGraphInstance.SetGraphId( 0 );
			}
			m_mainGraphInstance.ResetEvents();
			m_mainGraphInstance.OnNodeEvent += OnNodeStoppedMovingEvent;
			m_mainGraphInstance.OnMaterialUpdatedEvent += OnMaterialUpdated;
			m_mainGraphInstance.OnShaderUpdatedEvent += OnShaderUpdated;
			m_mainGraphInstance.OnEmptyGraphDetectedEvt += OnEmptyGraphDetected;
			m_mainGraphInstance.OnNodeRemovedEvent += m_toolsWindow.OnNodeRemovedFromGraph;
			m_outdatedShaderFromTemplateLoaded = false;
			GraphCount = 1;

			FullCleanUndoStack();
			m_performFullUndoRegister = true;
			m_toolsWindow.BorderStyle = null;
			m_selectionMode = ASESelectionMode.Shader;
			ResetCameraSettings();
			UIUtils.ResetMainSkin();
			m_duplicatePreventionBuffer.ReleaseAllData();
			if( m_genericMessageUI != null )
				m_genericMessageUI.CleanUpMessageStack();
		}


		public Shader CreateNewGraph( string name )
		{
			Reset();
			UIUtils.DirtyMask = false;
			m_mainGraphInstance.CreateNewEmpty( name );
			m_lastOpenedLocation = string.Empty;
			UIUtils.DirtyMask = true;
			return m_mainGraphInstance.CurrentMasterNode.CurrentShader;
		}

		public Shader CreateNewTemplateGraph( string templateGUID )
		{
			Reset();
			UIUtils.DirtyMask = false;
			m_mainGraphInstance.CreateNewEmptyTemplate( templateGUID );
			m_lastOpenedLocation = string.Empty;
			UIUtils.DirtyMask = true;
			return m_mainGraphInstance.CurrentMasterNode.CurrentShader;
		}

		public Shader CreateNewGraph( Shader shader )
		{
			Reset();
			UIUtils.DirtyMask = false;
			m_mainGraphInstance.CreateNewEmpty( shader.name );
			m_mainGraphInstance.CurrentMasterNode.CurrentShader = shader;

			m_lastOpenedLocation = string.Empty;
			UIUtils.DirtyMask = true;
			return m_mainGraphInstance.CurrentMasterNode.CurrentShader;
		}

		public void CreateNewFunctionGraph( AmplifyShaderFunction shaderFunction )
		{
			Reset();
			UIUtils.DirtyMask = false;
			m_mainGraphInstance.CreateNewEmptyFunction( shaderFunction );
			m_mainGraphInstance.CurrentShaderFunction = shaderFunction;

			m_lastOpenedLocation = AssetDatabase.GetAssetPath( shaderFunction ); //string.Empty;
			UIUtils.DirtyMask = true;
			//return m_mainGraphInstance.CurrentMasterNode.CurrentShader;
		}

		public bool SaveToDisk( bool checkTimestamp )
		{
			if( checkTimestamp )
			{
				if( !m_cacheSaveOp )
				{
					m_lastTimeSaved = EditorApplication.timeSinceStartup;
					m_cacheSaveOp = true;
				}
				return false;
			}


			System.Threading.Thread.CurrentThread.CurrentCulture = System.Globalization.CultureInfo.InvariantCulture;

			m_customGraph = null;
			m_cacheSaveOp = false;
			ShaderIsModified = false;
			m_mainGraphInstance.LoadedShaderVersion = VersionInfo.FullNumber;
			m_lastTimeSaved = EditorApplication.timeSinceStartup;

			if( m_mainGraphInstance.CurrentMasterNodeId == Constants.INVALID_NODE_ID )
			{
				Shader currentShader = m_mainGraphInstance.CurrentMasterNode != null ? m_mainGraphInstance.CurrentMasterNode.CurrentShader : null;
				string newShader;
				if( !String.IsNullOrEmpty( m_lastOpenedLocation ) )
				{
					newShader = m_lastOpenedLocation;
				}
				else if( currentShader != null )
				{
					newShader = AssetDatabase.GetAssetPath( currentShader );
				}
				else
				{
					newShader = EditorUtility.SaveFilePanel( "Select Shader to save", Application.dataPath, "MyShader", "shader" );
				}

				if( !String.IsNullOrEmpty( newShader ) )
				{
					ShowMessage( "No Master node assigned.\nShader file will only have node info" );
					IOUtils.StartSaveThread( GenerateGraphInfo(), newShader );
					AssetDatabase.Refresh();
					LoadFromDisk( newShader );
					System.Threading.Thread.CurrentThread.CurrentCulture = System.Threading.Thread.CurrentThread.CurrentUICulture;
					return true;
				}
			}
			else if( m_mainGraphInstance.CurrentMasterNode != null )
			{
				//m_mainGraphInstance.CurrentStandardSurface.ForceReordering();
				Shader currShader = m_mainGraphInstance.CurrentMasterNode.CurrentShader;
				if( currShader != null )
				{
					m_mainGraphInstance.FireMasterNode( currShader );
					Material material = m_mainGraphInstance.CurrentMaterial;
					m_lastpath = ( material != null ) ? AssetDatabase.GetAssetPath( material ) : AssetDatabase.GetAssetPath( currShader );
					EditorPrefs.SetString( IOUtils.LAST_OPENED_OBJ_ID, m_lastpath );
					System.Threading.Thread.CurrentThread.CurrentCulture = System.Threading.Thread.CurrentThread.CurrentUICulture;
					if( IOUtils.OnShaderSavedEvent != null )
					{
						string info = string.Empty;
						if( !m_mainGraphInstance.IsStandardSurface )
						{
							TemplateMultiPassMasterNode masterNode = m_mainGraphInstance.GetMainMasterNodeOfLOD( -1 );
							if( masterNode != null )
							{
								info = masterNode.CurrentTemplate.GUID;
							}
						}
						IOUtils.OnShaderSavedEvent( currShader, !m_mainGraphInstance.IsStandardSurface, info );
					}
					return true;
				}
				else
				{

					string shaderName;
					string pathName;
					IOUtils.GetShaderName( out shaderName, out pathName, Constants.DefaultShaderName, UIUtils.LatestOpenedFolder );
					if( !String.IsNullOrEmpty( pathName ) )
					{
						UIUtils.CurrentWindow.CurrentGraph.CurrentMasterNode.SetName( shaderName );
						m_mainGraphInstance.FireMasterNode( pathName, true );
						m_lastpath = pathName;
						EditorPrefs.SetString( IOUtils.LAST_OPENED_OBJ_ID, pathName );
						System.Threading.Thread.CurrentThread.CurrentCulture = System.Threading.Thread.CurrentThread.CurrentUICulture;
						return true;
					}
				}
			}
			else
			{
				//m_nodeParametersWindow.ForceReordering();
				m_mainGraphInstance.ResetNodesLocalVariables();

				List<FunctionInput> functionInputNodes = UIUtils.FunctionInputList();
				functionInputNodes.Sort( ( x, y ) => { return x.OrderIndex.CompareTo( y.OrderIndex ); } );
				for( int i = 0; i < functionInputNodes.Count; i++ )
				{
					functionInputNodes[ i ].OrderIndex = i;
				}

				List<FunctionOutput> functionOutputNodes = UIUtils.FunctionOutputList();
				functionOutputNodes.Sort( ( x, y ) => { return x.OrderIndex.CompareTo( y.OrderIndex ); } );
				for( int i = 0; i < functionOutputNodes.Count; i++ )
				{
					functionOutputNodes[ i ].OrderIndex = i;
				}

				m_mainGraphInstance.CurrentShaderFunction.AdditionalDirectives.UpdateSaveItemsFromDirectives();
				m_mainGraphInstance.CurrentShaderFunction.FunctionInfo = GenerateGraphInfo();
				m_mainGraphInstance.CurrentShaderFunction.FunctionInfo = IOUtils.AddAdditionalInfo( m_mainGraphInstance.CurrentShaderFunction.FunctionInfo );

				if( AssetDatabase.IsMainAsset( m_mainGraphInstance.CurrentShaderFunction ) )
				{
					EditorUtility.SetDirty( m_mainGraphInstance.CurrentShaderFunction );
				}
				else
				{
					//Debug.Log( LastOpenedLocation );
					//AssetDatabase.CreateAsset( m_mainGraphInstance.CurrentShaderFunction, LastOpenedLocation );
				}

				AssetDatabase.SaveAssets();
				AssetDatabase.Refresh();
				m_mainGraphInstance.CurrentShaderFunction.AdditionalDirectives.UpdateDirectivesFromSaveItems();
				IOUtils.FunctionNodeChanged = true;
				m_lastpath = AssetDatabase.GetAssetPath( m_mainGraphInstance.CurrentShaderFunction );
				System.Threading.Thread.CurrentThread.CurrentCulture = System.Threading.Thread.CurrentThread.CurrentUICulture;
				//EditorPrefs.SetString( IOUtils.LAST_OPENED_OBJ_ID, AssetDatabase.GetAssetPath( m_mainGraphInstance.CurrentShaderFunction ) );
				return true;
			}
			System.Threading.Thread.CurrentThread.CurrentCulture = System.Threading.Thread.CurrentThread.CurrentUICulture;
			return false;
		}

		public void OnToolButtonPressed( ToolButtonType type )
		{
			switch( type )
			{
				case ToolButtonType.New:
				{
					UIUtils.CreateNewEmpty();
				}
				break;
				case ToolButtonType.Open:
				{
					UIUtils.OpenFile();
				}
				break;
				case ToolButtonType.Save:
				{
					SaveToDisk( false );
				}
				break;
				case ToolButtonType.Library:
				{
					ShowShaderLibrary();
				}
				break;
				case ToolButtonType.Options: { } break;
				case ToolButtonType.Update:
				{
					if( Preferences.GlobalClearLog )
					{
						m_consoleLogWindow.ClearMessages();
					}
#if UNITY_2019_4_OR_NEWER
					if( EditorSettings.asyncShaderCompilation && Preferences.GlobalShowAsyncMsg )
					{
						ShowMessage( AsyncMessage );
					}
#endif
					SaveToDisk( false );
				}
				break;
				case ToolButtonType.Live:
				{
					m_liveShaderEditing = !m_liveShaderEditing;
					m_innerEditorVariables.LiveMode = m_liveShaderEditing;
					// 0 off
					// 1 on
					// 2 pending
					if( m_liveShaderEditing && m_mainGraphInstance.CurrentMasterNode != null && m_mainGraphInstance.CurrentMasterNode.CurrentShader == null )
					{
						m_liveShaderEditing = false;
						m_innerEditorVariables.LiveMode = false;
					}

					UpdateLiveUI();

					if( m_liveShaderEditing )
					{
						SaveToDisk( false );
					}
				}
				break;
				case ToolButtonType.OpenSourceCode:
				{
					AssetDatabase.OpenAsset( m_mainGraphInstance.CurrentMasterNode.CurrentShader, 1 );
				}
				break;
				case ToolButtonType.MasterNode:
				{
					m_mainGraphInstance.AssignMasterNode();
				}
				break;

				case ToolButtonType.FocusOnMasterNode:
				{
					double currTime = EditorApplication.timeSinceStartup;
					bool autoZoom = ( currTime - m_focusOnMasterNodeTimestamp ) < AutoZoomTime;
					m_focusOnMasterNodeTimestamp = currTime;
					FocusOnNode( m_mainGraphInstance.CurrentMasterNode, autoZoom ? 1 : m_cameraZoom, true );
				}
				break;

				case ToolButtonType.FocusOnSelection:
				{
					FocusZoom( false, true, true );
				}
				break;
				case ToolButtonType.ShowInfoWindow:
				{
					PortLegendInfo.OpenWindow();
				}
				break;
				case ToolButtonType.ShowTipsWindow:
				{
					TipsWindow.ShowWindow( true );
				}
				break;
				case ToolButtonType.ShowConsole:
				{
					m_consoleLogWindow.Toggle();
				}
				break;
				case ToolButtonType.Share:
				{
					List<ParentNode> selectedNodes = m_mainGraphInstance.SelectedNodes;
					if( selectedNodes.Count > 0 )
					{
						CopyToClipboard();
						StartPasteRequest();
					}
					else
					{
						ShowMessage( "No nodes selected to share" );
					}
				}
				break;
				case ToolButtonType.TakeScreenshot:
				{
#if UNITY_EDITOR_WIN
					this.Focus();
					m_aseHandle = WindowsUtil.GetActiveWindow();
					//m_aseHandle = FindASEWindowHandle();

					bool takeit = EditorUtility.DisplayDialog( "Take Screenshot", "This is a work in progress feature that will undock itself if needed, increase the window outside of your screen resolution to take the shot, if something fails (ie: graph too big) you may need to restart Unity, do you wish to continue?", "Yes", "Cancel" );
					if( !takeit )
						break;

					if( this.IsDocked() )
					{
						this.Undock();
						this.Focus();
						m_aseHandle = WindowsUtil.GetActiveWindow();
					}

					int windowLong = WindowsUtil.GetWindowLong( m_aseHandle, WindowsUtil.GWL_STYLE );

					List<ParentNode> selectedNodes = m_mainGraphInstance.AllNodes;

					Vector2 minPos = new Vector2( float.MaxValue, float.MaxValue );
					Vector2 maxPos = new Vector2( float.MinValue, float.MinValue );
					Vector2 centroid = Vector2.zero;

					for( int i = 0; i < selectedNodes.Count; i++ )
					{
						Rect currPos = selectedNodes[ i ].TruePosition;
						minPos.x = ( currPos.x < minPos.x ) ? currPos.x : minPos.x;
						minPos.y = ( currPos.y < minPos.y ) ? currPos.y : minPos.y;

						maxPos.x = ( ( currPos.x + currPos.width ) > maxPos.x ) ? ( currPos.x + currPos.width ) : maxPos.x;
						maxPos.y = ( ( currPos.y + currPos.height ) > maxPos.y ) ? ( currPos.y + currPos.height ) : maxPos.y;
					}

					centroid = ( maxPos - minPos );

					m_prevCameraOffset = m_cameraOffset;
					m_prevCameraZoom = CameraZoom;

					WindowsUtil.SetWindowLong( m_aseHandle, WindowsUtil.GWL_STYLE, (int)( windowLong & ~( WindowsUtil.WS_SIZEBOX ) ) );
					var rect = new WindowsUtil.Rect();
					WindowsUtil.GetWindowRect( m_aseHandle, ref rect );
					m_prevWindowRect = new Rect( rect.Left, rect.Top, rect.Width, rect.Height );

					WindowsUtil.SetWindowPos( m_aseHandle, 0, (int)m_prevWindowRect.xMin, (int)m_prevWindowRect.yMin, (int)centroid.x, (int)centroid.y, 0x0040 );
					WindowsUtil.SetWindowLong( m_aseHandle, WindowsUtil.GWL_STYLE, (int)( windowLong ) );

					m_takeScreenShot = true;
#else
					EditorUtility.DisplayDialog( "Take Screenshot", "This is a work in progress feature that only works in Windows environment", "Ok" );
#endif
				}
				break;
				case ToolButtonType.CleanUnusedNodes:
				{
					m_mainGraphInstance.CleanUnusedNodes();
				}
				break;
				case ToolButtonType.Help:
				{
					Application.OpenURL( Constants.HelpURL );
				}
				break;
			}
		}

#if UNITY_EDITOR_WIN
		IntPtr FindASEWindowHandle()
		{
			System.Diagnostics.Process process = System.Diagnostics.Process.GetCurrentProcess();

			IntPtr[] winPtrs = WindowsUtil.GetProcessWindows( process.Id );
			m_aseHandle = IntPtr.Zero;
			bool found = false;
			for( int i = 0; i < winPtrs.Length; i++ )
			{
				WindowsUtil.EnumChildWindows( winPtrs[ i ], delegate ( System.IntPtr hwnd, System.IntPtr param )
				{
					System.Text.StringBuilder Title = new System.Text.StringBuilder( 256 );
					WindowsUtil.GetWindowText( hwnd, Title, Title.Capacity );

					if( Title.ToString().Contains( "AmplifyShaderEditor.AmplifyShaderEditorWindow" ) )
					{
						if( !found )
						{
							m_aseHandle = winPtrs[ i ];
							found = true;
						}
					}

					return true;
				}, System.IntPtr.Zero );
			}

			return m_aseHandle;
		}

		void OpenSavedFolder()
		{
			m_openSavedFolder = false;

			var path = System.IO.Path.GetFullPath( Application.dataPath + "\\..\\ScreenshotASE.png" );
			EditorUtility.RevealInFinder( path );
			GUIUtility.ExitGUI();
		}

		void TakeScreenShot()
		{
			m_takeScreenShot = false;

			var cacher = RenderTexture.active;
			RenderTexture.active = null;

			Texture2D m_screenshotTex2D = new Texture2D( (int)position.width, (int)position.height, TextureFormat.RGB24, false );
			m_screenshotTex2D.ReadPixels( new Rect( 0, 0, m_screenshotTex2D.width, m_screenshotTex2D.height ), 0, 0 );
			m_screenshotTex2D.Apply();

			byte[] bytes = m_screenshotTex2D.EncodeToPNG();

			var path = System.IO.Path.GetFullPath( Application.dataPath + "\\..\\ScreenshotASE.png" );
			System.IO.File.WriteAllBytes( path, bytes );

			RenderTexture.active = cacher;

			ShowMessage( "[AmplifyShaderEditor] Screenshot successfully taken and saved at: " + path, consoleLog:true );

			WindowsUtil.SetWindowPos( m_aseHandle, 0, (int)m_prevWindowRect.xMin, (int)m_prevWindowRect.yMin, (int)m_prevWindowRect.width, (int)m_prevWindowRect.height, 0x0040 );
			m_cameraOffset = m_prevCameraOffset;
			CameraZoom = m_prevCameraZoom;

			m_openSavedFolder = true;
		}
#endif


		void UpdateLiveUI()
		{
			if( m_toolsWindow != null )
			{
				m_toolsWindow.SetStateOnButton( ToolButtonType.Live, ( m_liveShaderEditing ) ? 1 : 0 );
			}
		}

		void FocusZoom( bool forceAllNodes, bool doubleTap, bool smooth = true )
		{
			List<ParentNode> selectedNodes = ( m_mainGraphInstance.SelectedNodes.Count > 0 ) && !forceAllNodes ? m_mainGraphInstance.SelectedNodes : m_mainGraphInstance.AllNodes;

			Vector2 minPos = new Vector2( float.MaxValue, float.MaxValue );
			Vector2 maxPos = new Vector2( float.MinValue, float.MinValue );
			Vector2 centroid = Vector2.zero;

			for( int i = 0; i < selectedNodes.Count; i++ )
			{
				Rect currPos = selectedNodes[ i ].TruePosition;

				minPos.x = ( currPos.x < minPos.x ) ? currPos.x : minPos.x;
				minPos.y = ( currPos.y < minPos.y ) ? currPos.y : minPos.y;

				maxPos.x = ( ( currPos.x + currPos.width ) > maxPos.x ) ? ( currPos.x + currPos.width ) : maxPos.x;
				maxPos.y = ( ( currPos.y + currPos.height ) > maxPos.y ) ? ( currPos.y + currPos.height ) : maxPos.y;

			}

			centroid = ( maxPos - minPos );

			double currTime = EditorApplication.timeSinceStartup;
			bool autoZoom = ( currTime - m_focusOnSelectionTimestamp ) < AutoZoomTime;
			if( !doubleTap )
				autoZoom = true;
			m_focusOnSelectionTimestamp = currTime;

			float zoom = m_cameraZoom;
			if( autoZoom )
			{
				zoom = 1f;
				float canvasWidth = m_cameraInfo.width;
				if( m_nodeParametersWindow.IsMaximized )
					canvasWidth -= m_nodeParametersWindow.RealWidth;
				if( m_paletteWindow.IsMaximized )
					canvasWidth -= m_paletteWindow.RealWidth;
				canvasWidth -= 40;
				//float canvasWidth = AvailableCanvasWidth;// - 20;
				float canvasHeight = AvailableCanvasHeight - 60;
				if( centroid.x > canvasWidth ||
					centroid.y > canvasHeight )
				{
					float hZoom = float.MinValue;
					float vZoom = float.MinValue;
					if( centroid.x > canvasWidth )
					{
						hZoom = ( centroid.x ) / canvasWidth;
					}

					if( centroid.y > canvasHeight )
					{
						vZoom = ( centroid.y ) / canvasHeight;
					}
					zoom = ( hZoom > vZoom ) ? hZoom : vZoom;
				}
			}

			minPos.y -= 20 * zoom;
			if( m_nodeParametersWindow.IsMaximized )
				minPos.x -= m_nodeParametersWindow.RealWidth * 0.5f * zoom;
			if( m_paletteWindow.IsMaximized )
				minPos.x += m_paletteWindow.RealWidth * 0.5f * zoom;

			FocusOnPoint( minPos + centroid * 0.5f, zoom, smooth );
		}

		public void FocusOnNode( int nodeId, float zoom, bool selectNode, bool late = false )
		{
			ParentNode node = m_mainGraphInstance.GetNode( nodeId );
			if( node != null )
			{
				FocusOnNode( node, zoom, selectNode, late );
			}
		}

		public void FocusOnNode( ParentNode node, float zoom, bool selectNode, bool late = false )
		{
			if( late )
			{
				m_nodeToFocus = node;
				m_zoomToFocus = zoom;
				m_selectNodeToFocus = selectNode;
				return;
			}

			if( selectNode )
			{
				m_mainGraphInstance.SelectNode( node, false, false );
			}

			Vector2 nodePoint = node.CenterPosition;
			nodePoint.x = nodePoint.x - ( m_nodeParametersWindow.RealWidth * 0.5f + m_paletteWindow.RealWidth * 0.5f ) * ( zoom > 0.999f ? zoom : CameraZoom );
			FocusOnPoint( nodePoint, zoom );
		}

		public void FocusOnPoint( Vector2 point, float zoom, bool smooth = true )
		{
			if( zoom > 0.999f )
			{
				if( smooth )
					SmoothZoom( zoom );
				else
					CameraZoom = zoom;
			}

			if( smooth )
				SmoothCameraOffset( -point + new Vector2( ( m_cameraInfo.width ) * 0.5f, m_cameraInfo.height * 0.5f ) * CameraZoom );
			else
				m_cameraOffset = -point + new Vector2( ( m_cameraInfo.width ) * 0.5f, m_cameraInfo.height * 0.5f ) * CameraZoom;
		}

		void SmoothZoom( float newZoom )
		{
			m_smoothZoom = true;
			m_zoomTime = 0;
			m_targetZoom = newZoom;
			m_zoomPivot = m_graphArea.center;
		}

		void SmoothCameraOffset( Vector2 newOffset )
		{
			m_smoothOffset = true;
			m_offsetTime = 0;
			m_targetOffset = newOffset;
		}

		void PreTestLeftMouseDown()
		{
			if( m_currentEvent.type == EventType.MouseDown && m_currentEvent.button == ButtonClickId.LeftMouseButton )
			{
				ParentNode node = m_mainGraphInstance.CheckNodeAt( m_currentMousePos );
				if( node != null )
				{
					m_mainGraphInstance.NodeClicked = node.UniqueId;
					return;
				}
			}

			m_mainGraphInstance.NodeClicked = -1;
		}



		void OnLeftMouseDown()
		{
			Focus();

			if( m_lastKeyPressed == KeyCode.Q )
			{
				m_rmbStartPos = m_currentMousePos2D;
				UseCurrentEvent();
				return;
			}

			m_mouseDownOnValidArea = true;
			m_lmbPressed = true;
			if( m_currentEvent.alt )
			{
				m_altBoxSelection = true;
			}

			UIUtils.ShowContextOnPick = true;
			ParentNode node = ( m_mainGraphInstance.NodeClicked < 0 ) ? m_mainGraphInstance.CheckNodeAt( m_currentMousePos ) : m_mainGraphInstance.GetClickedNode();
			if( node != null )
			{
				m_mainGraphInstance.NodeClicked = node.UniqueId;
				m_altBoxSelection = false;

				if( m_contextMenu.CheckShortcutKey() )
				{
					if( node.ConnStatus == NodeConnectionStatus.Island )
					{
						if( !m_multipleSelectionActive )
						{
							ParentNode newNode = m_contextMenu.CreateNodeFromShortcutKey();
							if( newNode != null )
							{
								newNode.ContainerGraph = m_mainGraphInstance;
								newNode.Vec2Position = TranformedMousePos;
								m_mainGraphInstance.AddNode( newNode, true );
								m_mainGraphInstance.SelectNode( newNode, false, false );
								ForceRepaint();
							}
							( node as CommentaryNode ).AddNodeToCommentary( newNode );
						}
					}
				}
				else
				{
					if( node.OnClick( m_currentMousePos2D ) )
					{
						if( !node.Selected )
						{
							m_mainGraphInstance.SelectNode( node, ( m_currentEvent.modifiers == EventModifiers.Shift || m_currentEvent.modifiers == EventModifiers.Control ), true );
						}
						else if( m_currentEvent.modifiers == EventModifiers.Shift || m_currentEvent.modifiers == EventModifiers.Control )
						{
							m_mainGraphInstance.DeselectNode( node );
						}

						if( m_currentEvent.alt )
						{
							int conn = 0;
							for( int i = 0; i < node.InputPorts.Count; i++ )
							{
								if( node.InputPorts[ i ].IsConnected )
									conn++;
							}

							if( node.InputPorts.Count > 0 && node.OutputPorts.Count > 0 && conn > 0 && node.OutputPorts[ 0 ].IsConnected )
							{
								m_altDragStarted = true;
							}
						}

					}

					if( m_currentEvent.alt )
					{
						if( node.InputPorts.Count > 0 && node.OutputPorts.Count > 0 && node.InputPorts[ 0 ].IsConnected && node.OutputPorts[ 0 ].IsConnected )
						{
							m_altDragStarted = true;
						}
					}

					return;
				}
			}
			else if( !m_multipleSelectionActive )
			{
				ParentNode newNode = m_contextMenu.CreateNodeFromShortcutKey();
				if( newNode != null )
				{
					newNode.ContainerGraph = m_mainGraphInstance;
					newNode.Vec2Position = TranformedMousePos;
					m_mainGraphInstance.AddNode( newNode, true );
					m_mainGraphInstance.SelectNode( newNode, false, false );
					SetSaveIsDirty();
					ForceRepaint();
				}
				else
				{
					List<WireBezierReference> wireRefs = m_mainGraphInstance.GetWireBezierListInPos( m_currentMousePos2D );
					if( wireRefs != null && wireRefs.Count > 0 )
					{
						for( int i = 0; i < wireRefs.Count; i++ )
						{
							// Place wire code here
							ParentNode outNode = m_mainGraphInstance.GetNode( wireRefs[ i ].OutNodeId );
							ParentNode inNode = m_mainGraphInstance.GetNode( wireRefs[ i ].InNodeId );

							OutputPort outputPort = outNode.GetOutputPortByUniqueId( wireRefs[ i ].OutPortId );
							InputPort inputPort = inNode.GetInputPortByUniqueId( wireRefs[ i ].InPortId );

							// Calculate the 4 points for bezier taking into account wire nodes and their automatic tangents
							Vector3 endPos = new Vector3( inputPort.Position.x, inputPort.Position.y );
							Vector3 startPos = new Vector3( outputPort.Position.x, outputPort.Position.y );

							float mag = ( endPos - startPos ).magnitude;
							float resizedMag = Mathf.Min( mag, Constants.HORIZONTAL_TANGENT_SIZE * m_drawInfo.InvertedZoom );

							Vector3 startTangent = new Vector3( startPos.x + resizedMag, startPos.y );
							Vector3 endTangent = new Vector3( endPos.x - resizedMag, endPos.y );

							if( inNode != null && inNode.GetType() == typeof( WireNode ) )
								endTangent = endPos + ( ( inNode as WireNode ).TangentDirection ) * mag * 0.33f;

							if( outNode != null && outNode.GetType() == typeof( WireNode ) )
								startTangent = startPos - ( ( outNode as WireNode ).TangentDirection ) * mag * 0.33f;

							float dist = HandleUtility.DistancePointBezier( m_currentMousePos, startPos, endPos, startTangent, endTangent );
							if( dist < 10 )
							{
								double doubleTapTime = EditorApplication.timeSinceStartup;
								bool doubleTap = ( doubleTapTime - m_wiredDoubleTapTimestamp ) < WiredDoubleTapTime;
								m_wiredDoubleTapTimestamp = doubleTapTime;

								if( doubleTap )
								{
									Undo.RegisterCompleteObjectUndo( this, Constants.UndoCreateConnectionId );
									Undo.RegisterCompleteObjectUndo( m_mainGraphInstance, Constants.UndoCreateConnectionId );
									Undo.RecordObject( outNode, Constants.UndoCreateConnectionId );
									Undo.RecordObject( inNode, Constants.UndoCreateConnectionId );

									ParentNode wireNode = m_mainGraphInstance.CreateNode( typeof( WireNode ), true );
									if( wireNode != null )
									{
										wireNode.Vec2Position = TranformedMousePos;

										m_mainGraphInstance.CreateConnection( wireNode.InputPorts[ 0 ].NodeId, wireNode.InputPorts[ 0 ].PortId, outputPort.NodeId, outputPort.PortId );
										m_mainGraphInstance.CreateConnection( inputPort.NodeId, inputPort.PortId, wireNode.OutputPorts[ 0 ].NodeId, wireNode.OutputPorts[ 0 ].PortId );

										SetSaveIsDirty();
										ForceRepaint();
										Undo.IncrementCurrentGroup();
									}
								}

								break;
							}
						}
					}
					//Reset focus from any textfield which may be selected at this time
					GUIUtility.keyboardControl = 0;
				}
			}

			if( m_currentEvent.modifiers != EventModifiers.Shift && m_currentEvent.modifiers != EventModifiers.Control && !m_altBoxSelection )
				m_mainGraphInstance.DeSelectAll();

			if( m_wireReferenceUtils.ValidReferences() )
			{
				m_wireReferenceUtils.InvalidateReferences();
				return;
			}

			if( !m_contextMenu.CheckShortcutKey() && m_currentEvent.modifiers != EventModifiers.Shift && m_currentEvent.modifiers != EventModifiers.Control || m_altBoxSelection )
			{
				// Only activate multiple selection if no node is selected and shift key not pressed
				m_multipleSelectionActive = true;

				m_multipleSelectionStart = TranformedMousePos;
				m_multipleSelectionArea.position = m_multipleSelectionStart;
				m_multipleSelectionArea.size = Vector2.zero;
			}

			UseCurrentEvent();
		}

		void OnLeftMouseDrag()
		{
			if( m_lostFocus )
			{
				m_lostFocus = false;
				return;
			}

			if( m_lastKeyPressed == KeyCode.Q )
			{
				if( m_currentEvent.alt )
				{
					ModifyZoom( Constants.ALT_CAMERA_ZOOM_SPEED * ( m_currentEvent.delta.x + m_currentEvent.delta.y ), m_altKeyStartPos );
				}
				else
				{
					m_cameraOffset += m_cameraZoom * m_currentEvent.delta;
				}
				UseCurrentEvent();
				return;
			}

			if( m_altDragStarted )
			{
				m_altDragStarted = false;

				if( m_currentEvent.modifiers == EventModifiers.Alt && CurrentGraph.SelectedNodes.Count == 1 )
				{
					ParentNode node = CurrentGraph.SelectedNodes[ 0 ];
					int lastId = 0;
					int conn = 0;
					for( int i = 0; i < node.InputPorts.Count; i++ )
					{
						if( node.InputPorts[ i ].IsConnected )
						{
							conn++;
							lastId = i;
						}
					}

					if( conn > 1 )
						lastId = 0;



					OutputPort outputPort = node.InputPorts[ lastId ].GetOutputConnection( 0 );
					ParentNode outputNode = m_mainGraphInstance.GetNode( outputPort.NodeId );
					bool outputIsWireNode = outputNode is WireNode;

					Undo.RegisterCompleteObjectUndo( this, Constants.UndoCreateConnectionId );
					node.RecordObject( Constants.UndoCreateConnectionId );
					outputNode.RecordObject( Constants.UndoCreateConnectionId );

					List<InputPort> inputPorts = new List<InputPort>();
					for( int i = 0; i < node.OutputPorts[ 0 ].ConnectionCount; i++ )
					{
						InputPort inputPort = node.OutputPorts[ 0 ].GetInputConnection( i );
						ParentNode inputNode = m_mainGraphInstance.GetNode( inputPort.NodeId );
						inputNode.RecordObject( Constants.UndoCreateConnectionId );
						inputPorts.Add( inputPort );
					}
					
					for( int i = 0; i < inputPorts.Count; i++ )
					{
						if( outputIsWireNode )
						{
							if( i == 0 )
							{
								m_mainGraphInstance.CreateConnection( inputPorts[ i ].NodeId, inputPorts[ i ].PortId, outputPort.NodeId, outputPort.PortId );
							}
							else
							{
								UIUtils.DeleteConnection( true, inputPorts[ i ].NodeId, inputPorts[ i ].PortId, false, true );
							}
						}
						else
						{
							m_mainGraphInstance.CreateConnection( inputPorts[ i ].NodeId, inputPorts[ i ].PortId, outputPort.NodeId, outputPort.PortId );
						}
					}

					UIUtils.DeleteConnection( true, node.UniqueId, node.InputPorts[ lastId ].PortId, false, true );

					SetSaveIsDirty();
					ForceRepaint();
				}
			}

			if( !m_wireReferenceUtils.ValidReferences() && !m_altBoxSelection )
			{
				if( m_mouseDownOnValidArea && m_insideEditorWindow )
				{
					if( m_currentEvent.control )
					{
						m_mainGraphInstance.MoveSelectedNodes( m_cameraZoom * m_currentEvent.delta, true );
					}
					else
					{
						m_mainGraphInstance.MoveSelectedNodes( m_cameraZoom * m_currentEvent.delta );
					}
					//m_mainGraphInstance.MoveSelectedNodes( m_cameraZoom * m_currentEvent.delta );
					m_autoPanDirActive = true;
				}
			}
			else
			{
				List<ParentNode> nodes = m_mainGraphInstance.GetNodesInGrid( m_drawInfo.TransformedMousePos );
				if( nodes != null && nodes.Count > 0 )
				{
					Vector2 currentPortPos = new Vector2();
					Vector2 mousePos = TranformedMousePos;

					if( m_wireReferenceUtils.InputPortReference.IsValid )
					{
						OutputPort currentPort = null;
						float smallestDistance = float.MaxValue;
						Vector2 smallestPosition = Vector2.zero;
						for( int nodeIdx = 0; nodeIdx < nodes.Count; nodeIdx++ )
						{
							List<OutputPort> outputPorts = nodes[ nodeIdx ].OutputPorts;
							if( outputPorts != null )
							{
								for( int o = 0; o < outputPorts.Count; o++ )
								{
									if( outputPorts[ o ].Available )
									{
										currentPortPos.x = outputPorts[ o ].Position.x;
										currentPortPos.y = outputPorts[ o ].Position.y;

										currentPortPos = currentPortPos * m_cameraZoom - m_cameraOffset;
										float dist = ( mousePos - currentPortPos ).sqrMagnitude;
										if( dist < smallestDistance )
										{
											smallestDistance = dist;
											smallestPosition = currentPortPos;
											currentPort = outputPorts[ o ];
										}
									}
								}
							}
						}

						if( currentPort != null && currentPort.Available && ( smallestDistance < Constants.SNAP_SQR_DIST || currentPort.InsideActiveArea( ( mousePos + m_cameraOffset ) / m_cameraZoom ) ) )
						{
							m_wireReferenceUtils.ActivateSnap( smallestPosition, currentPort );
						}
						else
						{
							m_wireReferenceUtils.DeactivateSnap();
						}
					}

					if( m_wireReferenceUtils.OutputPortReference.IsValid )
					{
						InputPort currentPort = null;
						float smallestDistance = float.MaxValue;
						Vector2 smallestPosition = Vector2.zero;
						for( int nodeIdx = 0; nodeIdx < nodes.Count; nodeIdx++ )
						{
							List<InputPort> inputPorts = nodes[ nodeIdx ].InputPorts;
							if( inputPorts != null )
							{
								for( int i = 0; i < inputPorts.Count; i++ )
								{
									if( inputPorts[ i ].Available )
									{
										currentPortPos.x = inputPorts[ i ].Position.x;
										currentPortPos.y = inputPorts[ i ].Position.y;

										currentPortPos = currentPortPos * m_cameraZoom - m_cameraOffset;
										float dist = ( mousePos - currentPortPos ).sqrMagnitude;
										if( dist < smallestDistance )
										{
											smallestDistance = dist;
											smallestPosition = currentPortPos;
											currentPort = inputPorts[ i ];
										}
									}
								}
							}
						}
						if( currentPort != null && currentPort.Available && ( smallestDistance < Constants.SNAP_SQR_DIST || currentPort.InsideActiveArea( ( mousePos + m_cameraOffset ) / m_cameraZoom ) ) )
						{
							m_wireReferenceUtils.ActivateSnap( smallestPosition, currentPort );
						}
						else
						{
							m_wireReferenceUtils.DeactivateSnap();
						}
					}
				}
				else if( m_wireReferenceUtils.SnapEnabled )
				{
					m_wireReferenceUtils.DeactivateSnap();
				}
			}
			UseCurrentEvent();
		}

		public void OnLeftMouseUp()
		{
			m_lmbPressed = false;
			if( m_multipleSelectionActive )
			{
				//m_multipleSelectionActive = false;
				UpdateSelectionArea();
				//m_mainGraphInstance.MultipleSelection( m_multipleSelectionArea, ( m_currentEvent.modifiers == EventModifiers.Shift || m_currentEvent.modifiers == EventModifiers.Control ), true );
				if( m_currentEvent.alt && m_altBoxSelection )
				{
					m_mainGraphInstance.MultipleSelection( m_multipleSelectionArea, !m_currentEvent.shift );
				}
				else
				{
					m_mainGraphInstance.DeSelectAll();
					m_mainGraphInstance.MultipleSelection( m_multipleSelectionArea );
				}
			}

			if( m_wireReferenceUtils.ValidReferences() )
			{
				//Check if there is some kind of port beneath the mouse ... if so connect to it
				ParentNode targetNode = m_wireReferenceUtils.SnapEnabled ? m_mainGraphInstance.GetNode( m_wireReferenceUtils.SnapPort.NodeId ) : m_mainGraphInstance.CheckNodeAt( m_currentMousePos );
				if( targetNode != null && targetNode.ConnStatus != NodeConnectionStatus.Island )
				{
					if( m_wireReferenceUtils.InputPortReference.IsValid && m_wireReferenceUtils.InputPortReference.NodeId != targetNode.UniqueId )
					{
						OutputPort outputPort = m_wireReferenceUtils.SnapEnabled ? targetNode.GetOutputPortByUniqueId( m_wireReferenceUtils.SnapPort.PortId ) : targetNode.CheckOutputPortAt( m_currentMousePos );
						if( outputPort != null && !outputPort.Locked && ( !m_wireReferenceUtils.InputPortReference.TypeLocked ||
													m_wireReferenceUtils.InputPortReference.DataType == WirePortDataType.OBJECT ||
													( m_wireReferenceUtils.InputPortReference.TypeLocked && outputPort.DataType == m_wireReferenceUtils.InputPortReference.DataType ) ) )
						{

							ParentNode originNode = m_mainGraphInstance.GetNode( m_wireReferenceUtils.InputPortReference.NodeId );
							InputPort inputPort = originNode.GetInputPortByUniqueId( m_wireReferenceUtils.InputPortReference.PortId );
							UIUtils.MarkUndoAction();
							Undo.RegisterCompleteObjectUndo( this, Constants.UndoCreateConnectionId );
							originNode.RecordObject( Constants.UndoCreateConnectionId );
							targetNode.RecordObject( Constants.UndoCreateConnectionId );

							if( inputPort.NotFreeForAllTypes && outputPort.NotFreeForAllTypes )
							{
								if( !inputPort.CheckValidType( outputPort.DataType ) )
								{
									UIUtils.ShowIncompatiblePortMessage( true, originNode, inputPort, targetNode, outputPort );
									m_wireReferenceUtils.InvalidateReferences();
									UseCurrentEvent();
									return;
								}

								if( !outputPort.CheckValidType( inputPort.DataType ) )
								{
									UIUtils.ShowIncompatiblePortMessage( false, targetNode, outputPort, originNode, inputPort );
									m_wireReferenceUtils.InvalidateReferences();
									UseCurrentEvent();
									return;
								}
							}

							inputPort.DummyAdd( outputPort.NodeId, outputPort.PortId );
							outputPort.DummyAdd( m_wireReferenceUtils.InputPortReference.NodeId, m_wireReferenceUtils.InputPortReference.PortId );

							if( UIUtils.DetectNodeLoopsFrom( originNode, new Dictionary<int, int>() ) )
							{
								inputPort.DummyRemove();
								outputPort.DummyRemove();
								m_wireReferenceUtils.InvalidateReferences();
								ShowMessage( "Infinite Loop detected" );
								UseCurrentEvent();
								return;
							}

							inputPort.DummyRemove();
							outputPort.DummyRemove();

							if( inputPort.IsConnected )
							{
								DeleteConnection( true, m_wireReferenceUtils.InputPortReference.NodeId, m_wireReferenceUtils.InputPortReference.PortId, true, false );
							}

							//link output to input
							if( outputPort.ConnectTo( m_wireReferenceUtils.InputPortReference.NodeId, m_wireReferenceUtils.InputPortReference.PortId, m_wireReferenceUtils.InputPortReference.DataType, m_wireReferenceUtils.InputPortReference.TypeLocked ) )
								targetNode.OnOutputPortConnected( outputPort.PortId, m_wireReferenceUtils.InputPortReference.NodeId, m_wireReferenceUtils.InputPortReference.PortId );

							//link input to output
							if( inputPort.ConnectTo( outputPort.NodeId, outputPort.PortId, outputPort.DataType, m_wireReferenceUtils.InputPortReference.TypeLocked ) )
								originNode.OnInputPortConnected( m_wireReferenceUtils.InputPortReference.PortId, targetNode.UniqueId, outputPort.PortId );
							m_mainGraphInstance.MarkWireHighlights();
						}
						else if( outputPort != null && m_wireReferenceUtils.InputPortReference.TypeLocked && m_wireReferenceUtils.InputPortReference.DataType != outputPort.DataType )
						{
							ShowMessage( "Attempting to connect a port locked to type " + m_wireReferenceUtils.InputPortReference.DataType + " into a port of type " + outputPort.DataType );
						}
						ShaderIsModified = true;
						SetSaveIsDirty();
					}

					if( m_wireReferenceUtils.OutputPortReference.IsValid && m_wireReferenceUtils.OutputPortReference.NodeId != targetNode.UniqueId )
					{
						InputPort inputPort = m_wireReferenceUtils.SnapEnabled ? targetNode.GetInputPortByUniqueId( m_wireReferenceUtils.SnapPort.PortId ) : targetNode.CheckInputPortAt( m_currentMousePos );
						if( inputPort != null && !inputPort.Locked && ( !inputPort.TypeLocked ||
													 inputPort.DataType == WirePortDataType.OBJECT ||
													 ( inputPort.TypeLocked && inputPort.DataType == m_wireReferenceUtils.OutputPortReference.DataType ) ) )
						{
							ParentNode originNode = m_mainGraphInstance.GetNode( m_wireReferenceUtils.OutputPortReference.NodeId );
							OutputPort outputPort = originNode.GetOutputPortByUniqueId( m_wireReferenceUtils.OutputPortReference.PortId );

							UIUtils.MarkUndoAction();
							Undo.RegisterCompleteObjectUndo( this, Constants.UndoCreateConnectionId );
							originNode.RecordObject( Constants.UndoCreateConnectionId );
							targetNode.RecordObject( Constants.UndoCreateConnectionId );

							if( inputPort.NotFreeForAllTypes && outputPort.NotFreeForAllTypes )
							{
								if( !inputPort.CheckValidType( outputPort.DataType ) )
								{
									UIUtils.ShowIncompatiblePortMessage( true, targetNode, inputPort, originNode, outputPort );
									m_wireReferenceUtils.InvalidateReferences();
									UseCurrentEvent();
									return;
								}

								if( !outputPort.CheckValidType( inputPort.DataType ) )
								{
									UIUtils.ShowIncompatiblePortMessage( false, originNode, outputPort, targetNode, inputPort );
									m_wireReferenceUtils.InvalidateReferences();
									UseCurrentEvent();
									return;
								}
							}

							inputPort.DummyAdd( m_wireReferenceUtils.OutputPortReference.NodeId, m_wireReferenceUtils.OutputPortReference.PortId );
							outputPort.DummyAdd( inputPort.NodeId, inputPort.PortId );
							if( UIUtils.DetectNodeLoopsFrom( targetNode, new Dictionary<int, int>() ) )
							{
								inputPort.DummyRemove();
								outputPort.DummyRemove();
								m_wireReferenceUtils.InvalidateReferences();
								ShowMessage( "Infinite Loop detected" );
								UseCurrentEvent();
								return;
							}

							inputPort.DummyRemove();
							outputPort.DummyRemove();

							if( inputPort.IsConnected )
							{
								if( m_currentEvent.control && m_wireReferenceUtils.SwitchPortReference.IsValid )
								{
									ParentNode oldOutputNode = UIUtils.GetNode( inputPort.GetConnection( 0 ).NodeId );
									OutputPort oldOutputPort = oldOutputNode.GetOutputPortByUniqueId( inputPort.GetConnection( 0 ).PortId );

									ParentNode switchNode = UIUtils.GetNode( m_wireReferenceUtils.SwitchPortReference.NodeId );
									InputPort switchPort = switchNode.GetInputPortByUniqueId( m_wireReferenceUtils.SwitchPortReference.PortId );

									switchPort.DummyAdd( oldOutputPort.NodeId, oldOutputPort.PortId );
									oldOutputPort.DummyAdd( switchPort.NodeId, switchPort.PortId );
									if( UIUtils.DetectNodeLoopsFrom( switchNode, new Dictionary<int, int>() ) )
									{
										switchPort.DummyRemove();
										oldOutputPort.DummyRemove();
										m_wireReferenceUtils.InvalidateReferences();
										ShowMessage( "Infinite Loop detected" );
										UseCurrentEvent();
										return;
									}

									switchPort.DummyRemove();
									oldOutputPort.DummyRemove();

									DeleteConnection( true, inputPort.NodeId, inputPort.PortId, true, false );
									ConnectInputToOutput( switchPort.NodeId, switchPort.PortId, oldOutputPort.NodeId, oldOutputPort.PortId );
								}
								else
								{
									DeleteConnection( true, inputPort.NodeId, inputPort.PortId, true, false );
								}
							}
							inputPort.InvalidateAllConnections();


							//link input to output
							if( inputPort.ConnectTo( m_wireReferenceUtils.OutputPortReference.NodeId, m_wireReferenceUtils.OutputPortReference.PortId, m_wireReferenceUtils.OutputPortReference.DataType, inputPort.TypeLocked ) )
								targetNode.OnInputPortConnected( inputPort.PortId, m_wireReferenceUtils.OutputPortReference.NodeId, m_wireReferenceUtils.OutputPortReference.PortId );
							//link output to input

							if( outputPort.ConnectTo( inputPort.NodeId, inputPort.PortId, inputPort.DataType, inputPort.TypeLocked ) )
								originNode.OnOutputPortConnected( m_wireReferenceUtils.OutputPortReference.PortId, targetNode.UniqueId, inputPort.PortId );
							m_mainGraphInstance.MarkWireHighlights();
						}
						else if( inputPort != null && inputPort.TypeLocked && inputPort.DataType != m_wireReferenceUtils.OutputPortReference.DataType )
						{
							ShowMessage( "Attempting to connect a " + m_wireReferenceUtils.OutputPortReference.DataType + " to a port locked to type " + inputPort.DataType );
						}
						ShaderIsModified = true;
						SetSaveIsDirty();
					}
					m_wireReferenceUtils.InvalidateReferences();
				}
				else
				{
					if( UIUtils.ShowContextOnPick )
						m_contextPalette.Show( m_currentMousePos2D, m_cameraInfo );
					else
						m_wireReferenceUtils.InvalidateReferences();
				}
			}
			else if( m_currentEvent.modifiers == EventModifiers.Alt && m_altAvailable && CurrentGraph.SelectedNodes.Count == 1 && !m_altBoxSelection && !m_multipleSelectionActive )
			{
				List<WireBezierReference> wireRefs = m_mainGraphInstance.GetWireBezierListInPos( m_currentMousePos2D );
				if( wireRefs != null && wireRefs.Count > 0 )
				{
					float closestDist = 50;
					int closestId = 0;

					for( int i = 0; i < wireRefs.Count; i++ )
					{
						ParentNode outNode = m_mainGraphInstance.GetNode( wireRefs[ i ].OutNodeId );
						ParentNode inNode = m_mainGraphInstance.GetNode( wireRefs[ i ].InNodeId );

						if( outNode == CurrentGraph.SelectedNodes[ 0 ] || inNode == CurrentGraph.SelectedNodes[ 0 ] )
							continue;

						OutputPort outputPort = outNode.GetOutputPortByUniqueId( wireRefs[ i ].OutPortId );
						InputPort inputPort = inNode.GetInputPortByUniqueId( wireRefs[ i ].InPortId );

						// Calculate the 4 points for bezier taking into account wire nodes and their automatic tangents
						Vector3 endPos = new Vector3( inputPort.Position.x, inputPort.Position.y );
						Vector3 startPos = new Vector3( outputPort.Position.x, outputPort.Position.y );

						float mag = ( endPos - startPos ).magnitude;
						float resizedMag = Mathf.Min( mag, Constants.HORIZONTAL_TANGENT_SIZE * m_drawInfo.InvertedZoom );

						Vector3 startTangent = new Vector3( startPos.x + resizedMag, startPos.y );
						Vector3 endTangent = new Vector3( endPos.x - resizedMag, endPos.y );

						if( inNode != null && inNode.GetType() == typeof( WireNode ) )
							endTangent = endPos + ( ( inNode as WireNode ).TangentDirection ) * mag * 0.33f;

						if( outNode != null && outNode.GetType() == typeof( WireNode ) )
							startTangent = startPos - ( ( outNode as WireNode ).TangentDirection ) * mag * 0.33f;

						//Vector2 pos = ( CurrentGraph.SelectedNodes[0].CenterPosition + m_cameraOffset ) / m_cameraZoom;

						float dist = HandleUtility.DistancePointBezier( /*pos*/ m_currentMousePos, startPos, endPos, startTangent, endTangent );
						if( dist < 40 )
						{
							if( dist < closestDist )
							{
								closestDist = dist;
								closestId = i;
							}
						}
					}

					if( closestDist < 40 )
					{
						ParentNode outNode = m_mainGraphInstance.GetNode( wireRefs[ closestId ].OutNodeId );
						ParentNode inNode = m_mainGraphInstance.GetNode( wireRefs[ closestId ].InNodeId );

						OutputPort outputPort = outNode.GetOutputPortByUniqueId( wireRefs[ closestId ].OutPortId );
						InputPort inputPort = inNode.GetInputPortByUniqueId( wireRefs[ closestId ].InPortId );

						ParentNode selectedNode = CurrentGraph.SelectedNodes[ 0 ];
						if( selectedNode.InputPorts.Count > 0 && selectedNode.OutputPorts.Count > 0 )
						{
							Undo.RegisterCompleteObjectUndo( this, Constants.UndoCreateConnectionId );
							selectedNode.RecordObject( Constants.UndoCreateConnectionId );
							inNode.RecordObject( Constants.UndoCreateConnectionId );
							outNode.RecordObject( Constants.UndoCreateConnectionId );

							m_mainGraphInstance.CreateConnection( selectedNode.UniqueId, selectedNode.InputPorts[ 0 ].PortId, outputPort.NodeId, outputPort.PortId );
							m_mainGraphInstance.CreateConnection( inputPort.NodeId, inputPort.PortId, selectedNode.UniqueId, selectedNode.OutputPorts[ 0 ].PortId );
						}

						SetSaveIsDirty();
						ForceRepaint();
					}
				}
			}
			UIUtils.ShowContextOnPick = true;
			m_altBoxSelection = false;
			m_multipleSelectionActive = false;
			UseCurrentEvent();
		}

		public void ConnectInputToOutput( int inNodeId, int inPortId, int outNodeId, int outPortId, bool registerUndo = true )
		{
			ParentNode inNode = m_mainGraphInstance.GetNode( inNodeId );
			ParentNode outNode = m_mainGraphInstance.GetNode( outNodeId );
			if( inNode != null && outNode != null )
			{
				InputPort inPort = inNode.GetInputPortByUniqueId( inPortId );
				OutputPort outPort = outNode.GetOutputPortByUniqueId( outPortId );
				if( inPort != null && outPort != null )
				{
					if( registerUndo )
					{
						Undo.RegisterCompleteObjectUndo( this, Constants.UndoCreateConnectionId );
						inNode.RecordObject( Constants.UndoCreateConnectionId );
						outNode.RecordObject( Constants.UndoCreateConnectionId );
					}

					if( inPort.ConnectTo( outNodeId, outPortId, outPort.DataType, inPort.TypeLocked ) )
					{
						inNode.OnInputPortConnected( inPortId, outNodeId, outPortId );
					}

					if( outPort.ConnectTo( inNodeId, inPortId, inPort.DataType, inPort.TypeLocked ) )
					{
						outNode.OnOutputPortConnected( outPortId, inNodeId, inPortId );
					}
				}
				m_mainGraphInstance.MarkWireHighlights();
				ShaderIsModified = true;
			}
		}

		void OnRightMouseDown()
		{
			Focus();
			m_rmbStartPos = m_currentMousePos2D;
			UseCurrentEvent();
		}

		void OnRightMouseDrag()
		{
			// We look at the control to detect when user hits a tooltip ( which has a hot control of 0 )
			// This needs to be checked because on this first "frame" of hitting a tooltip because it generates incorrect mouse delta values 
			if( GUIUtility.hotControl == 0 && m_lastHotControl != 0 )
			{
				m_lastHotControl = GUIUtility.hotControl;
				return;
			}

			m_lastHotControl = GUIUtility.hotControl;
			if( m_currentEvent.alt )
			{
				ModifyZoom( Constants.ALT_CAMERA_ZOOM_SPEED * ( m_currentEvent.delta.x + m_currentEvent.delta.y ), m_altKeyStartPos );
			}
			else
			{
				m_cameraOffset += m_cameraZoom * m_currentEvent.delta;
			}
			UseCurrentEvent();
		}

		void OnRightMouseUp()
		{
			//Resetting the hot control test variable so it can be used again on right mouse drag detection ( if we did not do this then m_lastHotControl could be left with a a value of 0 and wouldn't be able to be correctly used on rthe drag ) 
			m_lastHotControl = -1;

			if( ( m_rmbStartPos - m_currentMousePos2D ).sqrMagnitude < Constants.RMB_SCREEN_DIST )
			{
				ParentNode node = m_mainGraphInstance.CheckNodeAt( m_currentMousePos, true );
				if( node == null )
				{
					m_contextPalette.Show( m_currentMousePos2D, m_cameraInfo );
				}
			}
			UseCurrentEvent();
		}

		void UpdateSelectionArea()
		{
			m_multipleSelectionArea.size = TranformedMousePos - m_multipleSelectionStart;
		}

		public void OnValidObjectsDropped( UnityEngine.Object[] droppedObjs )
		{
			bool propagateDraggedObjsToNode = true;
			// Only supporting single drag&drop object selection
			if( droppedObjs.Length == 1 )
			{
				ShaderIsModified = true;
				SetSaveIsDirty();
				// Check if its a shader, material or game object  and if so load the shader graph code from it
				Shader newShader = droppedObjs[ 0 ] as Shader;
				Material newMaterial = null;
				if( newShader == null )
				{
					newMaterial = droppedObjs[ 0 ] as Material;
#if UNITY_2018_1_OR_NEWER
					bool isProcedural = ( newMaterial != null );
#else
					// Disabling Substance Deprecated warning
#pragma warning disable 0618
					bool isProcedural = ( newMaterial != null && newMaterial is ProceduralMaterial );
#pragma warning restore 0618
#endif
					if( newMaterial != null && !isProcedural )
					{
						if( UIUtils.IsUnityNativeShader( AssetDatabase.GetAssetPath( newMaterial.shader ) ) )
						{
							return;
						}
						//newShader = newMaterial.shader;
						LoadMaterialToASE( newMaterial );
						//m_mainGraphInstance.UpdateMaterialOnMasterNode( newMaterial );
					}
					else
					{
						GameObject go = droppedObjs[ 0 ] as GameObject;
						if( go != null )
						{
							Renderer renderer = go.GetComponent<Renderer>();
							if( renderer )
							{
								newMaterial = renderer.sharedMaterial;
								newShader = newMaterial.shader;
							}
						}
					}
				}

				if( newShader != null )
				{
					ConvertShaderToASE( newShader );

					propagateDraggedObjsToNode = false;
				}

				// if not shader loading then propagate the seletion to whats below the mouse
				if( propagateDraggedObjsToNode )
				{
					ParentNode node = m_mainGraphInstance.CheckNodeAt( m_currentMousePos );
					if( node != null )
					{
						// if there's a node then pass the object into it to see if there's a setup with it
						node.OnObjectDropped( droppedObjs[ 0 ] );
					}
					else
					{
						// If not then check if there's a node that can be created through the dropped object
						ParentNode newNode = m_contextMenu.CreateNodeFromCastType( droppedObjs[ 0 ].GetType() );
						if( newNode )
						{
							newNode.ContainerGraph = m_mainGraphInstance;
							newNode.Vec2Position = TranformedMousePos;
							m_mainGraphInstance.AddNode( newNode, true );
							newNode.SetupFromCastObject( droppedObjs[ 0 ] );
							m_mainGraphInstance.SelectNode( newNode, false, false );
							ForceRepaint();
							bool find = false;
							if( newNode is FunctionNode && CurrentGraph.CurrentShaderFunction != null )
								find = SearchFunctionNodeRecursively( CurrentGraph.CurrentShaderFunction );

							if( find )
							{
								DestroyNode( newNode, false );
								ShowMessage( "Shader Function loop detected, new node was removed to prevent errors." );
							}
						}
					}
				}
			}
		}

		public bool SearchFunctionNodeRecursively( AmplifyShaderFunction function )
		{
			List<FunctionNode> graphList = UIUtils.FunctionList();

			bool nodeFind = false;

			for( int i = 0; i < graphList.Count; i++ )
			{
				ParentGraph temp = CustomGraph;
				CustomGraph = graphList[ i ].FunctionGraph;
				nodeFind = SearchFunctionNodeRecursively( function );
				CustomGraph = temp;

				//Debug.Log( "tested = " + node.Function.FunctionName + " : " + function.FunctionName );

				if( graphList[ i ].Function == function )
					return true;
			}

			return nodeFind;
		}

		public void SetDelayedMaterialMode( Material material )
		{
			if( material == null )
				return;
			m_delayedMaterialSet = material;
		}

		public ShaderLoadResult LoadDroppedObject( bool value, Shader shader, Material material, AmplifyShaderFunction shaderFunction = null )
		{
			UIUtils.CurrentWindow = this;
			ShaderLoadResult result;
			if( shaderFunction != null )
			{
				string assetDatapath = AssetDatabase.GetAssetPath( shaderFunction );
				string latestOpenedFolder = Application.dataPath + assetDatapath.Substring( 6 );
				UIUtils.LatestOpenedFolder = latestOpenedFolder.Substring( 0, latestOpenedFolder.LastIndexOf( '/' ) + 1 );
				result = LoadFromDisk( assetDatapath, shaderFunction );
				CurrentSelection = ASESelectionMode.ShaderFunction;
				IsShaderFunctionWindow = true;
				titleContent.text = GenerateTabTitle( shaderFunction.FunctionName );
				titleContent.image = UIUtils.ShaderFunctionIcon;
				m_lastpath = assetDatapath;
				m_nodeParametersWindow.OnShaderFunctionLoad();
				//EditorPrefs.SetString( IOUtils.LAST_OPENED_OBJ_ID, assetDatapath );
			}
			else if( value && shader != null )
			{
				string assetDatapath = AssetDatabase.GetAssetPath( shader );
				string latestOpenedFolder = Application.dataPath + assetDatapath.Substring( 6 );
				UIUtils.LatestOpenedFolder = latestOpenedFolder.Substring( 0, latestOpenedFolder.LastIndexOf( '/' ) + 1 );
				result = LoadFromDisk( assetDatapath );
				switch( result )
				{
					case ShaderLoadResult.LOADED:
					{
						m_mainGraphInstance.UpdateShaderOnMasterNode( shader );
					}
					break;
					case ShaderLoadResult.ASE_INFO_NOT_FOUND:
					{
						ShowMessage( "Loaded shader wasn't created with ASE. Saving it will remove previous data." );
						UIUtils.CreateEmptyFromInvalid( shader );
					}
					break;
					case ShaderLoadResult.FILE_NOT_FOUND:
					case ShaderLoadResult.UNITY_NATIVE_PATHS:
					{
						UIUtils.CreateEmptyFromInvalid( shader );
					}
					break;
				}

				m_mainGraphInstance.UpdateMaterialOnMasterNode( material );
				m_mainGraphInstance.SetMaterialModeOnGraph( material );

				if( material != null )
				{
					CurrentSelection = ASESelectionMode.Material;
					IsShaderFunctionWindow = false;
					titleContent.text = GenerateTabTitle( material.name );
					titleContent.image = UIUtils.MaterialIcon;
					if( material.HasProperty( IOUtils.DefaultASEDirtyCheckId ) )
					{
						material.SetInt( IOUtils.DefaultASEDirtyCheckId, 1 );
					}
					m_lastpath = AssetDatabase.GetAssetPath( material );
					EditorPrefs.SetString( IOUtils.LAST_OPENED_OBJ_ID, m_lastpath );
				}
				else
				{
					CurrentSelection = ASESelectionMode.Shader;
					IsShaderFunctionWindow = false;
					titleContent.text = GenerateTabTitle( shader.name );
					titleContent.image = UIUtils.ShaderIcon;
					m_lastpath = AssetDatabase.GetAssetPath( shader );
					EditorPrefs.SetString( IOUtils.LAST_OPENED_OBJ_ID, m_lastpath );
				}
			}
			else
			{
				result = ShaderLoadResult.FILE_NOT_FOUND;
			}
			return result;
		}

		bool InsideMenus( Vector2 position )
		{
			for( int i = 0; i < m_registeredMenus.Count; i++ )
			{
				if( m_registeredMenus[ i ].IsInside( position ) )
				{
					return true;
				}
			}
			return false;
		}

		void HandleGUIEvents()
		{
			if( m_currentEvent.type == EventType.KeyDown )
			{
				m_contextMenu.UpdateKeyPress( m_currentEvent.keyCode );
			}
			else if( m_currentEvent.type == EventType.KeyUp )
			{
				m_contextMenu.UpdateKeyReleased( m_currentEvent.keyCode );
			}

			if( InsideMenus( m_currentMousePos2D ) )
			{
				if( m_currentEvent.type == EventType.Used )
					m_mouseDownOnValidArea = false;

				if( m_currentEvent.type == EventType.MouseDown )
				{
					m_mouseDownOnValidArea = false;
					UseCurrentEvent();
				}
				return;
			}
			else if( m_nodeParametersWindow.IsResizing || m_paletteWindow.IsResizing )
			{
				m_mouseDownOnValidArea = false;
			}

			int controlID = GUIUtility.GetControlID( FocusType.Passive );
			switch( m_currentEvent.GetTypeForControl( controlID ) )
			{
				case EventType.MouseDown:
				{
					GUIUtility.hotControl = controlID;
					switch( m_currentEvent.button )
					{
						case ButtonClickId.LeftMouseButton:
						{
							OnLeftMouseDown();
						}
						break;
						case ButtonClickId.RightMouseButton:
						case ButtonClickId.MiddleMouseButton:
						{
							OnRightMouseDown();
						}
						break;
					}
				}
				break;
				case EventType.MouseMove:
				{
					m_keyEvtMousePos2D = m_currentEvent.mousePosition;
				}
				break;
				case EventType.MouseUp:
				{
					GUIUtility.hotControl = 0;
					switch( m_currentEvent.button )
					{
						case ButtonClickId.LeftMouseButton:
						{
							OnLeftMouseUp();
						}
						break;
						case ButtonClickId.MiddleMouseButton: break;
						case ButtonClickId.RightMouseButton:
						{
							OnRightMouseUp();
						}
						break;
					}
				}
				break;
				case EventType.MouseDrag:
				{
					switch( m_currentEvent.button )
					{
						case ButtonClickId.LeftMouseButton:
						{
							OnLeftMouseDrag();
						}
						break;
						case ButtonClickId.MiddleMouseButton:
						case ButtonClickId.RightMouseButton:
						{
							OnRightMouseDrag();
						}
						break;
					}
				}
				break;
				case EventType.ScrollWheel:
				{
					OnScrollWheel();
				}
				break;
				case EventType.KeyDown:
				{
					OnKeyboardDown();
				}
				break;
				case EventType.KeyUp:
				{
					OnKeyboardUp();
				}
				break;
				case EventType.ValidateCommand:
				{
					switch( m_currentEvent.commandName )
					{
						case CopyCommand:
						case PasteCommand:
						case SelectAll:
						case Duplicate:
						{
							m_currentEvent.Use();
						}
						break;
						case ObjectSelectorClosed:
						{
							m_mouseDownOnValidArea = false;
						}
						break;
					}
				}
				break;
				case EventType.ExecuteCommand:
				{
					m_currentEvent.Use();
					switch( m_currentEvent.commandName )
					{
						case CopyCommand:
						{
							CopyToClipboard();
						}
						break;
						case PasteCommand:
						{
							PasteFromClipboard( true );
						}
						break;
						case SelectAll:
						{
							m_mainGraphInstance.SelectAll();
							ForceRepaint();
						}
						break;
						case Duplicate:
						{
							CopyToClipboard();
							PasteFromClipboard( true );
						}
						break;
						case ObjectSelectorClosed:
						{
							m_mouseDownOnValidArea = false;
						}
						break;
					}
				}
				break;
				case EventType.Repaint:
				{
				}
				break;
			}

			m_dragAndDropTool.TestDragAndDrop( m_graphArea );

		}

		public void DeleteConnection( bool isInput, int nodeId, int portId, bool registerOnLog, bool propagateCallback )
		{
			m_mainGraphInstance.DeleteConnection( isInput, nodeId, portId, registerOnLog, propagateCallback );
		}

		void DeleteSelectedNodes()
		{
			if( m_mainGraphInstance.SelectedNodes.Count == 0 )
				return;

			UIUtils.ClearUndoHelper();
			ParentNode[] selectedNodes = new ParentNode[ m_mainGraphInstance.SelectedNodes.Count ];
			for( int i = 0; i < selectedNodes.Length; i++ )
			{
				selectedNodes[ i ] = m_mainGraphInstance.SelectedNodes[ i ];
				selectedNodes[ i ].Rewire();
				UIUtils.CheckUndoNode( selectedNodes[ i ] );
			}

			//Check nodes connected to deleted nodes to preserve connections on undo
			List<ParentNode> extraNodes = new List<ParentNode>();
			for( int selectedNodeIdx = 0; selectedNodeIdx < selectedNodes.Length; selectedNodeIdx++ )
			{
				// Check inputs
				{
					int inputIdxCount = selectedNodes[ selectedNodeIdx ].InputPorts.Count;
					if( inputIdxCount > 0 )
					{
						for( int inputIdx = 0; inputIdx < inputIdxCount; inputIdx++ )
						{
							if( selectedNodes[ selectedNodeIdx ].InputPorts[ inputIdx ].IsConnected )
							{
								int nodeIdx = selectedNodes[ selectedNodeIdx ].InputPorts[ inputIdx ].ExternalReferences[ 0 ].NodeId;
								if( nodeIdx > -1 )
								{
									ParentNode node = m_mainGraphInstance.GetNode( nodeIdx );
									if( node != null && UIUtils.CheckUndoNode( node ) )
									{
										extraNodes.Add( node );
									}
								}
							}
						}
					}
				}

				// Check outputs
				int outputIdxCount = selectedNodes[ selectedNodeIdx ].OutputPorts.Count;
				if( outputIdxCount > 0 )
				{
					for( int outputIdx = 0; outputIdx < outputIdxCount; outputIdx++ )
					{
						int inputIdxCount = selectedNodes[ selectedNodeIdx ].OutputPorts[ outputIdx ].ExternalReferences.Count;
						if( inputIdxCount > 0 )
						{
							for( int inputIdx = 0; inputIdx < inputIdxCount; inputIdx++ )
							{
								int nodeIdx = selectedNodes[ selectedNodeIdx ].OutputPorts[ outputIdx ].ExternalReferences[ inputIdx ].NodeId;
								if( nodeIdx > -1 )
								{
									ParentNode node = m_mainGraphInstance.GetNode( nodeIdx );
									if( UIUtils.CheckUndoNode( node ) )
									{
										extraNodes.Add( node );
									}
								}
							}
						}
					}
				}
			}

			UIUtils.ClearUndoHelper();
			//Undo.IncrementCurrentGroup();
			//Record deleted nodes
			UIUtils.MarkUndoAction();
			Undo.RegisterCompleteObjectUndo( this, Constants.UndoDeleteNodeId );
			Undo.RegisterCompleteObjectUndo( m_mainGraphInstance, Constants.UndoDeleteNodeId );
			Undo.RecordObjects( selectedNodes, Constants.UndoDeleteNodeId );
			Undo.RecordObjects( extraNodes.ToArray(), Constants.UndoDeleteNodeId );

			//Record deleting connections
			for( int i = 0; i < selectedNodes.Length; i++ )
			{
				selectedNodes[ i ].Alive = false;
				m_mainGraphInstance.DeleteAllConnectionFromNode( selectedNodes[ i ], false, true, true );
			}
			//Delete
			m_mainGraphInstance.DeleteNodesOnArray( ref selectedNodes );


			//Undo.IncrementCurrentGroup();
			extraNodes.Clear();
			extraNodes = null;

			EditorUtility.SetDirty( this );

			ForceRepaint();
		}

		void OnKeyboardUp()
		{
			CheckKeyboardCameraUp();

			if( m_altPressDown )
			{
				m_altPressDown = false;
			}

			if( m_shortcutManager.ActivateShortcut( m_currentEvent.modifiers, m_lastKeyPressed, false ) )
			{
				ForceRepaint();
			}
			m_lastKeyPressed = KeyCode.None;
		}

		bool OnKeyboardPress( KeyCode code )
		{
			return ( m_currentEvent.keyCode == code && m_lastKeyPressed == KeyCode.None );
		}

		void CheckKeyboardCameraDown()
		{
			if( m_contextPalette.IsActive )
				return;
			if( m_currentEvent.alt )
			{
				bool foundKey = false;
				float dir = 0;
				switch( m_currentEvent.keyCode )
				{
					case KeyCode.UpArrow: foundKey = true; dir = 1; break;
					case KeyCode.DownArrow: foundKey = true; dir = -1; break;
					case KeyCode.LeftArrow: foundKey = true; dir = 1; break;
					case KeyCode.RightArrow: foundKey = true; dir = -1; break;
				}
				if( foundKey )
				{
					ModifyZoom( Constants.ALT_CAMERA_ZOOM_SPEED * dir * m_cameraSpeed, new Vector2( m_cameraInfo.width * 0.5f, m_cameraInfo.height * 0.5f ) );
					if( m_cameraSpeed < 15 )
						m_cameraSpeed += 0.2f;
					UseCurrentEvent();
				}


			}
			else
			{
				bool foundKey = false;
				Vector2 dir = Vector2.zero;
				switch( m_currentEvent.keyCode )
				{
					case KeyCode.UpArrow: foundKey = true; dir = Vector2.up; break;
					case KeyCode.DownArrow: foundKey = true; dir = Vector2.down; break;
					case KeyCode.LeftArrow: foundKey = true; dir = Vector2.right; break;
					case KeyCode.RightArrow: foundKey = true; dir = Vector2.left; break;
				}
				if( foundKey )
				{
					m_cameraOffset += m_cameraZoom * m_cameraSpeed * dir;
					if( m_cameraSpeed < 15 )
						m_cameraSpeed += 0.2f;

					UseCurrentEvent();
				}
			}
		}

		void CheckKeyboardCameraUp()
		{
			switch( m_currentEvent.keyCode )
			{
				case KeyCode.UpArrow:
				case KeyCode.DownArrow:
				case KeyCode.LeftArrow:
				case KeyCode.RightArrow: m_cameraSpeed = 1; break;
			}
		}

		void OnKeyboardDown()
		{
			//if( DebugConsoleWindow.DeveloperMode )
			//{
			//	if( OnKeyboardPress( KeyCode.F8 ) )
			//	{
			//		Shader currShader = CurrentGraph.CurrentShader;
			//		ShaderUtilEx.OpenCompiledShader( currShader, ShaderInspectorPlatformsPopupEx.GetCurrentMode(), ShaderInspectorPlatformsPopupEx.GetCurrentPlatformMask(), ShaderInspectorPlatformsPopupEx.GetCurrentVariantStripping() == 0 );

			//		string filename = Application.dataPath;
			//		filename = filename.Replace( "Assets", "Temp/Compiled-" );
			//		string shaderFilename = AssetDatabase.GetAssetPath( currShader );
			//		int lastIndex = shaderFilename.LastIndexOf( '/' ) + 1;
			//		filename = filename + shaderFilename.Substring( lastIndex );

			//		string compiledContents = IOUtils.LoadTextFileFromDisk( filename );
			//		Debug.Log( compiledContents );
			//	}

			//	if( OnKeyboardPress( KeyCode.F9 ) )
			//	{
			//		m_nodeExporterUtils.CalculateShaderInstructions( CurrentGraph.CurrentShader );
			//	}
			//}

			CheckKeyboardCameraDown();

			if( m_lastKeyPressed == KeyCode.None )
			{
				m_shortcutManager.ActivateShortcut( m_currentEvent.modifiers, m_currentEvent.keyCode, true );
			}

			if( m_currentEvent.control && m_currentEvent.shift && m_currentEvent.keyCode == KeyCode.V )
			{
				PasteFromClipboard( false );
			}

			if( !m_altPressDown && ( OnKeyboardPress( KeyCode.LeftAlt ) || OnKeyboardPress( KeyCode.RightAlt ) || OnKeyboardPress( KeyCode.AltGr ) ) )
			{
				m_altPressDown = true;
				m_altAvailable = true;
				m_altKeyStartPos = m_currentMousePos2D;
			}

			if( m_currentEvent.keyCode != KeyCode.None && m_currentEvent.modifiers == EventModifiers.None )
			{
				m_lastKeyPressed = m_currentEvent.keyCode;
			}
		}

		IEnumerator m_coroutine;

		private void StartPasteRequest()
		{
			m_coroutine = SendPostCoroutine( "http://paste.amplify.pt/api/create" );
			EditorApplication.update += PasteRequest;
		}

		IEnumerator SendPostCoroutine( string url )
		{
			WWWForm form = new WWWForm();
			form.AddField( "text", Clipboard.ClipboardId + ";" + EditorPrefs.GetString( Clipboard.ClipboardId, string.Empty ) );
			form.AddField( "title", "ASE Copy" );
			form.AddField( "name", "ASE" );
			form.AddField( "private", "1" );
			form.AddField( "lang", "text" );
			form.AddField( "expire", "0" );

			UnityWebRequest www = UnityWebRequest.Post( url, form );
#if UNITY_2017_2_OR_NEWER
			www.SendWebRequest();
#else
			www.Send();
#endif

			yield return www;
		}

		public void PasteRequest()
		{
			UnityWebRequest www = (UnityWebRequest)m_coroutine.Current;
			if( !m_coroutine.MoveNext() )
			{
				if( !www.isDone )
				{
					m_coroutine.MoveNext();
				}
				else
				{
#if UNITY_2020_1_OR_NEWER
					if( www.result == UnityWebRequest.Result.ConnectionError )
#elif UNITY_2017_1_OR_NEWER
					if( www.isNetworkError )
#else
					if( www.isError )
#endif
					{
						Debug.Log( "[AmplifyShaderEditor]\n" + www.error );
					}
					else
					{
						// Print Body
						string finalURL = www.downloadHandler.text;

						if( finalURL.IndexOf( "paste.amplify.pt/view/" ) > -1 )
						{
							System.Text.RegularExpressions.Regex parser = new System.Text.RegularExpressions.Regex( @".*(http:\/\/paste.amplify.pt\/view\/)([0-9a-z]*).*", System.Text.RegularExpressions.RegexOptions.Singleline );
							finalURL = parser.Replace( finalURL, "$1raw/$2" );

							ShowMessage( "Link copied to clipboard\n"+ finalURL, consoleLog:false );
							Debug.Log( "[AmplifyShaderEditor] Link copied to clipboard\n"+ finalURL+"\n" );
							// Copy Paste to clipboard
							EditorGUIUtility.systemCopyBuffer = finalURL;
						}
						else
						{
							Debug.Log( "[AmplifyShaderEditor] Failed to generate paste:\n" + finalURL );
						}
					}
					EditorApplication.update -= PasteRequest;
				}
			}
		}

		void OnScrollWheel()
		{
			ModifyZoomSmooth( m_currentEvent.delta.y, m_currentMousePos2D );
			UseCurrentEvent();
		}

		void ModifyZoom( float zoomIncrement, Vector2 pivot )
		{
			float minCam = Mathf.Min( ( m_cameraInfo.width - ( m_nodeParametersWindow.RealWidth + m_paletteWindow.RealWidth ) ), ( m_cameraInfo.height - ( m_toolsWindow.Height ) ) );
			if( minCam < 1 )
				minCam = 1;

			float dynamicMaxZoom = m_mainGraphInstance.MaxNodeDist / minCam;

			Vector2 canvasPos = TranformPosition( pivot );
			if( zoomIncrement < 0 )
				CameraZoom = Mathf.Max( m_cameraZoom + zoomIncrement * Constants.CAMERA_ZOOM_SPEED, Constants.CAMERA_MIN_ZOOM );
			else if( CameraZoom < Mathf.Max( Constants.CAMERA_MAX_ZOOM, dynamicMaxZoom ) )
				CameraZoom = m_cameraZoom + zoomIncrement * Constants.CAMERA_ZOOM_SPEED;// Mathf.Min( m_cameraZoom + zoomIncrement * Constants.CAMERA_ZOOM_SPEED, Mathf.Max( Constants.CAMERA_MAX_ZOOM, dynamicMaxZoom ) );
			m_cameraOffset.x = pivot.x * m_cameraZoom - canvasPos.x;
			m_cameraOffset.y = pivot.y * m_cameraZoom - canvasPos.y;
		}

		void ModifyZoomSmooth( float zoomIncrement, Vector2 pivot )
		{
			if( m_smoothZoom && Mathf.Sign( m_targetZoomIncrement * zoomIncrement ) >= 0 )
				m_targetZoomIncrement += zoomIncrement;
			else
				m_targetZoomIncrement = zoomIncrement;

			m_smoothZoom = true;
			m_zoomTime = 0;

			float minCam = Mathf.Min( ( m_cameraInfo.width - ( m_nodeParametersWindow.RealWidth + m_paletteWindow.RealWidth ) ), ( m_cameraInfo.height - ( m_toolsWindow.Height ) ) );
			if( minCam < 1 )
				minCam = 1;

			float dynamicMaxZoom = m_mainGraphInstance.MaxNodeDist / minCam;
			if( m_targetZoomIncrement < 0 )
				m_targetZoom = Mathf.Max( m_cameraZoom + m_targetZoomIncrement * Constants.CAMERA_ZOOM_SPEED, Constants.CAMERA_MIN_ZOOM );
			else if( CameraZoom < Mathf.Max( Constants.CAMERA_MAX_ZOOM, dynamicMaxZoom ) )
				m_targetZoom = m_cameraZoom + m_targetZoomIncrement * Constants.CAMERA_ZOOM_SPEED;// Mathf.Min( m_cameraZoom + zoomIncrement * Constants.CAMERA_ZOOM_SPEED, Mathf.Max( Constants.CAMERA_MAX_ZOOM, dynamicMaxZoom ) );

			m_zoomPivot = pivot;
		}

		void OnSelectionChange()
		{
			ForceRepaint();
		}

		private void OnFocus()
		{
			EditorGUI.FocusTextInControl( null );
//#if UNITY_2019_1_OR_NEWER
//			m_fixOnFocus = true;
//#endif
		}

		void OnLostFocus()
		{
			m_lostFocus = true;
			m_multipleSelectionActive = false;
			m_wireReferenceUtils.InvalidateReferences();
			if( m_genericMessageUI != null )
				m_genericMessageUI.CleanUpMessageStack();
			m_nodeParametersWindow.OnLostFocus();
			m_paletteWindow.OnLostFocus();
			m_contextMenu.ResetShortcutKeyStates();
		}

		void CopyToClipboard()
		{
			m_copyPasteDeltaMul = 0;
			m_copyPasteDeltaPos = new Vector2( float.MaxValue, float.MaxValue );
			m_clipboard.ClearClipboard();
			m_copyPasteInitialPos = m_mainGraphInstance.SelectedNodesCentroid;
			m_clipboard.AddToClipboard( m_mainGraphInstance.SelectedNodes, m_copyPasteInitialPos, m_mainGraphInstance );
		}

		ParentNode CreateNodeFromClipboardData( int clipId )
		{
			string[] parameters = m_clipboard.CurrentClipboardStrData[ clipId ].Data.Split( IOUtils.FIELD_SEPARATOR );
			System.Type nodeType = System.Type.GetType( parameters[ IOUtils.NodeTypeId ] );
			NodeAttributes attributes = m_contextMenu.GetNodeAttributesForType( nodeType );
			if( attributes != null && !UIUtils.GetNodeAvailabilityInBitArray( attributes.NodeAvailabilityFlags, m_mainGraphInstance.CurrentCanvasMode ) && !UIUtils.GetNodeAvailabilityInBitArray( attributes.NodeAvailabilityFlags, m_currentNodeAvailability ) )
				return null;

			ParentNode newNode = (ParentNode)ScriptableObject.CreateInstance( nodeType );
			newNode.IsNodeBeingCopied = true;
			if( newNode != null )
			{
				newNode.ContainerGraph = m_mainGraphInstance;
				newNode.ClipboardFullReadFromString( ref parameters );
				m_mainGraphInstance.AddNode( newNode, true, true, true, false );
				newNode.IsNodeBeingCopied = false;
				m_clipboard.CurrentClipboardStrData[ clipId ].NewNodeId = newNode.UniqueId;
				return newNode;
			}
			return null;
		}

		void CreateConnectionsFromClipboardData( int clipId )
		{
			if( String.IsNullOrEmpty( m_clipboard.CurrentClipboardStrData[ clipId ].Connections ) )
				return;
			string[] lines = m_clipboard.CurrentClipboardStrData[ clipId ].Connections.Split( IOUtils.LINE_TERMINATOR );

			for( int lineIdx = 0; lineIdx < lines.Length; lineIdx++ )
			{
				string[] parameters = lines[ lineIdx ].Split( IOUtils.FIELD_SEPARATOR );

				int InNodeId = 0;
				int InPortId = 0;
				int OutNodeId = 0;
				int OutPortId = 0;

				try
				{
					InNodeId = Convert.ToInt32( parameters[ IOUtils.InNodeId ] );
					InPortId = Convert.ToInt32( parameters[ IOUtils.InPortId ] );

					OutNodeId = Convert.ToInt32( parameters[ IOUtils.OutNodeId ] );
					OutPortId = Convert.ToInt32( parameters[ IOUtils.OutPortId ] );
				}
				catch( Exception e )
				{
					Debug.LogException( e );
				}


				int newInNodeId = m_clipboard.GeNewNodeId( InNodeId );
				int newOutNodeId = m_clipboard.GeNewNodeId( OutNodeId );

				if( newInNodeId > -1 && newOutNodeId > -1 )
				{
					ParentNode inNode = m_mainGraphInstance.GetNode( newInNodeId );
					ParentNode outNode = m_mainGraphInstance.GetNode( newOutNodeId );

					InputPort inputPort = null;
					OutputPort outputPort = null;

					if( inNode != null && outNode != null )
					{
						inNode.IsNodeBeingCopied = true;
						outNode.IsNodeBeingCopied = true;
						inputPort = inNode.GetInputPortByUniqueId( InPortId );
						outputPort = outNode.GetOutputPortByUniqueId( OutPortId );
						if( inputPort != null && outputPort != null )
						{
							inputPort.ConnectTo( newOutNodeId, OutPortId, outputPort.DataType, false );
							outputPort.ConnectTo( newInNodeId, InPortId, inputPort.DataType, inputPort.TypeLocked );

							inNode.OnInputPortConnected( InPortId, newOutNodeId, OutPortId );
							outNode.OnOutputPortConnected( OutPortId, newInNodeId, InPortId );
						}

						inNode.IsNodeBeingCopied = false;
						outNode.IsNodeBeingCopied = false;
					}
				}
			}
		}

		private void StartGetRequest( string url )
		{
			m_coroutine = SendGetCoroutine( url );
			EditorApplication.update += GetRequest;
		}

		IEnumerator SendGetCoroutine( string url )
		{
			UnityWebRequest www = UnityWebRequest.Get( url );
#if UNITY_2017_2_OR_NEWER
			www.SendWebRequest();
#else
			www.Send();
#endif

			yield return www;
		}

		public void GetRequest()
		{
			UnityWebRequest www = (UnityWebRequest)m_coroutine.Current;
			if( !m_coroutine.MoveNext() )
			{
				if( !www.isDone )
				{
					m_coroutine.MoveNext();
				}
				else
				{
#if UNITY_2020_1_OR_NEWER
					if( www.result == UnityWebRequest.Result.ConnectionError )
#elif UNITY_2017_1_OR_NEWER
					if( www.isNetworkError )
#else
					if( www.isError )
#endif
					{
						Debug.Log( "[AmplifyShaderEditor]\n" + www.error );
					}
					else
					{
						string data = www.downloadHandler.text;
						if( data.IndexOf( Clipboard.ClipboardId + ";" ) > -1 )
						{
							data = www.downloadHandler.text.Replace( Clipboard.ClipboardId + ";", "" );
							if( data.IndexOf( "<div " ) > -1 )
							{
								System.Text.RegularExpressions.Regex parser = new System.Text.RegularExpressions.Regex( @"(.*)<div .*", System.Text.RegularExpressions.RegexOptions.Singleline );
								data = parser.Replace( data, "$1" );
							}
							EditorGUIUtility.systemCopyBuffer = string.Empty;
							Debug.Log( "[AmplifyShaderEditor] Successfully downloaded snippet!" );
							EditorPrefs.SetString( Clipboard.ClipboardId, data );
							try
							{
								// send paste event instead to make sure it runs properly
								Event e = new Event();
								e.type = EventType.ExecuteCommand;
								e.commandName = PasteCommand;
								this.SendEvent( e );
							}
							catch( Exception )
							{
								EditorGUIUtility.systemCopyBuffer = string.Empty;
								EditorApplication.update -= GetRequest;
								throw;
							}
						}
						else
						{
							Debug.Log( "[AmplifyShaderEditor] Error downloading, snippet might not exist anymore, clearing clipboard..." );
							EditorGUIUtility.systemCopyBuffer = string.Empty;
						}
					}
					EditorApplication.update -= GetRequest;
				}
			}
		}

		void PasteFromClipboard( bool copyConnections )
		{
			string result = EditorGUIUtility.systemCopyBuffer;
			if( result.IndexOf( "http://paste.amplify.pt/view/raw/" ) > -1 )
			{
				StartGetRequest( result );
				return;
			}

			if( result.IndexOf( Clipboard.ClipboardId + ";" ) > -1 )
			{
				result = result.Replace( Clipboard.ClipboardId + ";", "" );
				EditorPrefs.SetString( Clipboard.ClipboardId, result );
			}

			m_mainGraphInstance.IsDuplicating = true;
			m_copyPasteInitialPos = m_clipboard.GetDataFromEditorPrefs();
			if( m_clipboard.CurrentClipboardStrData.Count == 0 )
			{
				return;
			}

			Vector2 deltaPos = TranformedKeyEvtMousePos - m_copyPasteInitialPos;
			if( ( m_copyPasteDeltaPos - deltaPos ).magnitude > 5.0f )
			{
				m_copyPasteDeltaMul = 0;
			}
			else
			{
				m_copyPasteDeltaMul += 1;
			}
			m_copyPasteDeltaPos = deltaPos;

			m_mainGraphInstance.DeSelectAll();
			UIUtils.InhibitMessages = true;

			if( m_clipboard.CurrentClipboardStrData.Count > 0 )
			{
				UIUtils.MarkUndoAction();
				Undo.RegisterCompleteObjectUndo( this, Constants.UndoPasteNodeId );
			}

			List<ParentNode> createdNodes = new List<ParentNode>();
			for( int i = 0; i < m_clipboard.CurrentClipboardStrData.Count; i++ )
			{
				ParentNode node = CreateNodeFromClipboardData( i );
				if( node != null )
				{
					m_clipboard.CurrentClipboardStrData[ i ].NewNodeId = node.UniqueId;
					Vector2 pos = node.Vec2Position;
					node.Vec2Position = pos + deltaPos + m_copyPasteDeltaMul * Constants.CopyPasteDeltaPos;
					//node.RefreshExternalReferences();
					node.AfterDuplication();
					createdNodes.Add( node );
					m_mainGraphInstance.SelectNode( node, true, false );
				}
			}

			if( copyConnections )
			{
				for( int i = 0; i < m_clipboard.CurrentClipboardStrData.Count; i++ )
				{
					CreateConnectionsFromClipboardData( i );
				}
			}

			// Refresh external references must always be called after all nodes are created
			for( int i = 0; i < createdNodes.Count; i++ )
			{
				createdNodes[ i ].RefreshExternalReferences();
			}
			createdNodes.Clear();
			createdNodes = null;
			//Need to force increment on Undo because if not Undo may incorrectly group consecutive pastes
			Undo.IncrementCurrentGroup();

			UIUtils.InhibitMessages = false;
			ShaderIsModified = true;
			SetSaveIsDirty();
			ForceRepaint();
			m_mainGraphInstance.IsDuplicating = false;
		}

		public string GenerateGraphInfo()
		{
			string graphInfo = IOUtils.ShaderBodyBegin + '\n';
			string nodesInfo = "";
			string connectionsInfo = "";
			graphInfo += VersionInfo.FullLabel + '\n';
			graphInfo += (
							m_cameraInfo.x.ToString() + IOUtils.FIELD_SEPARATOR +
							m_cameraInfo.y.ToString() + IOUtils.FIELD_SEPARATOR +
							m_cameraInfo.width.ToString() + IOUtils.FIELD_SEPARATOR +
							m_cameraInfo.height.ToString() + IOUtils.FIELD_SEPARATOR +
							m_cameraOffset.x.ToString() + IOUtils.FIELD_SEPARATOR +
							m_cameraOffset.y.ToString() + IOUtils.FIELD_SEPARATOR +
							m_cameraZoom.ToString() + IOUtils.FIELD_SEPARATOR +
							m_nodeParametersWindow.IsMaximized + IOUtils.FIELD_SEPARATOR +
							m_paletteWindow.IsMaximized + '\n'
							);
			m_mainGraphInstance.OrderNodesByGraphDepth();
			m_mainGraphInstance.WriteToString( ref nodesInfo, ref connectionsInfo );
			graphInfo += nodesInfo;
			graphInfo += connectionsInfo;
			graphInfo += IOUtils.ShaderBodyEnd + '\n';

			return graphInfo;
		}

		// TODO: this need to be fused to the main load function somehow
		public static void LoadFromMeta( ref ParentGraph graph, GraphContextMenu contextMenu, string meta )
		{
			graph.IsLoading = true;
			graph.CleanNodes();

			int checksumId = meta.IndexOf( IOUtils.CHECKSUM );
			if( checksumId > -1 )
			{
				string checkSumStoredValue = meta.Substring( checksumId );
				string trimmedBuffer = meta.Remove( checksumId );

				string[] typeValuePair = checkSumStoredValue.Split( IOUtils.VALUE_SEPARATOR );
				if( typeValuePair != null && typeValuePair.Length == 2 )
				{
					// Check read checksum and compare with the actual shader body to detect external changes
					string currentChecksumValue = IOUtils.CreateChecksum( trimmedBuffer );
					if( DebugConsoleWindow.DeveloperMode && !currentChecksumValue.Equals( typeValuePair[ 1 ] ) )
					{
						//ShowMessage( "Wrong checksum" );
					}

					trimmedBuffer = trimmedBuffer.Replace( "\r", string.Empty );
					// find node info body
					int shaderBodyId = trimmedBuffer.IndexOf( IOUtils.ShaderBodyBegin );
					if( shaderBodyId > -1 )
					{
						trimmedBuffer = trimmedBuffer.Substring( shaderBodyId );
						//Find set of instructions
						string[] instructions = trimmedBuffer.Split( IOUtils.LINE_TERMINATOR );
						// First line is to be ignored and second line contains version
						string[] versionParams = instructions[ 1 ].Split( IOUtils.VALUE_SEPARATOR );
						if( versionParams.Length == 2 )
						{
							int version = 0;
							try
							{
								version = Convert.ToInt32( versionParams[ 1 ] );
							}
							catch( Exception e )
							{
								Debug.LogException( e );
							}

							//if( version > versionInfo.FullNumber )
							//{
							//ShowMessage( "This shader was created on a new ASE version\nPlease install v." + version );
							//}

							if( DebugConsoleWindow.DeveloperMode )
							{
								//if( version < versionInfo.FullNumber )
								//{
								//ShowMessage( "This shader was created on a older ASE version\nSaving will update it to the new one." );
								//}
							}

							graph.LoadedShaderVersion = version;
						}
						else
						{
							//ShowMessage( "Corrupted version" );
						}

						// Dummy values,camera values can only be applied after node loading is complete
						Rect dummyCameraInfo = new Rect();
						Vector2 dummyCameraOffset = new Vector2();
						//float dummyCameraZoom = 0;
						//bool applyDummy = false;
						//bool dummyNodeParametersWindowMaximized = false;
						//bool dummyPaletteWindowMaximized = false;

						//Second line contains camera information ( position, size, offset and zoom )
						string[] cameraParams = instructions[ 2 ].Split( IOUtils.FIELD_SEPARATOR );
						if( cameraParams.Length == 9 )
						{
							//applyDummy = true;
							try
							{
								dummyCameraInfo.x = Convert.ToSingle( cameraParams[ 0 ] );
								dummyCameraInfo.y = Convert.ToSingle( cameraParams[ 1 ] );
								dummyCameraInfo.width = Convert.ToSingle( cameraParams[ 2 ] );
								dummyCameraInfo.height = Convert.ToSingle( cameraParams[ 3 ] );
								dummyCameraOffset.x = Convert.ToSingle( cameraParams[ 4 ] );
								dummyCameraOffset.y = Convert.ToSingle( cameraParams[ 5 ] );

								//dummyCameraZoom = Convert.ToSingle( cameraParams[ 6 ] );
								//dummyNodeParametersWindowMaximized = Convert.ToBoolean( cameraParams[ 7 ] );
								//dummyPaletteWindowMaximized = Convert.ToBoolean( cameraParams[ 8 ] );
							}
							catch( Exception e )
							{
								Debug.LogException( e );
							}
						}
						else
						{
							//ShowMessage( "Camera parameters are corrupted" );
						}

						// valid instructions are only between the line after version and the line before the last one ( which contains ShaderBodyEnd ) 
						for( int instructionIdx = 3; instructionIdx < instructions.Length - 1; instructionIdx++ )
						{
							//TODO: After all is working, convert string parameters to ints in order to speed up reading
							string[] parameters = instructions[ instructionIdx ].Split( IOUtils.FIELD_SEPARATOR );

							// All nodes must be created before wiring the connections ... 
							// Since all nodes on the save op are written before the wires, we can safely create them
							// If that order is not maintained the it's because of external editing and its the users responsability
							switch( parameters[ 0 ] )
							{
								case IOUtils.NodeParam:
								{
									string typeStr = parameters[ IOUtils.NodeTypeId ];
									typeStr = IOUtils.NodeTypeReplacer.ContainsKey( typeStr ) ? IOUtils.NodeTypeReplacer[ typeStr ] : typeStr;
									System.Type type = System.Type.GetType( typeStr );
									if( type == null )
									{
#if UNITY_2017_3_OR_NEWER
										try
										{
											var editorAssembly = System.Reflection.Assembly.Load( "Assembly-CSharp-Editor" );
											if( editorAssembly != null )
											{
												type = editorAssembly.GetType( typeStr );
											}
										}
										catch( Exception )
										{

										}
#endif
									}
									if( type != null )
									{
										System.Type oldType = type;
										NodeAttributes attribs = contextMenu.GetNodeAttributesForType( type );
										if( attribs == null )
										{
											attribs = contextMenu.GetDeprecatedNodeAttributesForType( type );
											if( attribs != null )
											{
												if( attribs.Deprecated && attribs.DeprecatedAlternativeType != null )
												{
													type = attribs.DeprecatedAlternativeType;
													//ShowMessage( string.Format( "Node {0} is deprecated and was replaced by {1} ", attribs.Name, attribs.DeprecatedAlternative ) );
												}
											}
										}

										ParentNode newNode = (ParentNode)ScriptableObject.CreateInstance( type );
										if( newNode != null )
										{
											try
											{
												newNode.ContainerGraph = graph;
												if( oldType != type )
												{
													newNode.ParentReadFromString( ref parameters );
													newNode.ReadFromDeprecated( ref parameters, oldType );
													newNode.WasDeprecated = true;
												}
												else
													newNode.ReadFromString( ref parameters );


												if( oldType == type )
												{
													newNode.ReadInputDataFromString( ref parameters );
													if( UIUtils.CurrentShaderVersion() > 5107 )
													{
														newNode.ReadOutputDataFromString( ref parameters );
													}
												}
											}
											catch( Exception e )
											{
												Debug.LogException( e, newNode );
											}
											graph.AddNode( newNode, false, true, false );
										}
									}
									else
									{
										UIUtils.ShowMessage( string.Format( "{0} is not a valid ASE node ", parameters[ IOUtils.NodeTypeId ] ), MessageSeverity.Error );
									}
								}
								break;
								case IOUtils.WireConnectionParam:
								{
									int InNodeId = 0;
									int InPortId = 0;
									int OutNodeId = 0;
									int OutPortId = 0;

									try
									{
										InNodeId = Convert.ToInt32( parameters[ IOUtils.InNodeId ] );
										InPortId = Convert.ToInt32( parameters[ IOUtils.InPortId ] );
										OutNodeId = Convert.ToInt32( parameters[ IOUtils.OutNodeId ] );
										OutPortId = Convert.ToInt32( parameters[ IOUtils.OutPortId ] );
									}
									catch( Exception e )
									{
										Debug.LogException( e );
									}

									ParentNode inNode = graph.GetNode( InNodeId );
									ParentNode outNode = graph.GetNode( OutNodeId );

									//if ( UIUtils.CurrentShaderVersion() < 5002 )
									//{
									//	InPortId = inNode.VersionConvertInputPortId( InPortId );
									//	OutPortId = outNode.VersionConvertOutputPortId( OutPortId );
									//}

									InputPort inputPort = null;
									OutputPort outputPort = null;
									if( inNode != null && outNode != null )
									{

										if( UIUtils.CurrentShaderVersion() < 5002 )
										{
											InPortId = inNode.VersionConvertInputPortId( InPortId );
											OutPortId = outNode.VersionConvertOutputPortId( OutPortId );

											if( inNode.WasDeprecated )
												InPortId = inNode.InputIdFromDeprecated( InPortId );
											if( outNode.WasDeprecated )
												OutPortId = outNode.OutputIdFromDeprecated( OutPortId );

											inputPort = inNode.GetInputPortByArrayId( InPortId );
											outputPort = outNode.GetOutputPortByArrayId( OutPortId );
										}
										else
										{
											if( inNode.WasDeprecated )
												InPortId = inNode.InputIdFromDeprecated( InPortId );
											if( outNode.WasDeprecated )
												OutPortId = outNode.OutputIdFromDeprecated( OutPortId );

											inputPort = inNode.GetInputPortByUniqueId( InPortId );
											outputPort = outNode.GetOutputPortByUniqueId( OutPortId );
										}

										if( inputPort != null && outputPort != null )
										{
											bool inputCompatible = inputPort.CheckValidType( outputPort.DataType );
											bool outputCompatible = outputPort.CheckValidType( inputPort.DataType );
											if( inputCompatible && outputCompatible )
											{
												inputPort.ConnectTo( OutNodeId, OutPortId, outputPort.DataType, false );
												outputPort.ConnectTo( InNodeId, InPortId, inputPort.DataType, inputPort.TypeLocked );

												inNode.OnInputPortConnected( InPortId, OutNodeId, OutPortId, false );
												outNode.OnOutputPortConnected( OutPortId, InNodeId, InPortId );
											}
											else if( DebugConsoleWindow.DeveloperMode )
											{
												if( !inputCompatible )
													UIUtils.ShowIncompatiblePortMessage( true, inNode, inputPort, outNode, outputPort );

												if( !outputCompatible )
													UIUtils.ShowIncompatiblePortMessage( true, outNode, outputPort, inNode, inputPort );
											}
										}
										else if( DebugConsoleWindow.DeveloperMode )
										{
											if( inputPort == null )
											{
												UIUtils.ShowMessage( "Input Port " + InPortId + " doesn't exist on node " + InNodeId, MessageSeverity.Error );
											}
											else
											{
												UIUtils.ShowMessage( "Output Port " + OutPortId + " doesn't exist on node " + OutNodeId, MessageSeverity.Error );
											}
										}
									}
									else if( DebugConsoleWindow.DeveloperMode )
									{
										if( inNode == null )
										{
											UIUtils.ShowMessage( "Input node " + InNodeId + " doesn't exist", MessageSeverity.Error );
										}
										else
										{
											UIUtils.ShowMessage( "Output node " + OutNodeId + " doesn't exist", MessageSeverity.Error );
										}
									}
								}
								break;
							}
						}
					}
				}
			}

			graph.CheckForDuplicates();
			graph.UpdateRegisters();
			graph.RefreshExternalReferences();
			graph.ForceSignalPropagationOnMasterNode();
			graph.LoadedShaderVersion = VersionInfo.FullNumber;
			//Reset();
			graph.IsLoading = false;
		}

		public ShaderLoadResult LoadFromDisk( string pathname, AmplifyShaderFunction shaderFunction = null )
		{
			m_mainGraphInstance.IsLoading = true;
			System.Threading.Thread.CurrentThread.CurrentCulture = System.Globalization.CultureInfo.InvariantCulture;

			FullCleanUndoStack();
			m_performFullUndoRegister = true;

			UIUtils.DirtyMask = false;
			if( UIUtils.IsUnityNativeShader( pathname ) )
			{
				ShowMessage( "Cannot edit native unity shaders.\nReplacing by a new one." );
				return ShaderLoadResult.UNITY_NATIVE_PATHS;
			}

			m_lastOpenedLocation = pathname;
			Lastpath = pathname;

			string buffer = string.Empty;
			if( shaderFunction == null )
				buffer = IOUtils.LoadTextFileFromDisk( pathname );
			else
				buffer = shaderFunction.FunctionInfo;

			if( String.IsNullOrEmpty( buffer ) )
			{
				ShowMessage( "Could not open file " + pathname );
				return ShaderLoadResult.FILE_NOT_FOUND;
			}

			if( !IOUtils.HasValidShaderBody( ref buffer ) )
			{
				return ShaderLoadResult.ASE_INFO_NOT_FOUND;
			}

			m_mainGraphInstance.CleanNodes();
			Reset();

			Shader shader = null;
			ShaderLoadResult loadResult = ShaderLoadResult.LOADED;
			// Find checksum value on body
			int checksumId = buffer.IndexOf( IOUtils.CHECKSUM );
			if( checksumId > -1 )
			{
				string checkSumStoredValue = buffer.Substring( checksumId );
				string trimmedBuffer = buffer.Remove( checksumId );

				string[] typeValuePair = checkSumStoredValue.Split( IOUtils.VALUE_SEPARATOR );
				if( typeValuePair != null && typeValuePair.Length == 2 )
				{
					// Check read checksum and compare with the actual shader body to detect external changes
					string currentChecksumValue = IOUtils.CreateChecksum( trimmedBuffer );
					if( DebugConsoleWindow.DeveloperMode && !currentChecksumValue.Equals( typeValuePair[ 1 ] ) )
					{
						ShowMessage( "Wrong checksum" );
					}

					trimmedBuffer = trimmedBuffer.Replace( "\r", string.Empty );
					// find node info body
					int shaderBodyId = trimmedBuffer.IndexOf( IOUtils.ShaderBodyBegin );
					if( shaderBodyId > -1 )
					{
						trimmedBuffer = trimmedBuffer.Substring( shaderBodyId );
						//Find set of instructions
						string[] instructions = trimmedBuffer.Split( IOUtils.LINE_TERMINATOR );
						// First line is to be ignored and second line contains version
						string[] versionParams = instructions[ 1 ].Split( IOUtils.VALUE_SEPARATOR );
						if( versionParams.Length == 2 )
						{
							int version = 0;
							try
							{
								version = Convert.ToInt32( versionParams[ 1 ] );
							}
							catch( Exception e )
							{
								Debug.LogException( e );
							}

							if( version > VersionInfo.FullNumber )
							{
								ShowMessage( "This shader was created on a new ASE version\nPlease install v." + version );
							}

							if( DebugConsoleWindow.DeveloperMode )
							{
								if( version < VersionInfo.FullNumber )
								{
									ShowMessage( "This shader was created on a older ASE version\nSaving will update it to the new one." );
								}
							}

							m_mainGraphInstance.LoadedShaderVersion = version;
						}
						else
						{
							ShowMessage( "Corrupted version" );
						}

						// Dummy values,camera values can only be applied after node loading is complete
						Rect dummyCameraInfo = new Rect();
						Vector2 dummyCameraOffset = new Vector2();
						float dummyCameraZoom = 0;
						bool applyDummy = false;
						bool dummyNodeParametersWindowMaximized = false;
						bool dummyPaletteWindowMaximized = false;

						//Second line contains camera information ( position, size, offset and zoom )
						string[] cameraParams = instructions[ 2 ].Split( IOUtils.FIELD_SEPARATOR );
						if( cameraParams.Length == 9 )
						{
							applyDummy = true;
							try
							{
								dummyCameraInfo.x = Convert.ToSingle( cameraParams[ 0 ] );
								dummyCameraInfo.y = Convert.ToSingle( cameraParams[ 1 ] );
								dummyCameraInfo.width = Convert.ToSingle( cameraParams[ 2 ] );
								dummyCameraInfo.height = Convert.ToSingle( cameraParams[ 3 ] );
								dummyCameraOffset.x = Convert.ToSingle( cameraParams[ 4 ] );
								dummyCameraOffset.y = Convert.ToSingle( cameraParams[ 5 ] );
								dummyCameraZoom = Convert.ToSingle( cameraParams[ 6 ] );

								float centerWidth = ( this.position.width - dummyCameraInfo.width ) * 0.5f * dummyCameraZoom;
								float centerHeight = ( this.position.height - dummyCameraInfo.height ) * 0.5f * dummyCameraZoom;

								dummyCameraInfo.x += centerWidth;
								dummyCameraOffset.x += centerWidth;
								dummyCameraInfo.y += centerHeight;
								dummyCameraOffset.y += centerHeight;
								dummyNodeParametersWindowMaximized = Convert.ToBoolean( cameraParams[ 7 ] );
								dummyPaletteWindowMaximized = Convert.ToBoolean( cameraParams[ 8 ] );
							}
							catch( Exception e )
							{
								Debug.LogException( e );
							}
						}
						else
						{
							ShowMessage( "Camera parameters are corrupted" );
						}

						// valid instructions are only between the line after version and the line before the last one ( which contains ShaderBodyEnd ) 
						for( int instructionIdx = 3; instructionIdx < instructions.Length - 1; instructionIdx++ )
						{
							//TODO: After all is working, convert string parameters to ints in order to speed up reading
							string[] parameters = instructions[ instructionIdx ].Split( IOUtils.FIELD_SEPARATOR );

							// All nodes must be created before wiring the connections ... 
							// Since all nodes on the save op are written before the wires, we can safely create them
							// If that order is not maintained the it's because of external editing and its the users responsability
							switch( parameters[ 0 ] )
							{
								case IOUtils.NodeParam:
								{
									string typeStr = parameters[ IOUtils.NodeTypeId ];
									typeStr = IOUtils.NodeTypeReplacer.ContainsKey( typeStr ) ? IOUtils.NodeTypeReplacer[ typeStr ] : typeStr;
									System.Type type = System.Type.GetType( typeStr );
									if( type == null )
									{
#if UNITY_2017_3_OR_NEWER
										try
										{
											var editorAssembly = System.Reflection.Assembly.Load( "Assembly-CSharp-Editor" );
											if( editorAssembly != null )
											{
												type = editorAssembly.GetType( typeStr );
											}
										}
										catch( Exception )
										{
									
										}
#endif
									}

									if( type != null )
									{
										System.Type oldType = type;
										NodeAttributes attribs = m_contextMenu.GetNodeAttributesForType( type );
										if( attribs == null )
										{
											attribs = m_contextMenu.GetDeprecatedNodeAttributesForType( type );
											if( attribs != null )
											{
												if( attribs.Deprecated )
												{
													if( attribs.DeprecatedAlternativeType != null )
													{
														type = attribs.DeprecatedAlternativeType;
														ShowMessage( string.Format( "Node {0} is deprecated and was replaced by {1} ", attribs.Name, attribs.DeprecatedAlternative ) );
													}
													else
													{
														if( string.IsNullOrEmpty( attribs.DeprecatedAlternative ) )
															ShowMessage( string.Format( Constants.DeprecatedNoAlternativeMessageStr, attribs.Name, attribs.DeprecatedAlternative ), MessageSeverity.Normal, false );
														else
															ShowMessage( string.Format( Constants.DeprecatedMessageStr, attribs.Name, attribs.DeprecatedAlternative ), MessageSeverity.Normal, false );
													}
												}
											}
										}

										ParentNode newNode = (ParentNode)ScriptableObject.CreateInstance( type );
										if( newNode != null )
										{
											try
											{
												newNode.ContainerGraph = m_mainGraphInstance;
												if( oldType != type )
												{
													newNode.ParentReadFromString( ref parameters );
													newNode.ReadFromDeprecated( ref parameters, oldType );
													newNode.WasDeprecated = true;
												}
												else
													newNode.ReadFromString( ref parameters );


												if( oldType == type )
												{
													newNode.ReadInputDataFromString( ref parameters );
													if( UIUtils.CurrentShaderVersion() > 5107 )
													{
														newNode.ReadOutputDataFromString( ref parameters );
													}
												}
											}
											catch( Exception e )
											{
												Debug.LogException( e, newNode );
											}
											m_mainGraphInstance.AddNode( newNode, false, true, false );
										}
									}
									else
									{
										ShowMessage( string.Format( "{0} is not a valid ASE node ", parameters[ IOUtils.NodeTypeId ] ), MessageSeverity.Error );
									}
								}
								break;
								case IOUtils.WireConnectionParam:
								{
									int InNodeId = 0;
									int InPortId = 0;
									int OutNodeId = 0;
									int OutPortId = 0;

									try
									{
										InNodeId = Convert.ToInt32( parameters[ IOUtils.InNodeId ] );
										InPortId = Convert.ToInt32( parameters[ IOUtils.InPortId ] );
										OutNodeId = Convert.ToInt32( parameters[ IOUtils.OutNodeId ] );
										OutPortId = Convert.ToInt32( parameters[ IOUtils.OutPortId ] );
									}
									catch( Exception e )
									{
										Debug.LogException( e );
									}

									ParentNode inNode = m_mainGraphInstance.GetNode( InNodeId );
									ParentNode outNode = m_mainGraphInstance.GetNode( OutNodeId );

									//if ( UIUtils.CurrentShaderVersion() < 5002 )
									//{
									//	InPortId = inNode.VersionConvertInputPortId( InPortId );
									//	OutPortId = outNode.VersionConvertOutputPortId( OutPortId );
									//}

									InputPort inputPort = null;
									OutputPort outputPort = null;
									if( inNode != null && outNode != null )
									{

										if( UIUtils.CurrentShaderVersion() < 5002 )
										{
											InPortId = inNode.VersionConvertInputPortId( InPortId );
											OutPortId = outNode.VersionConvertOutputPortId( OutPortId );

											if( inNode.WasDeprecated )
												InPortId = inNode.InputIdFromDeprecated( InPortId );
											if( outNode.WasDeprecated )
												OutPortId = outNode.OutputIdFromDeprecated( OutPortId );

											inputPort = inNode.GetInputPortByArrayId( InPortId );
											outputPort = outNode.GetOutputPortByArrayId( OutPortId );
										}
										else
										{
											if( inNode.WasDeprecated )
												InPortId = inNode.InputIdFromDeprecated( InPortId );
											if( outNode.WasDeprecated )
												OutPortId = outNode.OutputIdFromDeprecated( OutPortId );

											inputPort = inNode.GetInputPortByUniqueId( InPortId );
											outputPort = outNode.GetOutputPortByUniqueId( OutPortId );
										}

										if( inputPort != null && outputPort != null )
										{
											bool inputCompatible = inputPort.CheckValidType( outputPort.DataType );
											bool outputCompatible = outputPort.CheckValidType( inputPort.DataType );
											if( inputCompatible && outputCompatible )
											{
												inputPort.ConnectTo( OutNodeId, OutPortId, outputPort.DataType, false );
												outputPort.ConnectTo( InNodeId, InPortId, inputPort.DataType, inputPort.TypeLocked );

												inNode.OnInputPortConnected( InPortId, OutNodeId, OutPortId, false );
												outNode.OnOutputPortConnected( OutPortId, InNodeId, InPortId );
											}
											else if( DebugConsoleWindow.DeveloperMode )
											{
												if( !inputCompatible )
													UIUtils.ShowIncompatiblePortMessage( true, inNode, inputPort, outNode, outputPort );

												if( !outputCompatible )
													UIUtils.ShowIncompatiblePortMessage( true, outNode, outputPort, inNode, inputPort );
											}
										}
										else if( DebugConsoleWindow.DeveloperMode )
										{
											if( inputPort == null )
											{
												UIUtils.ShowMessage( "Input Port " + InPortId + " doesn't exist on node " + InNodeId, MessageSeverity.Error );
											}
											else
											{
												UIUtils.ShowMessage( "Output Port " + OutPortId + " doesn't exist on node " + OutNodeId, MessageSeverity.Error );
											}
										}
									}
									else if( DebugConsoleWindow.DeveloperMode )
									{
										if( inNode == null )
										{
											UIUtils.ShowMessage( "Input node " + InNodeId + " doesn't exist", MessageSeverity.Error );
										}
										else
										{
											UIUtils.ShowMessage( "Output node " + OutNodeId + " doesn't exist", MessageSeverity.Error );
										}
									}
								}
								break;
							}
						}

						if( shaderFunction != null )
						{
							m_onLoadDone = 2;
							if( applyDummy )
							{
								m_cameraInfo = dummyCameraInfo;
								m_cameraOffset = dummyCameraOffset;
								CameraZoom = dummyCameraZoom;
								if( DebugConsoleWindow.UseShaderPanelsInfo )
								{
									m_nodeParametersWindowMaximized = m_nodeParametersWindow.IsMaximized = dummyNodeParametersWindowMaximized;
									m_paletteWindowMaximized = m_paletteWindow.IsMaximized = dummyPaletteWindowMaximized;
								}
							}

						}
						else
						{
							shader = AssetDatabase.LoadAssetAtPath<Shader>( pathname );
							if( shader )
							{

								m_onLoadDone = 2;
								if( applyDummy )
								{
									m_cameraInfo = dummyCameraInfo;
									m_cameraOffset = dummyCameraOffset;
									CameraZoom = dummyCameraZoom;
									if( DebugConsoleWindow.UseShaderPanelsInfo )
									{
										m_nodeParametersWindowMaximized = m_nodeParametersWindow.IsMaximized = dummyNodeParametersWindowMaximized;
										m_paletteWindowMaximized = m_paletteWindow.IsMaximized = dummyPaletteWindowMaximized;
									}
								}
							}
							else
							{
								ShowMessage( "Could not load shader asset" );
							}
						}
					}
					else
					{
						ShowMessage( "Graph info not found" );
					}
				}
				else
				{
					ShowMessage( "Corrupted checksum" );
				}
			}
			else
			{
				ShowMessage( "Checksum not found" );
			}

			//m_mainGraphInstance.LoadedShaderVersion = m_versionInfo.FullNumber;
			if( UIUtils.CurrentMasterNode() )
				UIUtils.CurrentMasterNode().ForcePortType();

			UIUtils.DirtyMask = true;
			m_checkInvalidConnections = true;

			m_mainGraphInstance.CheckForDuplicates();
			m_mainGraphInstance.UpdateRegisters();
			m_mainGraphInstance.RefreshExternalReferences();
			m_mainGraphInstance.ForceSignalPropagationOnMasterNode();

			if( shaderFunction != null )
			{
				//if( CurrentGraph.CurrentFunctionOutput == null )
				//{
				//	//Fix in case a function output node is not marked as main node
				//	CurrentGraph.AssignMasterNode( UIUtils.FunctionOutputList()[ 0 ], false );
				//}
				shaderFunction.ResetDirectivesOrigin();
				CurrentGraph.CurrentShaderFunction = shaderFunction;
			}
			else
			{
				if( shader != null )
				{
					m_mainGraphInstance.UpdateShaderOnMasterNode( shader );
					if( m_mainGraphInstance.CurrentCanvasMode == NodeAvailability.TemplateShader )
					{
						m_mainGraphInstance.RefreshLinkedMasterNodes( false );
						m_mainGraphInstance.OnRefreshLinkedPortsComplete();
						//m_mainGraphInstance.SetLateOptionsRefresh();
					}
				}
			}


			m_mainGraphInstance.LoadedShaderVersion = VersionInfo.FullNumber;

			System.Threading.Thread.CurrentThread.CurrentCulture = System.Threading.Thread.CurrentThread.CurrentUICulture;

			m_mainGraphInstance.IsLoading = false;
			//Remove focus from UI elements so no UI is incorrectly selected from previous loads
			//Shader Name textfield was sometimes incorrectly selected
			GUI.FocusControl( null );
			return loadResult;
		}

		public void FullCleanUndoStack()
		{
			Undo.ClearUndo( this );
			m_mainGraphInstance.FullCleanUndoStack();
		}

		public void FullRegisterOnUndoStack()
		{
			Undo.RegisterCompleteObjectUndo( this, Constants.UndoRegisterFullGrapId );
			m_mainGraphInstance.FullRegisterOnUndoStack();
		}

		public void ShowPortInfo()
		{
			GetWindow<PortLegendInfo>();
		}

		public void ShowShaderLibrary()
		{
			GetWindow<ShaderLibrary>();
		}

		public void ShowMessage( string message, MessageSeverity severity = MessageSeverity.Normal, bool registerTimestamp = true, bool consoleLog = false )
		{
			ShowMessage( -1, message, severity, registerTimestamp, consoleLog );
		}
		
		public void ShowMessage( int messageOwner, string message, MessageSeverity severity = MessageSeverity.Normal, bool registerTimestamp = true, bool consoleLog = false )
		{
			if( UIUtils.InhibitMessages || m_genericMessageUI == null )
				return;

			m_consoleLogWindow.AddMessage( severity, message , messageOwner);

			MarkToRepaint();
			
			if( consoleLog )
			{
				switch( severity )
				{
					case MessageSeverity.Normal:
					{
						Debug.Log( message );
					}
					break;
					case MessageSeverity.Warning:
					{
						Debug.LogWarning( message );
					}
					break;
					case MessageSeverity.Error:
					{
						Debug.LogError( message );
					}
					break;
				}
			}
		}

		// NOTE: this can probably be removed safely
		public void ShowMessageImmediately( string message, MessageSeverity severity = MessageSeverity.Normal, bool consoleLog = true )
		{
			if( UIUtils.InhibitMessages )
				return;

			switch( severity )
			{
				case MessageSeverity.Normal:
				{
					m_genericMessageContent.text = message;
					if( consoleLog )
					{
						Debug.Log( message );
					}
				}
				break;
				case MessageSeverity.Warning:
				{
					m_genericMessageContent.text = "Warning!\n" + message;
					if( consoleLog )
					{
						Debug.LogWarning( message );
					}
				}
				break;
				case MessageSeverity.Error:
				{
					m_genericMessageContent.text = "Error!!!\n" + message;
					if( consoleLog )
					{
						Debug.LogError( message );
					}
				}
				break;
			}

			try
			{
				ShowNotification( m_genericMessageContent );
			}
			catch( Exception e )
			{
				Debug.LogException( e );
			}
		}

		public bool MouseInteracted = false;

		void OnGUI()
		{
#if UNITY_2018_3_OR_NEWER
			if( ASEPackageManagerHelper.CheckImporter )
				return;
#endif

#if UNITY_EDITOR_WIN
			if( m_openSavedFolder && Event.current.type == EventType.Repaint )
			{
				OpenSavedFolder();
				return;
			}
#endif
			AmplifyShaderEditorWindow cacheWindow = UIUtils.CurrentWindow;
			UIUtils.CurrentWindow = this;

			if( !m_initialized || (object)UIUtils.MainSkin == null || !UIUtils.Initialized )
			{
				UIUtils.InitMainSkin();
				Init();
			}

			m_currentEvent = Event.current;
			if( m_currentEvent.type == EventType.ExecuteCommand || m_currentEvent.type == EventType.ValidateCommand )
				m_currentCommandName = m_currentEvent.commandName;
			else
				m_currentCommandName = string.Empty;

			System.Threading.Thread.CurrentThread.CurrentCulture = System.Globalization.CultureInfo.InvariantCulture;

			MouseInteracted = false;

			if( m_refreshOnUndo )
			{
				m_refreshOnUndo = false;
				m_mainGraphInstance.RefreshOnUndo();
			}

			if( m_refreshAvailableNodes )
			{
				RefreshAvaibleNodes();
			}

			if( m_previousShaderFunction != CurrentGraph.CurrentShaderFunction )
			{
				m_nodeParametersWindow.ForceUpdate = true;
				m_previousShaderFunction = CurrentGraph.CurrentShaderFunction;
			}

			if( m_nodeToFocus != null && m_currentEvent.type == EventType.Layout )
			{
				FocusOnNode( m_nodeToFocus, m_zoomToFocus, m_selectNodeToFocus );
				m_nodeToFocus = null;
			}

			m_mainGraphInstance.OnDuplicateEventWrapper();

			m_currentInactiveTime = CalculateInactivityTime();

			if( m_nodeParametersWindow != null && m_innerEditorVariables.NodeParametersMaximized != m_nodeParametersWindow.IsMaximized )
				m_innerEditorVariables.NodeParametersMaximized = m_nodeParametersWindow.IsMaximized;
			if( m_paletteWindow != null && m_innerEditorVariables.NodePaletteMaximized != m_paletteWindow.IsMaximized )
				m_innerEditorVariables.NodePaletteMaximized = m_paletteWindow.IsMaximized;

			if( m_checkInvalidConnections )
			{
				m_checkInvalidConnections = false;
				m_mainGraphInstance.DeleteInvalidConnections();
			}

			//if ( m_repaintIsDirty )
			//{
			//	m_repaintIsDirty = false;
			//	ForceRepaint();
			//}

			if( m_forcingMaterialUpdateFlag )
			{
				Focus();
				if( m_materialsToUpdate.Count > 0 )
				{
					float percentage = 100.0f * (float)( UIUtils.TotalExampleMaterials - m_materialsToUpdate.Count ) / (float)UIUtils.TotalExampleMaterials;
					if( m_forcingMaterialUpdateOp ) // Read
					{
						Debug.Log( percentage + "% Recompiling " + m_materialsToUpdate[ 0 ].name );
						LoadDroppedObject( true, m_materialsToUpdate[ 0 ].shader, m_materialsToUpdate[ 0 ] );
					}
					else // Write
					{
						Debug.Log( percentage + "% Saving " + m_materialsToUpdate[ 0 ].name );
						SaveToDisk( false );
						m_materialsToUpdate.RemoveAt( 0 );
					}
					m_forcingMaterialUpdateOp = !m_forcingMaterialUpdateOp;
				}
				else
				{
					Debug.Log( "100% - All Materials compiled " );
					m_forcingMaterialUpdateFlag = false;
				}
			}


			if( m_removedKeyboardFocus )
			{
				m_removedKeyboardFocus = false;
				GUIUtility.keyboardControl = 0;
			}


			Vector2 pos = m_currentEvent.mousePosition;
			pos.x += position.x;
			pos.y += position.y;
			m_insideEditorWindow = position.Contains( pos );

			if( m_delayedLoadObject != null && m_mainGraphInstance.CurrentMasterNode != null )
			{
				m_mainGraphInstance.SetLateOptionsRefresh();
				LoadObject( m_delayedLoadObject );
				m_delayedLoadObject = null;
			}
			else if( m_delayedLoadObject != null && m_mainGraphInstance.CurrentOutputNode != null )
			{
				m_mainGraphInstance.SetLateOptionsRefresh();
				LoadObject( m_delayedLoadObject );
				m_delayedLoadObject = null;
			}

			if( m_delayedMaterialSet != null && m_mainGraphInstance.CurrentMasterNode != null )
			{
				m_mainGraphInstance.UpdateMaterialOnMasterNode( m_delayedMaterialSet );
				m_mainGraphInstance.SetMaterialModeOnGraph( m_delayedMaterialSet );
				CurrentSelection = ASESelectionMode.Material;
				IsShaderFunctionWindow = false;
				m_delayedMaterialSet = null;
			}

			Material currentMaterial = m_mainGraphInstance.CurrentMaterial;
			if( m_forceUpdateFromMaterialFlag )
			{
				Focus();
				m_forceUpdateFromMaterialFlag = false;
				if( currentMaterial != null )
				{
					m_mainGraphInstance.CopyValuesFromMaterial( currentMaterial );
					m_repaintIsDirty = true;
				}
			}

			m_repaintCount = 0;
			m_cameraInfo = position;

			//if( m_currentEvent.type == EventType.keyDown )
			if( m_currentEvent.type == EventType.Repaint )
				m_keyEvtMousePos2D = m_currentEvent.mousePosition;

			m_currentMousePos2D = m_currentEvent.mousePosition;
			m_currentMousePos.x = m_currentMousePos2D.x;
			m_currentMousePos.y = m_currentMousePos2D.y;

			m_graphArea.width = m_cameraInfo.width;
			m_graphArea.height = m_cameraInfo.height;

			m_autoPanDirActive = m_lmbPressed || m_forceAutoPanDir || m_multipleSelectionActive || m_wireReferenceUtils.ValidReferences();


			// Need to use it in order to prevent Mismatched LayoutGroup on ValidateCommand when rendering nodes
			//if( Event.current.type == EventType.ValidateCommand )
			//{
			//	Event.current.Use();
			//}

			// Nodes Graph background area
			//GUILayout.BeginArea( m_graphArea, "Nodes" );
			{
				// Camera movement is simulated by grabing the current camera offset, transforming it into texture space and manipulating the tiled texture uv coords
				GUI.DrawTextureWithTexCoords( m_graphArea, m_graphBgTexture,
					new Rect( ( -m_cameraOffset.x / m_graphBgTexture.width ),
								( m_cameraOffset.y / m_graphBgTexture.height ) - m_cameraZoom * m_cameraInfo.height / m_graphBgTexture.height,
								m_cameraZoom * m_cameraInfo.width / m_graphBgTexture.width,
								m_cameraZoom * m_cameraInfo.height / m_graphBgTexture.height ) );

				Color col = GUI.color;
				GUI.color = new Color( 1, 1, 1, 0.7f );
				GUI.DrawTexture( m_graphArea, m_graphFgTexture, ScaleMode.StretchToFill, true );
				GUI.color = col;
			}
			//GUILayout.EndArea();

			if( DebugConsoleWindow.DeveloperMode && m_currentEvent.type == EventType.Repaint )
			{
				GUI.Label( new Rect(Screen.width - 60, 40, 60, 50), m_fpsDisplay );
			}

			bool restoreMouse = false;
			if( InsideMenus( m_currentMousePos2D ) /*|| _confirmationWindow.IsActive*/ )
			{
				if( Event.current.type == EventType.MouseDown )
				{
					restoreMouse = true;
					Event.current.type = EventType.Ignore;
				}

				// Must guarantee that mouse up ops on menus will reset auto pan if it is set
				if( m_currentEvent.type == EventType.MouseUp && m_currentEvent.button == ButtonClickId.LeftMouseButton )
				{
					m_lmbPressed = false;
				}

			}
			// Nodes
			//GUILayout.BeginArea( m_graphArea );
			{
				m_drawInfo.CameraArea = m_cameraInfo;
				m_drawInfo.TransformedCameraArea = m_graphArea;

				m_drawInfo.MousePosition = m_currentMousePos2D;
				m_drawInfo.CameraOffset = m_cameraOffset;
				m_drawInfo.InvertedZoom = 1 / m_cameraZoom;
				m_drawInfo.LeftMouseButtonPressed = m_currentEvent.button == ButtonClickId.LeftMouseButton;
				m_drawInfo.CurrentEventType = m_currentEvent.type;
				m_drawInfo.ZoomChanged = m_zoomChanged;

				m_drawInfo.TransformedMousePos = m_currentMousePos2D * m_cameraZoom - m_cameraOffset;

				if( m_drawInfo.CurrentEventType == EventType.Repaint )
					UIUtils.UpdateMainSkin( m_drawInfo );

				// Draw mode indicator
				m_modeWindow.Draw( m_graphArea, m_currentMousePos2D, m_mainGraphInstance.CurrentShader, currentMaterial,
									0.5f * ( m_graphArea.width - m_paletteWindow.RealWidth - m_nodeParametersWindow.RealWidth ),
									( m_nodeParametersWindow.IsMaximized ? m_nodeParametersWindow.RealWidth : 0 ),
									( m_paletteWindow.IsMaximized ? m_paletteWindow.RealWidth : 0 )/*, m_openedAssetFromNode*/ );

				PreTestLeftMouseDown();
				//m_consoleLogWindow.Draw( m_graphArea, m_currentMousePos2D, m_currentEvent.button, false, m_paletteWindow.IsMaximized ? m_paletteWindow.RealWidth : 0 );
				//m_mainGraphInstance.DrawBezierBoundingBox();
				//CheckNodeReplacement();

				// Main Graph Draw
				m_repaintIsDirty = m_mainGraphInstance.Draw( m_drawInfo ) || m_repaintIsDirty;

				m_mainGraphInstance.DrawGrid( m_drawInfo );
				bool hasUnusedConnNodes = m_mainGraphInstance.HasUnConnectedNodes;
				m_toolsWindow.SetStateOnButton( ToolButtonType.CleanUnusedNodes, hasUnusedConnNodes ? 1 : 0 );

				m_zoomChanged = false;

				MasterNode masterNode = m_mainGraphInstance.CurrentMasterNode;
				if( masterNode != null )
				{
					m_toolsWindow.DrawShaderTitle( m_nodeParametersWindow, m_paletteWindow, AvailableCanvasWidth, m_graphArea.height, masterNode.CroppedShaderName );
				}
				else if( m_mainGraphInstance.CurrentOutputNode != null )
				{
					string functionName = string.Empty;

					if( m_mainGraphInstance.CurrentShaderFunction != null )
						functionName = m_mainGraphInstance.CurrentShaderFunction.FunctionName;
					m_toolsWindow.DrawShaderTitle( m_nodeParametersWindow, m_paletteWindow, AvailableCanvasWidth, m_graphArea.height, functionName );
				}
			}
			//m_consoleLogWindow.Draw( m_graphArea, m_currentMousePos2D, m_currentEvent.button, false, m_paletteWindow.IsMaximized ? m_paletteWindow.RealWidth : 0 );
			//GUILayout.EndArea();

			if( restoreMouse )
			{
				Event.current.type = EventType.MouseDown;
				m_drawInfo.CurrentEventType = EventType.MouseDown;
			}

			m_toolsWindow.InitialX = m_nodeParametersWindow.RealWidth;
			m_toolsWindow.Width = m_cameraInfo.width - ( m_nodeParametersWindow.RealWidth + m_paletteWindow.RealWidth );
			m_toolsWindow.Draw( m_cameraInfo, m_currentMousePos2D, m_currentEvent.button, false );

			m_tipsWindow.Draw( m_cameraInfo, m_currentMousePos2D, m_currentEvent.button, false );

			bool autoMinimize = false;
			if( position.width < m_lastWindowWidth && position.width < Constants.MINIMIZE_WINDOW_LOCK_SIZE )
			{
				autoMinimize = true;
			}

			if( autoMinimize )
				m_nodeParametersWindow.IsMaximized = false;

			ParentNode selectedNode = ( m_mainGraphInstance.SelectedNodes.Count == 1 ) ? m_mainGraphInstance.SelectedNodes[ 0 ] : m_mainGraphInstance.CurrentMasterNode;
			m_repaintIsDirty = m_nodeParametersWindow.Draw( m_cameraInfo, selectedNode, m_currentMousePos2D, m_currentEvent.button, false ) || m_repaintIsDirty; //TODO: If multiple nodes from the same type are selected also show a parameters window which modifies all of them 
			if( m_nodeParametersWindow.IsResizing )
				m_repaintIsDirty = true;

			// Test to ignore mouse on main palette when inside context palette ... IsInside also takes active state into account 
			bool ignoreMouseForPalette = m_contextPalette.IsInside( m_currentMousePos2D );
			if( ignoreMouseForPalette && Event.current.type == EventType.MouseDown )
			{
				Event.current.type = EventType.Ignore;
				m_drawInfo.CurrentEventType = EventType.Ignore;
			}
			if( autoMinimize )
				m_paletteWindow.IsMaximized = false;

			m_paletteWindow.Draw( m_cameraInfo, m_currentMousePos2D, m_currentEvent.button, !m_contextPalette.IsActive );
			if( m_paletteWindow.IsResizing )
			{
				m_repaintIsDirty = true;
			}

			if( ignoreMouseForPalette )
			{
				if( restoreMouse )
				{
					Event.current.type = EventType.MouseDown;
					m_drawInfo.CurrentEventType = EventType.MouseDown;
				}
			}

			m_consoleLogWindow.Draw( m_graphArea, m_currentMousePos2D, m_currentEvent.button, false, m_paletteWindow.IsMaximized ? m_paletteWindow.RealWidth : 0 );

			if( m_contextPalette.IsActive )
			{
				m_contextPalette.Draw( m_cameraInfo, m_currentMousePos2D, m_currentEvent.button, m_contextPalette.IsActive );
			}

			if( m_palettePopup.IsActive )
			{
				m_palettePopup.Draw( m_currentMousePos2D );
				m_repaintIsDirty = true;
				int controlID = GUIUtility.GetControlID( FocusType.Passive );
				if( m_currentEvent.GetTypeForControl( controlID ) == EventType.MouseUp )
				{
					if( m_currentEvent.button == ButtonClickId.LeftMouseButton )
					{
						m_palettePopup.Deactivate();
						if( !InsideMenus( m_currentMousePos2D ) )
						{
							ParentNode newNode = CreateNode( m_paletteChosenType, TranformedMousePos, m_paletteChosenFunction );
							//Debug.Log("created menu");
							m_mainGraphInstance.SelectNode( newNode, false, false );

							bool find = false;
							if( newNode is FunctionNode && CurrentGraph.CurrentShaderFunction != null )
								find = SearchFunctionNodeRecursively( CurrentGraph.CurrentShaderFunction );

							if( find )
							{
								DestroyNode( newNode, false );
								ShowMessage( "Shader Function loop detected, new node was removed to prevent errors." );
							}
							else
							{
								newNode.RefreshExternalReferences();
							}
						}
					}
				}
			}

			// Handle all events ( mouse interaction + others )
			if( !MouseInteracted )
				HandleGUIEvents();

			if( m_currentEvent.type == EventType.Repaint )
			{
				m_mainGraphInstance.UpdateMarkForDeletion();
			}
			// UI Overlay
			// Selection Box
			if( m_multipleSelectionActive )
			{
				UpdateSelectionArea();
				Rect transformedArea = m_multipleSelectionArea;
				transformedArea.position = ( transformedArea.position + m_cameraOffset ) / m_cameraZoom;
				transformedArea.size /= m_cameraZoom;

				if( transformedArea.width < 0 )
				{
					transformedArea.width = -transformedArea.width;
					transformedArea.x -= transformedArea.width;
				}

				if( transformedArea.height < 0 )
				{
					transformedArea.height = -transformedArea.height;
					transformedArea.y -= transformedArea.height;
				}
				Color original = GUI.color;
				GUI.color = Constants.BoxSelectionColor;
				GUI.Label( transformedArea, "", UIUtils.Box );
				GUI.color = original;
				//GUI.backgroundColor = original;
			}

			bool isResizing = m_nodeParametersWindow.IsResizing || m_paletteWindow.IsResizing;
			//Test boundaries for auto-pan
			if( !isResizing && m_autoPanDirActive )
			{
				m_autoPanArea[ (int)AutoPanLocation.LEFT ].AdjustInitialX = m_nodeParametersWindow.IsMaximized ? m_nodeParametersWindow.RealWidth : 0;
				m_autoPanArea[ (int)AutoPanLocation.RIGHT ].AdjustInitialX = m_paletteWindow.IsMaximized ? -m_paletteWindow.RealWidth : 0;
				Vector2 autoPanDir = Vector2.zero;
				for( int i = 0; i < m_autoPanArea.Length; i++ )
				{
					if( m_autoPanArea[ i ].CheckArea( m_currentMousePos2D, m_cameraInfo, false ) )
					{
						autoPanDir += m_autoPanArea[ i ].Velocity;
					}
				}
				m_cameraOffset += autoPanDir;
				if( !m_wireReferenceUtils.ValidReferences() && m_insideEditorWindow && !m_altBoxSelection )
				{
					m_mainGraphInstance.MoveSelectedNodes( -autoPanDir );
				}

				m_repaintIsDirty = true;
			}

			m_isDirty = m_isDirty || m_mainGraphInstance.IsDirty;
			if( m_isDirty )
			{
				m_isDirty = false;
				//ShaderIsModified = true;
				EditorUtility.SetDirty( this );
			}

			m_saveIsDirty = m_saveIsDirty || m_mainGraphInstance.SaveIsDirty;
			if( m_liveShaderEditing )
			{
				if( m_saveIsDirty )
				{
					if( focusedWindow == this && m_currentInactiveTime > InactivitySaveTime && !(EditorGUIUtility.editingTextField && EditorGUIUtility.keyboardControl!=0) )
					{
						m_saveIsDirty = false;
						if( m_mainGraphInstance.CurrentMasterNodeId != Constants.INVALID_NODE_ID )
						{
							SaveToDisk( true );
						}
						else
						{
							ShowMessage( LiveShaderError );
						}
					}
				}
			}
			else if( m_saveIsDirty )
			{
				ShaderIsModified = true;
				m_saveIsDirty = false;
			}

			if( m_onLoadDone > 0 )
			{
				m_onLoadDone--;
				if( m_onLoadDone == 0 )
				{
					ShaderIsModified = false;
				}
			}

			if( m_cacheSaveOp )
			{
				if( ( EditorApplication.timeSinceStartup - m_lastTimeSaved ) > SaveTime )
				{
					SaveToDisk( false );
				}
			}
			m_genericMessageUI.CheckForMessages();

			if( m_ctrlSCallback )
			{
				m_ctrlSCallback = false;
				OnToolButtonPressed( ToolButtonType.Update );
			}

			m_lastWindowWidth = position.width;
			m_nodeExporterUtils.Update();

			if( m_markedToSave )
			{
				m_markedToSave = false;
				SaveToDisk( false );
			}
			if( m_performFullUndoRegister )
			{
				m_performFullUndoRegister = false;
				FullRegisterOnUndoStack();
			}

			if( CheckFunctions )
				CheckFunctions = false;

			System.Threading.Thread.CurrentThread.CurrentCulture = System.Threading.Thread.CurrentThread.CurrentUICulture;

			UIUtils.CurrentWindow = cacheWindow;
			if( !m_nodesLoadedCorrectly )
			{
				try
				{
					ShowNotification( NodesExceptionMessage );
				}
				catch( Exception e )
				{
					Debug.LogException( e );
				}
			}

			CheckNodeReplacement();
#if UNITY_EDITOR_WIN
			if( m_takeScreenShot )
				FocusZoom( true, false, false );

			if( m_takeScreenShot && Event.current.type == EventType.Repaint )
				TakeScreenShot();
#endif

			if( Event.current.type == EventType.Layout )
			{
				switch( State )
				{
					default:
					case OpenSaveState.NONE:
					break;
					case OpenSaveState.OPEN:
					{
						State = OpenSaveState.WAIT;
						string list = EditorPrefs.GetString( "ASEfileList", "" );
						m_assetPaths = new List<string>( list.Split( ',' ) );
						Repaint();
					}
					break;
					case OpenSaveState.WAIT:
					{
						// we wait one frame to give time for the editor to properly initialize everything
						State = OpenSaveState.SAVE;
						Repaint();
					}
					break;
					case OpenSaveState.SAVE:
					{
						State = OpenSaveState.CLOSE;
						try
						{
							SaveToDisk( false );
						}
						catch( Exception e )
						{
							State = OpenSaveState.NONE;
							throw e;
						}

						Repaint();
					}
					break;
					case OpenSaveState.CLOSE:
					{
						State = OpenSaveState.NONE;
						m_assetPaths.RemoveAt( 0 );
						if( m_assetPaths.Count > 0 )
						{
							AmplifyShaderEditorWindow.LoadAndSaveList( m_assetPaths.ToArray() );
						}
						else
						{
							EditorPrefs.DeleteKey( "ASEfileList" );
						}
#if UNITY_2018_3_OR_NEWER
						this.Close();
#else
						m_markToClose = true;
#endif
					}
					break;
				}
			}
		}

		bool m_markToClose = false;
		private List<string> m_assetPaths = new List<string>();
		public OpenSaveState State = OpenSaveState.NONE;
		public enum OpenSaveState
		{
			NONE,
			OPEN,
			WAIT,
			SAVE,
			CLOSE
		}

		void OnInspectorUpdate()
		{
			Preferences.LoadDefaults();
#if UNITY_2018_3_OR_NEWER
			ASEPackageManagerHelper.Update();
#endif

			if( m_afterDeserializeFlag )
			{
				m_afterDeserializeFlag = false;
				//m_mainGraphInstance.ParentWindow = this;
			}

			if( (IsShaderFunctionWindow && CurrentGraph.CurrentShaderFunction == null) || m_markToClose )
			{
				Close();
			}
		}

		public void SetCtrlSCallback( bool imediate )
		{
			//MasterNode node = _mainGraphInstance.CurrentMasterNode;
			if( /*node != null && node.CurrentShader != null && */m_shaderIsModified )
			{
				if( imediate )
				{
					OnToolButtonPressed( ToolButtonType.Update );
				}
				else
				{
					m_ctrlSCallback = true;
				}
			}
		}

		public void SetSaveIsDirty()
		{
			m_saveIsDirty = true && UIUtils.DirtyMask;
		}

		public void OnPaletteNodeCreate( System.Type type, string name, AmplifyShaderFunction function )
		{
			m_mainGraphInstance.DeSelectAll();
			m_paletteChosenType = type;
			m_paletteChosenFunction = function;
			m_palettePopup.Activate( name );
		}

		public void OnContextPaletteNodeCreate( System.Type type, string name, AmplifyShaderFunction function )
		{
			m_mainGraphInstance.DeSelectAll();
			ParentNode newNode = CreateNode( type, m_contextPalette.StartDropPosition * m_cameraZoom - m_cameraOffset, function );
			//Debug.Log( "created context" );
			m_mainGraphInstance.SelectNode( newNode, false, false );
			bool find = false;
			if( newNode is FunctionNode && CurrentGraph.CurrentShaderFunction != null )
				find = SearchFunctionNodeRecursively( CurrentGraph.CurrentShaderFunction );

			if( find )
			{
				DestroyNode( newNode, false );
				ShowMessage( "Shader Function loop detected, new node was removed to prevent errors." );
			}
			else
			{
				newNode.RefreshExternalReferences();
			}
		}

		void OnNodeStoppedMovingEvent( ParentNode node )
		{
			CheckZoomBoundaries( node.Vec2Position );
			//ShaderIsModified = true;
		}

		void OnRefreshFunctionNodeEvent( FunctionNode node )
		{
			Debug.Log( node );
		}

		private const string ShaderIsModifiedMessage = "Click to save changes.";
		private const string ShaderIsNotModified = "No changes to save, up-to-date.";
		void OnMaterialUpdated( MasterNode masterNode )
		{
			if( masterNode != null )
			{
				if( masterNode.CurrentMaterial )
				{
					m_toolsWindow.SetStateOnButton( ToolButtonType.Update, ShaderIsModified ? 0 : 2, ShaderIsModified ? ShaderIsModifiedMessage : ShaderIsNotModified);
				}
				else
				{
					m_toolsWindow.SetStateOnButton( ToolButtonType.Update, 1, "Set an active Material in the Master Node." );
				}
				UpdateLiveUI();
			}
			else
			{
				m_toolsWindow.SetStateOnButton( ToolButtonType.Update, 1, "Set an active Material in the Master Node." );
			}
		}

		void OnShaderUpdated( MasterNode masterNode )
		{
			m_toolsWindow.SetStateOnButton( ToolButtonType.OpenSourceCode, masterNode.CurrentShader != null ? 1 : 0 );
		}

		public void CheckZoomBoundaries( Vector2 newPosition )
		{
			if( newPosition.x < m_minNodePos.x )
			{
				m_minNodePos.x = newPosition.x;
			}
			else if( newPosition.x > m_maxNodePos.x )
			{
				m_maxNodePos.x = newPosition.x;
			}

			if( newPosition.y < m_minNodePos.y )
			{
				m_minNodePos.y = newPosition.y;
			}
			else if( newPosition.y > m_maxNodePos.y )
			{
				m_maxNodePos.y = newPosition.y;
			}
		}
		public void DestroyNode( ParentNode node, bool registerUndo = true ) { m_mainGraphInstance.DestroyNode( node, registerUndo ); }
		public ParentNode CreateNode( System.Type type, Vector2 position, AmplifyShaderFunction function = null, bool selectNode = true )
		{
			ParentNode node;
			if( function == null )
				node = m_mainGraphInstance.CreateNode( type, true );
			else
				node = m_mainGraphInstance.CreateNode( function, true );

			Vector2 newPosition = position;
			node.Vec2Position = newPosition;
			CheckZoomBoundaries( newPosition );

			// Connect node if a wire is active 
			if( m_wireReferenceUtils.ValidReferences() )
			{
				if( m_wireReferenceUtils.InputPortReference.IsValid )
				{
					ParentNode originNode = m_mainGraphInstance.GetNode( m_wireReferenceUtils.InputPortReference.NodeId );
					InputPort originPort = originNode.GetInputPortByUniqueId( m_wireReferenceUtils.InputPortReference.PortId );
					OutputPort outputPort = node.GetFirstOutputPortOfType( m_wireReferenceUtils.InputPortReference.DataType, true );
					if( outputPort != null && originPort.CheckValidType( outputPort.DataType ) && ( !m_wireReferenceUtils.InputPortReference.TypeLocked ||
												m_wireReferenceUtils.InputPortReference.DataType == WirePortDataType.OBJECT ||
												( m_wireReferenceUtils.InputPortReference.TypeLocked && outputPort.DataType == m_wireReferenceUtils.InputPortReference.DataType ) ) )
					{

						//link output to input
						if( outputPort.ConnectTo( m_wireReferenceUtils.InputPortReference.NodeId, m_wireReferenceUtils.InputPortReference.PortId, m_wireReferenceUtils.InputPortReference.DataType, m_wireReferenceUtils.InputPortReference.TypeLocked ) )
							node.OnOutputPortConnected( outputPort.PortId, m_wireReferenceUtils.InputPortReference.NodeId, m_wireReferenceUtils.InputPortReference.PortId );

						//link input to output
						if( originPort.ConnectTo( outputPort.NodeId, outputPort.PortId, outputPort.DataType, m_wireReferenceUtils.InputPortReference.TypeLocked ) )
							originNode.OnInputPortConnected( m_wireReferenceUtils.InputPortReference.PortId, node.UniqueId, outputPort.PortId );
					}
				}

				if( m_wireReferenceUtils.OutputPortReference.IsValid )
				{
					ParentNode originNode = m_mainGraphInstance.GetNode( m_wireReferenceUtils.OutputPortReference.NodeId );
					InputPort inputPort = node.GetFirstInputPortOfType( m_wireReferenceUtils.OutputPortReference.DataType, true );

					if( inputPort != null && ( !inputPort.TypeLocked ||
													inputPort.DataType == WirePortDataType.OBJECT ||
													( inputPort.TypeLocked && inputPort.DataType == m_wireReferenceUtils.OutputPortReference.DataType ) ) )
					{

						inputPort.InvalidateAllConnections();
						//link input to output
						if( inputPort.ConnectTo( m_wireReferenceUtils.OutputPortReference.NodeId, m_wireReferenceUtils.OutputPortReference.PortId, m_wireReferenceUtils.OutputPortReference.DataType, inputPort.TypeLocked ) )
							node.OnInputPortConnected( inputPort.PortId, m_wireReferenceUtils.OutputPortReference.NodeId, m_wireReferenceUtils.OutputPortReference.PortId );
						//link output to input

						if( originNode.GetOutputPortByUniqueId( m_wireReferenceUtils.OutputPortReference.PortId ).ConnectTo( inputPort.NodeId, inputPort.PortId, m_wireReferenceUtils.OutputPortReference.DataType, inputPort.TypeLocked ) )
							originNode.OnOutputPortConnected( m_wireReferenceUtils.OutputPortReference.PortId, node.UniqueId, inputPort.PortId );
					}
				}
				m_wireReferenceUtils.InvalidateReferences();

				//for ( int i = 0; i < m_mainGraphInstance.VisibleNodes.Count; i++ )
				//{
				//	m_mainGraphInstance.VisibleNodes[ i ].OnNodeInteraction( node );
				//}
			}

			if( selectNode )
				m_mainGraphInstance.SelectNode( node, false, false );
			//_repaintIsDirty = true

			SetSaveIsDirty();
			ForceRepaint();
			return node;
		}

		public void UpdateNodePreviewListAndTime()
		{
			if( UIUtils.CurrentWindow != this )
				return;

			double deltaTime = Time.realtimeSinceStartup - m_time;
			m_time = Time.realtimeSinceStartup;

			if( DebugConsoleWindow.DeveloperMode )
			{
				m_frameCounter++;
				if( m_frameCounter >= 60 )
				{
					m_fpsDisplay = ( 60 / ( Time.realtimeSinceStartup - m_fpsTime ) ).ToString( "N2" );
					m_fpsTime = Time.realtimeSinceStartup;
					m_frameCounter = 0;
				}
			}

			if( m_smoothZoom )
			{
				m_repaintIsDirty = true;
				if( Mathf.Abs( m_targetZoom - m_cameraZoom ) < 0.001f )
				{
					m_smoothZoom = false;
					m_cameraZoom = m_targetZoom;
					m_zoomTime = 0;
				}
				else
				{
					m_zoomTime += deltaTime;
					Vector2 canvasPos = m_zoomPivot * m_cameraZoom;
					m_cameraZoom = Mathf.SmoothDamp( m_cameraZoom, m_targetZoom, ref m_zoomVelocity, 0.1f, 10000, (float)deltaTime * 1.5f );
					canvasPos = canvasPos - m_zoomPivot * m_cameraZoom;
					m_cameraOffset = m_cameraOffset - canvasPos;
					m_targetOffset = m_targetOffset - canvasPos;
				}

			}

			if( m_smoothOffset )
			{
				m_repaintIsDirty = true;
				if( ( m_targetOffset - m_cameraOffset ).SqrMagnitude() < 1f )
				{
					m_smoothOffset = false;
					m_offsetTime = 0;
				}
				else
				{
					m_offsetTime += deltaTime;
					m_cameraOffset = Vector2.SmoothDamp( m_cameraOffset, m_targetOffset, ref m_camVelocity, 0.1f, 100000, (float)deltaTime * 1.5f );
				}
			}

			if( m_cachedEditorTimeId == -1 )
				m_cachedEditorTimeId = Shader.PropertyToID( "_EditorTime" );

			if( m_cachedEditorDeltaTimeId == -1 )
				m_cachedEditorDeltaTimeId = Shader.PropertyToID( "_EditorDeltaTime" );

			//Update Game View?
			//Shader.SetGlobalVector( "_Time", new Vector4( Time.realtimeSinceStartup / 20, Time.realtimeSinceStartup, Time.realtimeSinceStartup * 2, Time.realtimeSinceStartup * 3 ) );

			//System.Type T = System.Type.GetType( "UnityEditor.GameView,UnityEditor" );
			//UnityEngine.Object[] array = Resources.FindObjectsOfTypeAll( T );
			//EditorWindow gameView = ( array.Length <= 0 ) ? null : ( ( EditorWindow ) array[ 0 ] );
			//gameView.Repaint();

			if( RenderSettings.sun != null )
			{
				Vector3 lightdir = -RenderSettings.sun.transform.forward;//.rotation.eulerAngles;

				Shader.SetGlobalVector( "_EditorWorldLightPos", new Vector4( lightdir.x, lightdir.y, lightdir.z, 0 ) );
				Shader.SetGlobalColor( "_EditorLightColor", RenderSettings.sun.color.linear );
			}
			Shader.SetGlobalFloat( "_EditorTime", (float)m_time );
			Shader.SetGlobalFloat( "_EditorDeltaTime", (float)deltaTime );
			
			/////////// UPDATE PREVIEWS //////////////
			UIUtils.CheckNullMaterials();
			//CurrentGraph.AllNodes.Sort( ( x, y ) => { return x.Depth.CompareTo( y.Depth ); } );
			int nodeCount = CurrentGraph.AllNodes.Count;
			for( int i = nodeCount - 1; i >= 0; i-- )
			{
				ParentNode node = CurrentGraph.AllNodes[ i ];
				if( node != null && !VisitedChanged.ContainsKey( node.OutputId ) )
				{
					bool result = node.RecursivePreviewUpdate();
					if( result )
						m_repaintIsDirty = true;
				}
			}

			VisitedChanged.Clear();
			if( m_repaintIsDirty )
			{
				m_repaintIsDirty = false;
				Repaint();
			}
		}

		public void ForceRepaint()
		{
			m_repaintCount += 1;
			m_repaintIsDirty = true;
			//Repaint();
		}

		public void ForceUpdateFromMaterial() { m_forceUpdateFromMaterialFlag = true; }
		void UseCurrentEvent()
		{
			m_currentEvent.Use();
		}



		public void OnBeforeSerialize()
		{
			//if ( !UIUtils.SerializeFromUndo() )
			//{
			//	m_mainGraphInstance.DeSelectAll();
			//}

			if( DebugConsoleWindow.UseShaderPanelsInfo )
			{
				if( m_nodeParametersWindow != null )
					m_nodeParametersWindowMaximized = m_nodeParametersWindow.IsMaximized;

				if( m_paletteWindow != null )
					m_paletteWindowMaximized = m_paletteWindow.IsMaximized;
			}
		}

		public void OnAfterDeserialize()
		{
			m_afterDeserializeFlag = true;

			//m_customGraph = null;
			if( DebugConsoleWindow.UseShaderPanelsInfo )
			{
				if( m_nodeParametersWindow != null )
					m_nodeParametersWindow.IsMaximized = m_nodeParametersWindowMaximized;

				if( m_paletteWindow != null )
					m_paletteWindow.IsMaximized = m_paletteWindowMaximized;
			}
		}

		void OnDestroy()
		{
			m_ctrlSCallback = false;
			Destroy();
		}

		public override void OnDisable()
		{
			base.OnDisable();
			m_ctrlSCallback = false;
			//EditorApplication.update -= UpdateTime;
			EditorApplication.update -= UpdateNodePreviewListAndTime;

			EditorApplication.update -= IOUtils.UpdateIO;

			for( int i = 0; i < IOUtils.AllOpenedWindows.Count; i++ )
			{
				if( IOUtils.AllOpenedWindows[ i ] != this )
				{
					EditorApplication.update += IOUtils.UpdateIO;
					break;
				}
			}
		}

		void OnEmptyGraphDetected( ParentGraph graph )
		{
			if( m_delayedLoadObject != null )
			{
				LoadObject( m_delayedLoadObject );
				m_delayedLoadObject = null;
				Repaint();
			}
			else
			{
				if( !string.IsNullOrEmpty( Lastpath ) )
				{
					Shader shader = AssetDatabase.LoadAssetAtPath<Shader>( Lastpath );
					if( shader == null )
					{
						Material material = AssetDatabase.LoadAssetAtPath<Material>( Lastpath );
						if( material != null )
						{
							LoadDroppedObject( true, material.shader, material, null );
						}
						else
						{
							AmplifyShaderFunction function = AssetDatabase.LoadAssetAtPath<AmplifyShaderFunction>( Lastpath );
							if( function != null )
							{
								LoadDroppedObject( true, null, null, function );
							}
						}
					}
					else
					{
						LoadDroppedObject( true, shader, null, null );
					}
					Repaint();
				}
			}
		}


		public void ForceMaterialsToUpdate( ref Dictionary<string, string> availableMaterials )
		{
			m_forcingMaterialUpdateOp = true;
			m_forcingMaterialUpdateFlag = true;
			m_materialsToUpdate.Clear();
			foreach( KeyValuePair<string, string> kvp in availableMaterials )
			{
				Material material = AssetDatabase.LoadAssetAtPath<Material>( AssetDatabase.GUIDToAssetPath( kvp.Value ) );
				if( material != null )
				{
					m_materialsToUpdate.Add( material );
				}
			}
		}

		public void SetOutdatedShaderFromTemplate()
		{
			m_outdatedShaderFromTemplateLoaded = true;
		}

		public void ReplaceMasterNode( MasterNodeCategoriesData data, bool cacheMasterNodes )
		{
			// save connection list before switching
			m_savedList.Clear();
			int count = m_mainGraphInstance.CurrentMasterNode.InputPorts.Count;
			for( int i = 0; i < count; i++ )
			{
				if( m_mainGraphInstance.CurrentMasterNode.InputPorts[ i ].IsConnected )
				{
					string name = m_mainGraphInstance.CurrentMasterNode.InputPorts[ i ].Name;
					OutputPort op = m_mainGraphInstance.CurrentMasterNode.InputPorts[ i ].GetOutputConnection();
					if( !m_savedList.ContainsKey( name ) )
					{
						m_savedList.Add( name, op );
					}
				}
			}

			m_replaceMasterNodeType = data.Category;
			m_replaceMasterNode = true;
			m_replaceMasterNodeData = data.Name;
			m_replaceMasterNodeDataFromCache = cacheMasterNodes;
			if( cacheMasterNodes )
			{
				m_clipboard.AddMultiPassNodesToClipboard( m_mainGraphInstance.MultiPassMasterNodes.NodesList, true, -1 );
				for( int i = 0; i < m_mainGraphInstance.LodMultiPassMasternodes.Count; i++ )
				{
					if( m_mainGraphInstance.LodMultiPassMasternodes[ i ].Count > 0 )
						m_clipboard.AddMultiPassNodesToClipboard( m_mainGraphInstance.LodMultiPassMasternodes[ i ].NodesList, false, i );
				}
			}
		}

		void CheckNodeReplacement()
		{
			if( m_replaceMasterNode )
			{
				m_replaceMasterNode = false;
				switch( m_replaceMasterNodeType )
				{
					default:
					case AvailableShaderTypes.SurfaceShader:
					{
						SetStandardShader();
						if( IOUtils.OnShaderTypeChangedEvent != null )
						{
							IOUtils.OnShaderTypeChangedEvent( m_mainGraphInstance.CurrentShader, false, string.Empty );
						}
					}
					break;
					case AvailableShaderTypes.Template:
					{

						TemplateDataParent templateData = m_templatesManager.GetTemplate( m_replaceMasterNodeData );
						if( m_replaceMasterNodeDataFromCache )
						{
							m_mainGraphInstance.CrossCheckTemplateNodes( templateData, m_mainGraphInstance.MultiPassMasterNodes.NodesList , -1 );
							for( int i = 0; i < m_mainGraphInstance.LodMultiPassMasternodes.Count; i++ )
							{
								if( m_mainGraphInstance.LodMultiPassMasternodes[ i ].Count > 0 )
									m_mainGraphInstance.CrossCheckTemplateNodes( templateData, m_mainGraphInstance.LodMultiPassMasternodes[ i ].NodesList, i );
							}

							//Getting data from clipboard must be done after cross check all lists
							m_clipboard.GetMultiPassNodesFromClipboard( m_mainGraphInstance.MultiPassMasterNodes.NodesList,-1 );
							for( int i = 0; i < m_mainGraphInstance.LodMultiPassMasternodes.Count; i++ )
							{
								if( m_mainGraphInstance.LodMultiPassMasternodes[ i ].Count > 0 )
									m_clipboard.GetMultiPassNodesFromClipboard( m_mainGraphInstance.LodMultiPassMasternodes[i].NodesList,  i );
							}
							m_clipboard.ResetMultipassNodesData();
						}
						else
						{
							SetTemplateShader( m_replaceMasterNodeData, false );
						}

						if( IOUtils.OnShaderTypeChangedEvent != null )
						{
							IOUtils.OnShaderTypeChangedEvent( m_mainGraphInstance.CurrentShader, true, templateData.GUID );
						}
					}
					break;
				}
			}
			else if( m_outdatedShaderFromTemplateLoaded )
			{
				m_outdatedShaderFromTemplateLoaded = false;
				TemplateMultiPassMasterNode masterNode = m_mainGraphInstance.CurrentMasterNode as TemplateMultiPassMasterNode;
				if( masterNode != null )
				{
					ReplaceMasterNode( masterNode.CurrentCategoriesData, true );
				}
			}

			// restore possible connections by name
			if( m_savedList.Count > 0 )
			{
				foreach( var item in m_savedList )
				{
					string name = item.Key;
					OutputPort op = item.Value;
					InputPort ip = m_mainGraphInstance.CurrentMasterNode.InputPorts.Find( x => x.Name == name );

					if( op != null && ip != null && ip.Visible )
					{
						var iNode = UIUtils.GetNode( ip.NodeId );
						var oNode = UIUtils.GetNode( op.NodeId );
						ip.ConnectTo( oNode.UniqueId, op.PortId, op.DataType, false );
						op.ConnectTo( iNode.UniqueId, ip.PortId, ip.DataType, ip.TypeLocked );

						iNode.OnInputPortConnected( ip.PortId, oNode.UniqueId, op.PortId );
						oNode.OnOutputPortConnected( op.PortId, iNode.UniqueId, ip.PortId );
					}
				}
			}
			m_savedList.Clear();
		}

		public Vector2 TranformPosition( Vector2 pos )
		{
			return pos * m_cameraZoom - m_cameraOffset;
		}

		public void UpdateTabTitle()
		{
			if( m_isShaderFunctionWindow )
			{
				if( m_openedShaderFunction != null )
				{
					this.titleContent.text = GenerateTabTitle( m_openedShaderFunction.FunctionName );
				}
			}
			else
			{
				if( m_selectionMode == ASESelectionMode.Material )
				{
					this.titleContent.text = GenerateTabTitle( m_mainGraphInstance.CurrentMaterial.name );
				}
				else
				{
					this.titleContent.text = GenerateTabTitle( m_mainGraphInstance.CurrentShader.name );
				}
			}
		}

		public ParentGraph CustomGraph
		{
			get { return m_customGraph; }
			set { m_customGraph = value; }
		}

		public ParentGraph CurrentGraph
		{
			get
			{
				if( m_customGraph != null )
					return m_customGraph;

				return m_mainGraphInstance;
			}
		}

		public void RefreshAvaibleNodes()
		{
			if( m_contextMenu != null && m_mainGraphInstance != null )
			{
				m_contextMenu.RefreshNodes( m_mainGraphInstance );
				m_paletteWindow.ForceUpdate = true;
				m_contextPalette.ForceUpdate = true;
				m_refreshAvailableNodes = false;
			}
		}

		public void LateRefreshAvailableNodes()
		{
			m_refreshAvailableNodes = true;
		}

		public ParentGraph OutsideGraph { get { return m_mainGraphInstance; } }

		public bool ShaderIsModified
		{
			get { return m_shaderIsModified; }
			set
			{
				m_shaderIsModified = value && UIUtils.DirtyMask;

				m_toolsWindow.SetStateOnButton( ToolButtonType.Save, m_shaderIsModified ? 1 : 0 );
				if( !IsShaderFunctionWindow )
				{
					MasterNode masterNode = m_mainGraphInstance.CurrentMasterNode;
					if( masterNode != null && masterNode.CurrentShader != null )
					{
						m_toolsWindow.SetStateOnButton( ToolButtonType.Update, ShaderIsModified ? 0 : 2, ShaderIsModified ? ShaderIsModifiedMessage : ShaderIsNotModified );
						UpdateTabTitle( masterNode.ShaderName, m_shaderIsModified );
					}
					else
					{
						m_toolsWindow.SetStateOnButton( ToolButtonType.Update, 1 );
					}

					//if( m_mainGraphInstance.CurrentStandardSurface != null )
					//	UpdateTabTitle( m_mainGraphInstance.CurrentStandardSurface.ShaderName, m_shaderIsModified );
				}
				else
				{
					m_toolsWindow.SetStateOnButton( ToolButtonType.Update, m_shaderIsModified ? 0 : 2 );
					if( m_mainGraphInstance.CurrentShaderFunction != null )
						UpdateTabTitle( m_mainGraphInstance.CurrentShaderFunction.FunctionName, m_shaderIsModified );
				}

			}
		}
		public void MarkToRepaint() { m_repaintIsDirty = true; }
		public void RequestSave() { m_markedToSave = true; }
		public void RequestRepaint() { m_repaintIsDirty = true; }
		public OptionsWindow Options { get { return m_optionsWindow; } }
		public GraphContextMenu ContextMenuInstance { get { return m_contextMenu; } set { m_contextMenu = value; } }
		public ShortcutsManager ShortcutManagerInstance { get { return m_shortcutManager; } }

		public bool GlobalPreview
		{
			get { return m_globalPreview; }
			set { m_globalPreview = value; }
		}

		public bool GlobalShowInternalData
		{
			get { return m_globalShowInternalData; }
			set { m_globalShowInternalData = value; }
		}

		public double EditorTime
		{
			get { return m_time; }
			set { m_time = value; }
		}

		public ASESelectionMode CurrentSelection
		{
			get { return m_selectionMode; }
			set
			{
				m_selectionMode = value;
				switch( m_selectionMode )
				{
					default:
					case ASESelectionMode.Shader:
					{
						m_toolsWindow.BorderStyle = UIUtils.GetCustomStyle( CustomStyle.ShaderBorder );
					}
					break;
					case ASESelectionMode.Material:
					{
						m_toolsWindow.BorderStyle = UIUtils.GetCustomStyle( CustomStyle.MaterialBorder );
					}
					break;
					case ASESelectionMode.ShaderFunction:
					{
						m_toolsWindow.BorderStyle = UIUtils.GetCustomStyle( CustomStyle.ShaderFunctionBorder );
					}
					break;
				}
			}
		}

		public bool LiveShaderEditing
		{
			get { return m_liveShaderEditing; }
			set
			{
				m_liveShaderEditing = value;
				m_innerEditorVariables.LiveMode = m_liveShaderEditing;
				UpdateLiveUI();
			}
		}

		public NodeAvailability CurrentNodeAvailability
		{
			get { return m_currentNodeAvailability; }
			set
			{
				NodeAvailability cache = m_currentNodeAvailability;
				m_currentNodeAvailability = value;

				if( cache != value )
					RefreshAvaibleNodes();
			}
		}
		public string GUID
		{
			get
			{
				if( m_isShaderFunctionWindow )
				{
					return m_openedShaderFunction != null ? AssetDatabase.AssetPathToGUID( AssetDatabase.GetAssetPath( m_openedShaderFunction ) ) : string.Empty;
				}
				else
				{
					return m_mainGraphInstance.CurrentShader != null ? AssetDatabase.AssetPathToGUID( AssetDatabase.GetAssetPath( m_mainGraphInstance.CurrentShader ) ) : string.Empty;
				}
			}
		}
		public List<Toast> Messages { get { return m_messages; } set { m_messages = value; } }
		public float MaxMsgWidth { get { return m_maxMsgWidth; } set { m_maxMsgWidth = value; } }
		public bool MaximizeMessages { get { return m_maximizeMessages; } set { m_maximizeMessages = value; } }
		public void InvalidateAlt() { m_altAvailable = false; }
		public PaletteWindow CurrentPaletteWindow { get { return m_paletteWindow; } }
		public PreMadeShaders PreMadeShadersInstance { get { return m_preMadeShaders; } }
		public Rect CameraInfo { get { return m_cameraInfo; } }
		public Vector2 TranformedMousePos { get { return m_currentMousePos2D * m_cameraZoom - m_cameraOffset; } }
		public Vector2 TranformedKeyEvtMousePos { get { return m_keyEvtMousePos2D * m_cameraZoom - m_cameraOffset; } }
		public PalettePopUp PalettePopUpInstance { get { return m_palettePopup; } }
		public DuplicatePreventionBuffer DuplicatePrevBufferInstance { get { return m_duplicatePreventionBuffer; } }
		public NodeParametersWindow ParametersWindow { get { return m_nodeParametersWindow; } }
		public NodeExporterUtils CurrentNodeExporterUtils { get { return m_nodeExporterUtils; } }
		public AmplifyShaderFunction OpenedShaderFunction { get { return m_openedShaderFunction; } }
		public DrawInfo CameraDrawInfo { get { return m_drawInfo; } }
		public string Lastpath { get { return m_lastpath; } set { m_lastpath = value; } }
		public string LastOpenedLocation { get { return m_lastOpenedLocation; } set { m_lastOpenedLocation = value; } }
		public float AvailableCanvasWidth { get { return ( m_cameraInfo.width - m_paletteWindow.RealWidth - m_nodeParametersWindow.RealWidth ); } }
		public float AvailableCanvasHeight { get { return ( m_cameraInfo.height ); } }
		public float CameraZoom { get { return m_cameraZoom; } set { m_cameraZoom = value; m_zoomChanged = true; } }
		public int GraphCount { get { return m_graphCount; } set { m_graphCount = value; } }
		public bool ForceAutoPanDir { get { return m_forceAutoPanDir; } set { m_forceAutoPanDir = value; } }
		public bool OpenedAssetFromNode { get { return m_openedAssetFromNode; } set { m_openedAssetFromNode = value; } }
		public bool IsShaderFunctionWindow { get { return m_isShaderFunctionWindow; } set { m_isShaderFunctionWindow = value; } }
		public bool NodesLoadedCorrectly { get { return m_nodesLoadedCorrectly; } set { m_nodesLoadedCorrectly = value; } }
		public double CurrentInactiveTime { get { return m_currentInactiveTime; } }
		public string ReplaceMasterNodeData { get { return m_replaceMasterNodeData; } }
		public AvailableShaderTypes ReplaceMasterNodeType { get { return m_replaceMasterNodeType; } }
		public NodeWireReferencesUtils WireReferenceUtils { get { return m_wireReferenceUtils; } }
		public ContextPalette WindowContextPallete { get { return m_contextPalette; } }
		// This needs to go to UIUtils
		public Texture2D WireTexture { get { return m_wireTexture; } }
		public Event CurrentEvent { get { return m_currentEvent; } }
		public string CurrentCommandName { get { return m_currentCommandName; } }
		public InnerWindowEditorVariables InnerWindowVariables { get { return m_innerEditorVariables; } }
		public TemplatesManager TemplatesManagerInstance { get { return m_templatesManager; } }
		public Material CurrentMaterial { get { return CurrentGraph.CurrentMaterial; } }
		public Clipboard ClipboardInstance { get { return m_clipboard; } }
	}
}
