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

using UnityEngine;
using UnityEditor;
using System;
using System.Linq;
using System.Collections.Generic;

namespace AmplifyShaderEditor
{
	[Serializable]
	public class ParentGraph : ScriptableObject, ISerializationCallbackReceiver
	{
		private const int MasterNodeLODIncrement = 100;
		private const int MaxLodAmount = 9;

		public enum NodeLOD
		{
			LOD0,
			LOD1,
			LOD2,
			LOD3,
			LOD4,
			LOD5
		}

		[SerializeField]
		private bool m_samplingThroughMacros = false;

		private NodeLOD m_lodLevel = NodeLOD.LOD0;
		private GUIStyle nodeStyleOff;
		private GUIStyle nodeStyleOn;
		private GUIStyle nodeTitle;
		private GUIStyle commentaryBackground;

		public delegate void LODMasterNodesAdded( int lod );
		public event LODMasterNodesAdded OnLODMasterNodesAddedEvent;

		public delegate void EmptyGraphDetected( ParentGraph graph );
		public event EmptyGraphDetected OnEmptyGraphDetectedEvt;

		public delegate void NodeEvent( ParentNode node );
		public event NodeEvent OnNodeEvent = null;
		public event NodeEvent OnNodeRemovedEvent;

		public delegate void DuplicateEvent();
		public event DuplicateEvent OnDuplicateEvent;

		public event MasterNode.OnMaterialUpdated OnMaterialUpdatedEvent;
		public event MasterNode.OnMaterialUpdated OnShaderUpdatedEvent;

		private bool m_afterDeserializeFlag = true;
		private bool m_lateOptionsRefresh = false;
		private bool m_foundDuplicates = false;

		//[SerializeField]
		private AmplifyShaderEditorWindow m_parentWindow = null;

		[SerializeField]
		private int m_validNodeId;

		[SerializeField]
		private List<ParentNode> m_nodes = new List<ParentNode>();

		// Sampler Nodes registry
		[SerializeField]
		private UsageListSamplerNodes m_samplerNodes = new UsageListSamplerNodes();

		[SerializeField]
		private UsageListFloatIntNodes m_floatNodes = new UsageListFloatIntNodes();

		[SerializeField]
		private UsageListTexturePropertyNodes m_texturePropertyNodes = new UsageListTexturePropertyNodes();

		[SerializeField]
		private UsageListTextureArrayNodes m_textureArrayNodes = new UsageListTextureArrayNodes();

		[SerializeField]
		private UsageListPropertyNodes m_propertyNodes = new UsageListPropertyNodes();

		[SerializeField]
		private UsageListPropertyNodes m_rawPropertyNodes = new UsageListPropertyNodes();

		[SerializeField]
		private UsageListScreenColorNodes m_screenColorNodes = new UsageListScreenColorNodes();

		[SerializeField]
		private UsageListRegisterLocalVarNodes m_localVarNodes = new UsageListRegisterLocalVarNodes();

		[SerializeField]
		private UsageListGlobalArrayNodes m_globalArrayNodes = new UsageListGlobalArrayNodes();

		[SerializeField]
		private UsageListFunctionInputNodes m_functionInputNodes = new UsageListFunctionInputNodes();

		[SerializeField]
		private UsageListFunctionNodes m_functionNodes = new UsageListFunctionNodes();

		[SerializeField]
		private UsageListFunctionOutputNodes m_functionOutputNodes = new UsageListFunctionOutputNodes();

		[SerializeField]
		private UsageListFunctionSwitchNodes m_functionSwitchNodes = new UsageListFunctionSwitchNodes();

		[SerializeField]
		private UsageListFunctionSwitchCopyNodes m_functionSwitchCopyNodes = new UsageListFunctionSwitchCopyNodes();

		[SerializeField]
		private UsageListTemplateMultiPassMasterNodes m_multiPassMasterNodes = new UsageListTemplateMultiPassMasterNodes();

		[SerializeField]
		private List<UsageListTemplateMultiPassMasterNodes> m_lodMultiPassMasterNodes;

		[SerializeField]
		private UsageListCustomExpressionsOnFunctionMode m_customExpressionsOnFunctionMode = new UsageListCustomExpressionsOnFunctionMode();

		[SerializeField]
		private UsageListStaticSwitchNodes m_staticSwitchNodes = new UsageListStaticSwitchNodes();

		[SerializeField]
		private int m_masterNodeId = Constants.INVALID_NODE_ID;

		[SerializeField]
		private bool m_isDirty;

		[SerializeField]
		private bool m_saveIsDirty = false;

		[SerializeField]
		private int m_nodeClicked;

		[SerializeField]
		private int m_loadedShaderVersion;

		[SerializeField]
		private int m_instancePropertyCount = 0;

		[SerializeField]
		private int m_virtualTextureCount = 0;

		[SerializeField]
		private int m_graphId = 0;

		[SerializeField]
		private PrecisionType m_currentPrecision = PrecisionType.Float;

		[SerializeField]
		private NodeAvailability m_currentCanvasMode = NodeAvailability.SurfaceShader;

		[SerializeField]
		private TemplateSRPType m_currentSRPType = TemplateSRPType.BiRP;

		//private List<ParentNode> m_visibleNodes = new List<ParentNode>();

		private List<ParentNode> m_nodePreviewList = new List<ParentNode>();

		private Dictionary<int, ParentNode> m_nodesDict = new Dictionary<int, ParentNode>();

		[NonSerialized]
		private List<ParentNode> m_selectedNodes = new List<ParentNode>();

		[NonSerialized]
		private List<ParentNode> m_markedForDeletion = new List<ParentNode>();

		[SerializeField]
		private List<WireReference> m_highlightedWires = new List<WireReference>();
		private System.Type m_masterNodeDefaultType;

		[SerializeField]
		private List<PropertyNode> m_internalTemplateNodesList = new List<PropertyNode>();
		private Dictionary<int, PropertyNode> m_internalTemplateNodesDict = new Dictionary<int, PropertyNode>();

		private NodeGrid m_nodeGrid;

		private bool m_markedToDeSelect = false;
		private int m_markToSelect = -1;
		private bool m_markToReOrder = false;

		private bool m_hasUnConnectedNodes = false;

		private bool m_checkSelectedWireHighlights = false;

		// Bezier info
		[SerializeField]
		private List<WireBezierReference> m_bezierReferences;
		private const int MaxBezierReferences = 50;
		private int m_wireBezierCount = 0;

		protected int m_normalDependentCount = 0;
		private bool m_forceCategoryRefresh = false;

		[SerializeField]
		private bool m_forceRepositionCheck = false;

		private bool m_isLoading = false;
		private bool m_isDuplicating = false;

		private bool m_changedLightingModel = false;

		public void ResetEvents()
		{
			OnNodeEvent = null;
			OnMaterialUpdatedEvent = null;
			OnShaderUpdatedEvent = null;
			OnEmptyGraphDetectedEvt = null;
			OnNodeRemovedEvent = null;
		}

		public void Init()
		{
			UndoUtils.RegisterUndoRedoCallback( OnUndoRedoCallback );
			m_normalDependentCount = 0;
			m_nodes = new List<ParentNode>();
			m_samplerNodes = new UsageListSamplerNodes();
			m_samplerNodes.ContainerGraph = this;
			m_samplerNodes.ReorderOnChange = true;
			m_floatNodes = new UsageListFloatIntNodes();
			m_floatNodes.ContainerGraph = this;
			m_texturePropertyNodes = new UsageListTexturePropertyNodes();
			m_texturePropertyNodes.ContainerGraph = this;
			m_textureArrayNodes = new UsageListTextureArrayNodes();
			m_textureArrayNodes.ContainerGraph = this;
			m_textureArrayNodes.ReorderOnChange = true;
			m_propertyNodes = new UsageListPropertyNodes();
			m_propertyNodes.ContainerGraph = this;
			m_rawPropertyNodes = new UsageListPropertyNodes();
			m_rawPropertyNodes.ContainerGraph = this;
			m_customExpressionsOnFunctionMode = new UsageListCustomExpressionsOnFunctionMode();
			m_customExpressionsOnFunctionMode.ContainerGraph = this;
			m_staticSwitchNodes = new UsageListStaticSwitchNodes();
			m_staticSwitchNodes.ContainerGraph = this;
			m_staticSwitchNodes.ReorderOnChange = true;
			m_screenColorNodes = new UsageListScreenColorNodes();
			m_screenColorNodes.ContainerGraph = this;
			m_screenColorNodes.ReorderOnChange = true;
			m_localVarNodes = new UsageListRegisterLocalVarNodes();
			m_localVarNodes.ContainerGraph = this;
			m_localVarNodes.ReorderOnChange = true;
			m_globalArrayNodes = new UsageListGlobalArrayNodes();
			m_globalArrayNodes.ContainerGraph = this;
			m_functionInputNodes = new UsageListFunctionInputNodes();
			m_functionInputNodes.ContainerGraph = this;
			m_functionNodes = new UsageListFunctionNodes();
			m_functionNodes.ContainerGraph = this;
			m_functionOutputNodes = new UsageListFunctionOutputNodes();
			m_functionOutputNodes.ContainerGraph = this;
			m_functionSwitchNodes = new UsageListFunctionSwitchNodes();
			m_functionSwitchNodes.ContainerGraph = this;
			m_functionSwitchCopyNodes = new UsageListFunctionSwitchCopyNodes();
			m_functionSwitchCopyNodes.ContainerGraph = this;
			m_multiPassMasterNodes = new UsageListTemplateMultiPassMasterNodes();
			m_multiPassMasterNodes.ContainerGraph = this;
			m_lodMultiPassMasterNodes = new List<UsageListTemplateMultiPassMasterNodes>( MaxLodAmount );
			for( int i = 0; i < MaxLodAmount; i++ )
			{
				m_lodMultiPassMasterNodes.Add( new UsageListTemplateMultiPassMasterNodes() );
			}
			m_selectedNodes = new List<ParentNode>();
			m_markedForDeletion = new List<ParentNode>();
			m_highlightedWires = new List<WireReference>();
			m_validNodeId = 0;
			IsDirty = false;
			SaveIsDirty = false;
			m_masterNodeDefaultType = typeof( StandardSurfaceOutputNode );

			m_bezierReferences = new List<WireBezierReference>( MaxBezierReferences );
			for( int i = 0; i < MaxBezierReferences; i++ )
			{
				m_bezierReferences.Add( new WireBezierReference() );
			}
		}

		public void ActivatePreviews( bool value )
		{
			int count = m_nodes.Count;
			if( value )
			{
				for( int i = 0 ; i < count ; i++ )
				{
					m_nodes[ i ].PreviewIsDirty = true;
				}
			}
			else
			{
				//for( int i = 0 ; i < count ; i++ )
				//{
				//	m_nodes[ i ].DisablePreview();
				//}
			}
		}

		private void OnUndoRedoCallback()
		{
			DeSelectAll();
		}

		private void OnEnable()
		{
			hideFlags = HideFlags.HideAndDontSave;
			m_nodeGrid = new NodeGrid();
			m_internalTemplateNodesDict = new Dictionary<int, PropertyNode>();
			m_nodesDict = new Dictionary<int, ParentNode>();
			nodeStyleOff = UIUtils.GetCustomStyle( CustomStyle.NodeWindowOff );
			nodeStyleOn = UIUtils.GetCustomStyle( CustomStyle.NodeWindowOn );
			nodeTitle = UIUtils.GetCustomStyle( CustomStyle.NodeHeader );
			commentaryBackground = UIUtils.GetCustomStyle( CustomStyle.CommentaryBackground );
		}

		public void UpdateRegisters()
		{
			m_samplerNodes.UpdateNodeArr();
			m_propertyNodes.UpdateNodeArr();
			m_rawPropertyNodes.UpdateNodeArr();
			m_customExpressionsOnFunctionMode.UpdateNodeArr();
			m_staticSwitchNodes.UpdateNodeArr();
			m_functionInputNodes.UpdateNodeArr();
			m_functionNodes.UpdateNodeArr();
			m_functionOutputNodes.UpdateNodeArr();
			m_functionSwitchNodes.UpdateNodeArr();
			m_functionSwitchCopyNodes.UpdateNodeArr();
			m_multiPassMasterNodes.UpdateNodeArr();
			for( int i = 0; i < m_lodMultiPassMasterNodes.Count; i++ )
			{
				m_lodMultiPassMasterNodes[ i ].UpdateNodeArr();
			}
			m_texturePropertyNodes.UpdateNodeArr();
			m_textureArrayNodes.UpdateNodeArr();
			m_screenColorNodes.UpdateNodeArr();
			m_localVarNodes.UpdateNodeArr();
			m_globalArrayNodes.UpdateNodeArr();
		}

		public int GetValidId()
		{
			return m_validNodeId++;
		}

		void UpdateIdFromNode( ParentNode node )
		{
			if( node.UniqueId >= m_validNodeId )
			{
				m_validNodeId = node.UniqueId + 1;
			}
		}

		public void ResetNodeConnStatus()
		{
			for( int i = 0; i < m_nodes.Count; i++ )
			{
				if( m_nodes[ i ].ConnStatus == NodeConnectionStatus.Connected )
				{
					m_nodes[ i ].ConnStatus = NodeConnectionStatus.Not_Connected;
				}
			}
		}

		public void CleanUnusedNodes()
		{
			List<ParentNode> unusedNodes = new List<ParentNode>();
			for( int i = 0; i < m_nodes.Count; i++ )
			{
				if( m_nodes[ i ].ConnStatus == NodeConnectionStatus.Not_Connected )
				{
					unusedNodes.Add( m_nodes[ i ] );
				}
			}

			for( int i = 0; i < unusedNodes.Count; i++ )
			{
				DestroyNode( unusedNodes[ i ] );
			}
			unusedNodes.Clear();
			unusedNodes = null;

			IsDirty = true;
		}

		// Destroy all nodes excluding Master Node
		public void ClearGraph()
		{
			List<ParentNode> list = new List<ParentNode>();
			int count = m_nodes.Count;
			for( int i = 0; i < count; i++ )
			{
				if( m_nodes[ i ].UniqueId != m_masterNodeId )
				{
					list.Add( m_nodes[ i ] );
				}
			}

			while( list.Count > 0 )
			{
				DestroyNode( list[ 0 ] );
				list.RemoveAt( 0 );
			}
		}

		public void CleanNodes()
		{
			for( int i = 0; i < m_nodes.Count; i++ )
			{
				if( m_nodes[ i ] != null )
				{					
					UndoUtils.ClearUndo( m_nodes[ i ] );
					m_nodes[ i ].Destroy();
					GameObject.DestroyImmediate( m_nodes[ i ] );
				}
			}
			ClearInternalTemplateNodes();

			m_masterNodeId = Constants.INVALID_NODE_ID;
			m_validNodeId = 0;
			m_instancePropertyCount = 0;
			m_virtualTextureCount = 0;

			m_nodesDict.Clear();
			m_nodes.Clear();
			m_samplerNodes.Clear();
			m_propertyNodes.Clear();
			m_rawPropertyNodes.Clear();
			m_customExpressionsOnFunctionMode.Clear();
			m_staticSwitchNodes.Clear();
			m_functionInputNodes.Clear();
			m_functionNodes.Clear();
			m_functionOutputNodes.Clear();
			m_functionSwitchNodes.Clear();
			m_functionSwitchCopyNodes.Clear();
			m_multiPassMasterNodes.Clear();
			for( int i = 0; i < m_lodMultiPassMasterNodes.Count; i++ )
			{
				m_lodMultiPassMasterNodes[ i ].Clear();
			}
			
			m_texturePropertyNodes.Clear();
			m_textureArrayNodes.Clear();
			m_screenColorNodes.Clear();
			m_localVarNodes.Clear();
			m_globalArrayNodes.Clear();
			m_selectedNodes.Clear();
			m_markedForDeletion.Clear();
		}

		public void ResetHighlightedWires()
		{
			for( int i = 0; i < m_highlightedWires.Count; i++ )
			{
				m_highlightedWires[ i ].WireStatus = WireStatus.Default;
			}
			m_highlightedWires.Clear();
		}

		public void HighlightWiresStartingNode( ParentNode node )
		{
			for( int outputIdx = 0; outputIdx < node.OutputPorts.Count; outputIdx++ )
			{
				for( int extIdx = 0; extIdx < node.OutputPorts[ outputIdx ].ExternalReferences.Count; extIdx++ )
				{
					WireReference wireRef = node.OutputPorts[ outputIdx ].ExternalReferences[ extIdx ];
					ParentNode nextNode = GetNode( wireRef.NodeId );
					if( nextNode && nextNode.ConnStatus == NodeConnectionStatus.Connected )
					{
						InputPort port = nextNode.GetInputPortByUniqueId( wireRef.PortId );
						if( port.ExternalReferences.Count == 0 || port.ExternalReferences[ 0 ].WireStatus == WireStatus.Highlighted )
						{
							// if even one wire is already highlighted then this tells us that this node was already been analysed
							return;
						}

						port.ExternalReferences[ 0 ].WireStatus = WireStatus.Highlighted;
						m_highlightedWires.Add( port.ExternalReferences[ 0 ] );
						HighlightWiresStartingNode( nextNode );
					}
				}
			}

			RegisterLocalVarNode regNode = node as RegisterLocalVarNode;
			if( (object)regNode != null )
			{
				int count = regNode.NodeReferences.Count;
				for( int i = 0; i < count; i++ )
				{
					HighlightWiresStartingNode( regNode.NodeReferences[ i ] );
				}
			}
		}

		void PropagateHighlightDeselection( ParentNode node, int portId = -1 )
		{
			if( portId > -1 )
			{
				InputPort port = node.GetInputPortByUniqueId( portId );
				port.ExternalReferences[ 0 ].WireStatus = WireStatus.Default;
			}

			if( node.Selected )
				return;

			for( int i = 0; i < node.InputPorts.Count; i++ )
			{
				if( node.InputPorts[ i ].ExternalReferences.Count > 0 && node.InputPorts[ i ].ExternalReferences[ 0 ].WireStatus == WireStatus.Highlighted )
				{
					// even though node is deselected, it receives wire highlight from a previous one 
					return;
				}
			}

			for( int outputIdx = 0; outputIdx < node.OutputPorts.Count; outputIdx++ )
			{
				for( int extIdx = 0; extIdx < node.OutputPorts[ outputIdx ].ExternalReferences.Count; extIdx++ )
				{
					WireReference wireRef = node.OutputPorts[ outputIdx ].ExternalReferences[ extIdx ];
					ParentNode nextNode = GetNode( wireRef.NodeId );
					PropagateHighlightDeselection( nextNode, wireRef.PortId );
				}
			}

			RegisterLocalVarNode regNode = node as RegisterLocalVarNode;
			if( (object)regNode != null )
			{
				int count = regNode.NodeReferences.Count;
				for( int i = 0; i < count; i++ )
				{
					PropagateHighlightDeselection( regNode.NodeReferences[ i ], -1 );
				}
			}
		}


		public void ResetNodesData()
		{
			int count = m_nodes.Count;
			for( int i = 0; i < count; i++ )
			{
				m_nodes[ i ].ResetNodeData();
			}
		}

		public void FullCleanUndoStack()
		{
			UndoUtils.ClearUndo( this );
			int count = m_nodes.Count;
			for( int i = 0; i < count; i++ )
			{
				if( m_nodes[ i ] != null )
				{
					UndoUtils.ClearUndo( m_nodes[ i ] );
				}
			}
		}

		public void FullRegisterOnUndoStack()
		{
			UndoUtils.RegisterCompleteObjectUndo( this, Constants.UndoRegisterFullGrapId );
			int count = m_nodes.Count;
			for( int i = 0; i < count; i++ )
			{
				if( m_nodes[ i ] != null )
				{
					UndoUtils.RegisterCompleteObjectUndo( m_nodes[ i ], Constants.UndoRegisterFullGrapId );
				}
			}
		}

		public void CheckPropertiesAutoRegister( ref MasterNodeDataCollector dataCollector )
		{
			List<PropertyNode> propertyNodesList = m_rawPropertyNodes.NodesList;
			int propertyCount = propertyNodesList.Count;
			for( int i = 0; i < propertyCount; i++ )
			{
				propertyNodesList[ i ].CheckIfAutoRegister( ref dataCollector );
			}
			propertyNodesList = null;

			List<GlobalArrayNode> globalArrayNodeList = m_globalArrayNodes.NodesList;
			int globalArrayCount = globalArrayNodeList.Count;
			for( int i = 0; i < globalArrayCount; i++ )
			{
				globalArrayNodeList[ i ].CheckIfAutoRegister( ref dataCollector );
			}
			globalArrayNodeList = null;

			//List<PropertyNode> propertyNodesList = m_propertyNodes.NodesList;
			//int propertyCount = propertyNodesList.Count;
			//for( int i = 0; i < propertyCount; i++ )
			//{
			//	propertyNodesList[ i ].CheckIfAutoRegister( ref dataCollector );
			//}
			//propertyNodesList = null;

			//List<ScreenColorNode> screenColorNodes = m_screenColorNodes.NodesList;
			//int screenColorNodesCount = screenColorNodes.Count;
			//for( int i = 0; i < screenColorNodesCount; i++ )
			//{
			//	screenColorNodes[ i ].CheckIfAutoRegister( ref dataCollector );
			//}
			//screenColorNodes = null;
		}

		public void SoftDestroy()
		{
			OnNodeRemovedEvent = null;

			m_masterNodeId = Constants.INVALID_NODE_ID;
			m_validNodeId = 0;
			
			m_nodeGrid.Destroy();
			//m_nodeGrid = null;

			ClearInternalTemplateNodes();

			for( int i = 0; i < m_nodes.Count; i++ )
			{
				if( m_nodes[ i ] != null )
				{
					m_nodes[ i ].Destroy();
					GameObject.DestroyImmediate( m_nodes[ i ] );
				}
			}

			m_instancePropertyCount = 0;

			m_nodes.Clear();
			//m_nodes = null;

			m_nodesDict.Clear();
			//m_nodesDict = null;

			m_samplerNodes.Clear();
			//m_samplerNodes = null;

			m_propertyNodes.Clear();
			m_rawPropertyNodes.Clear();
			//m_propertyNodes = null;

			m_customExpressionsOnFunctionMode.Clear();

			m_staticSwitchNodes.Clear();

			m_functionInputNodes.Clear();
			//m_functionInputNodes = null;

			m_functionNodes.Clear();
			//m_functionNodes = null;

			m_functionOutputNodes.Clear();
			//m_functionOutputNodes = null;

			m_functionSwitchNodes.Clear();
			//m_functionSwitchNodes = null;

			m_functionSwitchCopyNodes.Clear();
			//m_functionSwitchCopyNodes = null;

			m_texturePropertyNodes.Clear();
			//m_texturePropertyNodes = null;

			m_textureArrayNodes.Clear();
			//m_textureArrayNodes = null;

			m_screenColorNodes.Clear();
			//m_screenColorNodes = null;

			m_localVarNodes.Clear();
			//m_localVarNodes = null;

			m_globalArrayNodes.Clear();

			m_selectedNodes.Clear();
			//m_selectedNodes = null;

			m_markedForDeletion.Clear();
			//m_markedForDeletion = null;

			m_nodePreviewList.Clear();
			//m_nodePreviewList = null;

			IsDirty = true;

			OnNodeEvent = null;
			OnDuplicateEvent = null;
			//m_currentShaderFunction = null;

			OnMaterialUpdatedEvent = null;
			OnShaderUpdatedEvent = null;
			OnEmptyGraphDetectedEvt = null;

			nodeStyleOff = null;
			nodeStyleOn = null;
			nodeTitle = null;
			commentaryBackground = null;
			OnLODMasterNodesAddedEvent = null;
		}




		public void Destroy()
		{
			UndoUtils.UnregisterUndoRedoCallback( OnUndoRedoCallback );
			for( int i = 0; i < m_nodes.Count; i++ )
			{
				if( m_nodes[ i ] != null )
				{
					UndoUtils.ClearUndo( m_nodes[ i ] );
					m_nodes[ i ].Destroy();
					GameObject.DestroyImmediate( m_nodes[ i ] );
				}
			}

			//Must be before m_propertyNodes.Destroy();
			ClearInternalTemplateNodes();
			m_internalTemplateNodesDict = null;
			m_internalTemplateNodesList = null;

			OnNodeRemovedEvent = null;

			m_masterNodeId = Constants.INVALID_NODE_ID;
			m_validNodeId = 0;
			m_instancePropertyCount = 0;

			m_nodeGrid.Destroy();
			m_nodeGrid = null;

			m_nodes.Clear();
			m_nodes = null;

			m_samplerNodes.Destroy();
			m_samplerNodes = null;

			m_propertyNodes.Destroy();
			m_propertyNodes = null;

			m_rawPropertyNodes.Destroy();
			m_rawPropertyNodes = null;

			m_customExpressionsOnFunctionMode.Destroy();
			m_customExpressionsOnFunctionMode = null;

			m_staticSwitchNodes.Destroy();
			m_staticSwitchNodes = null;

			m_functionInputNodes.Destroy();
			m_functionInputNodes = null;

			m_functionNodes.Destroy();
			m_functionNodes = null;

			m_functionOutputNodes.Destroy();
			m_functionOutputNodes = null;

			m_functionSwitchNodes.Destroy();
			m_functionSwitchNodes = null;

			m_functionSwitchCopyNodes.Destroy();
			m_functionSwitchCopyNodes = null;

			m_multiPassMasterNodes.Destroy();
			m_multiPassMasterNodes = null;

			for( int i = 0; i < m_lodMultiPassMasterNodes.Count; i++ )
			{
				m_lodMultiPassMasterNodes[ i ].Destroy();
				m_lodMultiPassMasterNodes[ i ] = null;
			}
			m_lodMultiPassMasterNodes.Clear();
			m_lodMultiPassMasterNodes = null;

			m_texturePropertyNodes.Destroy();
			m_texturePropertyNodes = null;

			m_textureArrayNodes.Destroy();
			m_textureArrayNodes = null;

			m_screenColorNodes.Destroy();
			m_screenColorNodes = null;

			m_localVarNodes.Destroy();
			m_localVarNodes = null;

			m_globalArrayNodes.Destroy();
			m_globalArrayNodes = null;

			m_selectedNodes.Clear();
			m_selectedNodes = null;

			m_markedForDeletion.Clear();
			m_markedForDeletion = null;


			m_nodesDict.Clear();
			m_nodesDict = null;

			m_nodePreviewList.Clear();
			m_nodePreviewList = null;

			IsDirty = true;

			OnNodeEvent = null;
			OnDuplicateEvent = null;
			//m_currentShaderFunction = null;

			OnMaterialUpdatedEvent = null;
			OnShaderUpdatedEvent = null;
			OnEmptyGraphDetectedEvt = null;

			nodeStyleOff = null;
			nodeStyleOn = null;
			nodeTitle = null;
			commentaryBackground = null;

			OnLODMasterNodesAddedEvent = null;
		}

		void OnNodeChangeSizeEvent( ParentNode node )
		{
			m_nodeGrid.RemoveNodeFromGrid( node, true );
			m_nodeGrid.AddNodeToGrid( node );
		}

		public void OnNodeFinishMoving( ParentNode node, bool testOnlySelected, InteractionMode interactionMode )
		{
			if( OnNodeEvent != null )
			{
				OnNodeEvent( node );
				SaveIsDirty = true;
			}

			m_nodeGrid.RemoveNodeFromGrid( node, true );
			m_nodeGrid.AddNodeToGrid( node );

			//if( testOnlySelected )
			//{
			//	for( int i = m_visibleNodes.Count - 1; i > -1; i-- )
			//	{
			//		if( node.UniqueId != m_visibleNodes[ i ].UniqueId )
			//		{
			//			switch( interactionMode )
			//			{
			//				case InteractionMode.Target:
			//				{
			//					node.OnNodeInteraction( m_visibleNodes[ i ] );
			//				}
			//				break;
			//				case InteractionMode.Other:
			//				{
			//					m_visibleNodes[ i ].OnNodeInteraction( node );
			//				}
			//				break;
			//				case InteractionMode.Both:
			//				{
			//					node.OnNodeInteraction( m_visibleNodes[ i ] );
			//					m_visibleNodes[ i ].OnNodeInteraction( node );
			//				}
			//				break;
			//			}
			//		}
			//	}
			//}
			//else
			{
				for( int i = m_nodes.Count - 1; i > -1; i-- )
				{
					if( node.UniqueId != m_nodes[ i ].UniqueId )
					{
						switch( interactionMode )
						{
							case InteractionMode.Target:
							{
								node.OnNodeInteraction( m_nodes[ i ] );
							}
							break;
							case InteractionMode.Other:
							{
								m_nodes[ i ].OnNodeInteraction( node );
							}
							break;
							case InteractionMode.Both:
							{
								node.OnNodeInteraction( m_nodes[ i ] );
								m_nodes[ i ].OnNodeInteraction( node );
							}
							break;
						}
					}
				}
			}
		}


		public void OnNodeReOrderEvent( ParentNode node, int index )
		{
			if( node.Depth < index )
			{
				Debug.LogWarning( "Reorder canceled: This is a specific method for when reordering needs to be done and a its original index is higher than the new one" );
			}
			else
			{
				m_nodes.Remove( node );
				m_nodes.Insert( index, node );
				m_markToReOrder = true;
			}
		}

		public void AddNode( ParentNode node, bool updateId = false, bool addLast = true, bool registerUndo = true, bool fetchMaterialValues = true )
		{
			if( registerUndo )
			{
				UIUtils.MarkUndoAction();
				UndoUtils.RegisterCompleteObjectUndo( ParentWindow, Constants.UndoCreateNodeId );
				UndoUtils.RegisterCompleteObjectUndo( this, Constants.UndoCreateNodeId );
				UndoUtils.RegisterCreatedObjectUndo( node, Constants.UndoCreateNodeId );
			}

			if( OnNodeEvent != null )
			{
				OnNodeEvent( node );
			}
			if( updateId )
			{
				node.UniqueId = GetValidId();
			}
			else
			{
				UpdateIdFromNode( node );
			}



			if( addLast )
			{
				m_nodes.Add( node );
				node.Depth = m_nodes.Count;
			}
			else
			{
				m_nodes.Insert( 0, node );
				node.Depth = 0;
			}

			if( m_nodesDict.ContainsKey( node.UniqueId ) )
			{
				//m_nodesDict[ node.UniqueId ] = node;
				m_foundDuplicates = true;
			}
			else
			{
				m_nodesDict.Add( node.UniqueId, node );
				node.SetMaterialMode( CurrentMaterial, fetchMaterialValues );
			}

			m_nodeGrid.AddNodeToGrid( node );
			node.OnNodeChangeSizeEvent += OnNodeChangeSizeEvent;
			node.OnNodeReOrderEvent += OnNodeReOrderEvent;
			IsDirty = true;
		}

		public void CheckForDuplicates()
		{
			if( m_foundDuplicates )
			{
				Debug.LogWarning( "Found duplicates:" );
				m_foundDuplicates = false;
				m_nodesDict.Clear();
				int count = m_nodes.Count;
				for( int i = 0; i < count; i++ )
				{
					if( m_nodesDict.ContainsKey( m_nodes[ i ].UniqueId ) )
					{
						m_nodes[ i ].UniqueId = GetValidId();
						m_nodesDict.Add( m_nodes[ i ].UniqueId, m_nodes[ i ] );
						Debug.LogWarning( "Assigning new ID to " + m_nodes[ i ].TypeName );
					}
					else
					{
						m_nodesDict.Add( m_nodes[ i ].UniqueId, m_nodes[ i ] );
					}
				}
			}
		}

		public ParentNode GetClickedNode()
		{
			if( m_nodeClicked < 0 )
				return null;
			return GetNode( m_nodeClicked );
		}

		public PropertyNode GetInternalTemplateNode( int nodeId )
		{
			if( m_internalTemplateNodesDict.Count != m_internalTemplateNodesList.Count )
			{
				m_internalTemplateNodesDict.Clear();
				int count = m_internalTemplateNodesList.Count;
				for( int i = 0; i < m_internalTemplateNodesList.Count; i++ )
				{
					if( m_internalTemplateNodesList[ i ] != null )
						m_internalTemplateNodesDict.Add( m_internalTemplateNodesList[ i ].UniqueId, m_internalTemplateNodesList[ i ] );
				}
			}

			if( m_internalTemplateNodesDict.ContainsKey( nodeId ) )
				return m_internalTemplateNodesDict[ nodeId ];

			return null;
		}

		public PropertyNode GetInternalTemplateNode( string propertyName )
		{
			return m_internalTemplateNodesList.Find( ( x ) => x.PropertyName.Equals( propertyName ) );
		}

		public void AddInternalTemplateNode( TemplateShaderPropertyData data )
		{
			PropertyNode propertyNode = null;
			switch( data.PropertyDataType )
			{
				case WirePortDataType.FLOAT:
				propertyNode = CreateInstance<RangedFloatNode>(); break;
				case WirePortDataType.FLOAT4:
				propertyNode = CreateInstance<Vector4Node>();
				break;
				case WirePortDataType.COLOR:
				propertyNode = CreateInstance<ColorNode>();
				break;
				case WirePortDataType.INT:
				propertyNode = CreateInstance<IntNode>(); break;
				case WirePortDataType.SAMPLER1D:
				case WirePortDataType.SAMPLER2D:
				case WirePortDataType.SAMPLER3D:
				case WirePortDataType.SAMPLERCUBE:
				case WirePortDataType.SAMPLER2DARRAY:
				propertyNode = CreateInstance<SamplerNode>();
				break;
				default: return;
			}

			propertyNode.PropertyNameFromTemplate( data );

			// Create a negative unique Id to separate it from 
			// the regular ids on the main nodes list 
			// Its begins at -2 since -1 is used to detect invalid values
			int uniqueId = -( m_internalTemplateNodesList.Count + 2 );
			propertyNode.SetBaseUniqueId( uniqueId );

			//Register into Float/Int Nodes list to be available inline
			// Unique Id must be already set at this point to properly 
			// create array
			if( data.PropertyDataType == WirePortDataType.FLOAT ||
				data.PropertyDataType == WirePortDataType.INT )
				m_floatNodes.AddNode( propertyNode );

			m_internalTemplateNodesList.Add( propertyNode );
			m_internalTemplateNodesDict.Add( uniqueId, propertyNode );
		}

		public void ClearInternalTemplateNodes()
		{
			if( m_internalTemplateNodesList != null )
			{
				int count = m_internalTemplateNodesList.Count;
				for( int i = 0; i < count; i++ )
				{
					m_internalTemplateNodesList[ i ].Destroy();
					GameObject.DestroyImmediate( m_internalTemplateNodesList[ i ] );
				}

				m_internalTemplateNodesList.Clear();
				m_internalTemplateNodesDict.Clear();
			}
		}

		public ParentNode GetNode( int nodeId )
		{
			if( m_nodesDict.Count != m_nodes.Count )
			{
				m_nodesDict.Clear();
				int count = m_nodes.Count;
				for( int i = 0; i < count; i++ )
				{
					if( m_nodes[ i ] != null && !m_nodesDict.ContainsKey( m_nodes[ i ].UniqueId ) )
						m_nodesDict.Add( m_nodes[ i ].UniqueId, m_nodes[ i ] );
				}
			}

			if( m_nodesDict.ContainsKey( nodeId ) )
				return m_nodesDict[ nodeId ];

			return null;
		}

		public void ForceReOrder()
		{
			// @diogo: replaced Sort() with OrderBy() for stable sorting
			m_nodes.OrderBy( s => s.Depth );
		}

		public bool Draw( DrawInfo drawInfo )
		{
			MasterNode masterNode = GetNode( m_masterNodeId ) as MasterNode;
			if( m_forceCategoryRefresh && masterNode != null )
			{
				masterNode.RefreshAvailableCategories();
				m_forceCategoryRefresh = false;
			}

			SaveIsDirty = false;
			if( m_afterDeserializeFlag )
			{
				// this is now done after logic update... templates needs it this way
				//m_afterDeserializeFlag = false;

				CleanCorruptedNodes();
				if( m_nodes.Count == 0 )
				{
					//TODO: remove this temp from here
					NodeAvailability cachedCanvas = CurrentCanvasMode;
					ParentWindow.CreateNewGraph( "Empty" );
					CurrentCanvasMode = cachedCanvas;
					if( OnEmptyGraphDetectedEvt != null )
					{
						OnEmptyGraphDetectedEvt( this );
						SaveIsDirty = false;
					}
					else
					{
						SaveIsDirty = true;
					}
				}

				//for( int i = 0; i < m_nodes.Count; i++ )
				//{
				//	m_nodes[ i ].SetContainerGraph( this );
				//}
			}

			if( drawInfo.CurrentEventType == EventType.Repaint )
			{
				if( m_markedToDeSelect )
					DeSelectAll();

				if( m_markToSelect > -1 )
				{
					AddToSelectedNodes( GetNode( m_markToSelect ) );
					m_markToSelect = -1;
				}

				if( m_markToReOrder )
				{
					m_markToReOrder = false;
					int nodesCount = m_nodes.Count;
					for( int i = 0; i < nodesCount; i++ )
					{
						m_nodes[ i ].Depth = i;
					}
				}
			}

			if( drawInfo.CurrentEventType == EventType.Repaint )
			{
				// Resizing Nods per LOD level
				NodeLOD newLevel = NodeLOD.LOD0;
				float referenceValue;
				if( drawInfo.InvertedZoom > 0.5f )
				{
					newLevel = NodeLOD.LOD0;
					referenceValue = 4;
				}
				else if( drawInfo.InvertedZoom > 0.25f )
				{
					newLevel = NodeLOD.LOD1;
					referenceValue = 2;
				}
				else if( drawInfo.InvertedZoom > 0.15f )
				{
					newLevel = NodeLOD.LOD2;
					referenceValue = 1;
				}
				else if( drawInfo.InvertedZoom > 0.1f )
				{
					newLevel = NodeLOD.LOD3;
					referenceValue = 0;
				}
				else if( drawInfo.InvertedZoom > 0.07f )
				{
					newLevel = NodeLOD.LOD4;
					referenceValue = 0;
				}
				else
				{
					newLevel = NodeLOD.LOD5;
					referenceValue = 0;
				}

				// Just a sanity check
				nodeStyleOff = UIUtils.GetCustomStyle( CustomStyle.NodeWindowOff );
				nodeStyleOn = UIUtils.GetCustomStyle( CustomStyle.NodeWindowOn );//= UIUtils.GetCustomStyle( CustomStyle.NodeWindowOn );
				nodeTitle = UIUtils.GetCustomStyle( CustomStyle.NodeHeader );
				commentaryBackground = UIUtils.GetCustomStyle( CustomStyle.CommentaryBackground );

				if( newLevel != m_lodLevel || ( UIUtils.MainSkin != null && UIUtils.MainSkin.textField.border.left != referenceValue ) )
				{
					m_lodLevel = newLevel;
					switch( m_lodLevel )
					{
						default:
						case NodeLOD.LOD0:
						{
							UIUtils.MainSkin.textField.border = UIUtils.RectOffsetFour;
							nodeStyleOff.border = UIUtils.RectOffsetSix;
							UIUtils.NodeWindowOffSquare.border = UIUtils.RectOffsetFour;

							nodeStyleOn.border = UIUtils.RectOffsetSix;
							UIUtils.NodeWindowOnSquare.border = UIUtils.RectOffsetSix;

							nodeTitle.border.left = 6;
							nodeTitle.border.right = 6;
							nodeTitle.border.top = 6;
							nodeTitle.border.bottom = 4;

							UIUtils.NodeHeaderSquare.border = UIUtils.RectOffsetFour;
							commentaryBackground.border = UIUtils.RectOffsetSix;
						}
						break;
						case NodeLOD.LOD1:
						{
							UIUtils.MainSkin.textField.border = UIUtils.RectOffsetTwo;
							nodeStyleOff.border = UIUtils.RectOffsetFive;
							UIUtils.NodeWindowOffSquare.border = UIUtils.RectOffsetFive;

							nodeStyleOn.border = UIUtils.RectOffsetFive;
							UIUtils.NodeWindowOnSquare.border = UIUtils.RectOffsetFour;

							nodeTitle.border.left = 5;
							nodeTitle.border.right = 5;
							nodeTitle.border.top = 5;
							nodeTitle.border.bottom = 2;

							UIUtils.NodeHeaderSquare.border = UIUtils.RectOffsetThree;
							commentaryBackground.border = UIUtils.RectOffsetFive;
						}
						break;
						case NodeLOD.LOD2:
						{
							UIUtils.MainSkin.textField.border = UIUtils.RectOffsetOne;

							nodeStyleOff.border.left = 2;
							nodeStyleOff.border.right = 2;
							nodeStyleOff.border.top = 2;
							nodeStyleOff.border.bottom = 3;

							UIUtils.NodeWindowOffSquare.border = UIUtils.RectOffsetThree;

							nodeStyleOn.border.left = 4;
							nodeStyleOn.border.right = 4;
							nodeStyleOn.border.top = 4;
							nodeStyleOn.border.bottom = 3;

							UIUtils.NodeWindowOnSquare.border = UIUtils.RectOffsetThree;

							nodeTitle.border = UIUtils.RectOffsetTwo;
							UIUtils.NodeHeaderSquare.border = UIUtils.RectOffsetTwo;

							commentaryBackground.border.left = 2;
							commentaryBackground.border.right = 2;
							commentaryBackground.border.top = 2;
							commentaryBackground.border.bottom = 3;
						}
						break;
						case NodeLOD.LOD3:
						case NodeLOD.LOD4:
						case NodeLOD.LOD5:
						{
							UIUtils.MainSkin.textField.border = UIUtils.RectOffsetZero;

							nodeStyleOff.border.left = 1;
							nodeStyleOff.border.right = 1;
							nodeStyleOff.border.top = 1;
							nodeStyleOff.border.bottom = 2;

							UIUtils.NodeWindowOffSquare.border = UIUtils.RectOffsetTwo;

							nodeStyleOn.border = UIUtils.RectOffsetTwo;
							UIUtils.NodeWindowOnSquare.border = UIUtils.RectOffsetTwo;

							nodeTitle.border = UIUtils.RectOffsetOne;
							UIUtils.NodeHeaderSquare.border = UIUtils.RectOffsetOne;

							commentaryBackground.border.left = 1;
							commentaryBackground.border.right = 1;
							commentaryBackground.border.top = 1;
							commentaryBackground.border.bottom = 2;
						}
						break;
					}
				}
			}

			//m_visibleNodes.Clear();
			//int nullCount = 0;
			m_hasUnConnectedNodes = false;
			bool repaint = false;
			Material currentMaterial = masterNode != null ? masterNode.CurrentMaterial : null;
			EditorGUI.BeginChangeCheck();
			bool repaintMaterialInspector = false;

			int nodeCount = m_nodes.Count;
			for( int i = 0; i < nodeCount; i++ )
			{
				m_nodes[ i ].OnNodeLogicUpdate( drawInfo );
			}

			if( m_afterDeserializeFlag || m_lateOptionsRefresh )
			{
				m_afterDeserializeFlag = false;
				m_lateOptionsRefresh = false;
				if( CurrentCanvasMode == NodeAvailability.TemplateShader )
				{
					RefreshLinkedMasterNodes( true );
					OnRefreshLinkedPortsComplete();
					//If clipboard has cached nodes then a master node replacement will take place
					//We need to re-cache master nodes to ensure applied options are correctly cached
					//As first cache happens before that
					if( m_parentWindow.ClipboardInstance.HasCachedMasterNodes )
					{
						m_parentWindow.ClipboardInstance.AddMultiPassNodesToClipboard( MultiPassMasterNodes.NodesList,true,-1 );
						for( int i = 0; i < m_lodMultiPassMasterNodes.Count; i++ )
						{
							if( m_lodMultiPassMasterNodes[ i ].Count > 0 )
								m_parentWindow.ClipboardInstance.AddMultiPassNodesToClipboard( m_lodMultiPassMasterNodes[ i ].NodesList, false, i );
						}
					}
					//RepositionTemplateNodes( CurrentMasterNode );
				}
			}

			if( m_forceRepositionCheck )
			{
				RepositionTemplateNodes( CurrentMasterNode );
			}

			//for( int i = 0; i < m_functionNodes.NodesList.Count; i++ )
			//{
			//	m_functionNodes.NodesList[ i ].LogicGraph();
			//}

			//for( int i = 0; i < UIUtils.FunctionSwitchCopyList().Count; i++ )
			//{
			//	UIUtils.FunctionSwitchCopyList()[ i ].CheckReference();
			//}



			// Dont use nodeCount variable because node count can change in this loop???
			nodeCount = m_nodes.Count;
			ParentNode node = null;
			for( int i = 0; i < nodeCount; i++ )
			{
				node = m_nodes[ i ];
				if( !node.IsOnGrid )
				{
					m_nodeGrid.AddNodeToGrid( node );
				}

				node.MovingInFrame = false;

				if( drawInfo.CurrentEventType == EventType.Repaint )
					node.OnNodeLayout( drawInfo );

				m_hasUnConnectedNodes = m_hasUnConnectedNodes ||
										( node.ConnStatus != NodeConnectionStatus.Connected && node.ConnStatus != NodeConnectionStatus.Island );

				if( node.RequireMaterialUpdate && currentMaterial != null )
				{
					node.UpdateMaterial( currentMaterial );
					repaintMaterialInspector = true;
				}

				//if( node.IsVisible )
				//	m_visibleNodes.Add( node );

				IsDirty = ( m_isDirty || node.IsDirty );
				SaveIsDirty = ( m_saveIsDirty || node.SaveIsDirty );
			}

			// Handles GUI controls
			nodeCount = m_nodes.Count;
			for( int i = nodeCount - 1; i >= 0; i-- )
			//for ( int i = 0; i < nodeCount; i++ )
			{
				node = m_nodes[ i ];
				bool restoreMouse = false;
				if( drawInfo.CurrentEventType == EventType.MouseDown && m_nodeClicked > -1 && node.UniqueId != m_nodeClicked )
				{
					restoreMouse = true;
					drawInfo.CurrentEventType = EventType.Ignore;
				}

				node.DrawGUIControls( drawInfo );

				if( restoreMouse )
				{
					drawInfo.CurrentEventType = EventType.MouseDown;
				}
			}

			// Draw connection wires
			if( drawInfo.CurrentEventType == EventType.Repaint )
				DrawWires( ParentWindow.WireTexture, drawInfo, ParentWindow.WindowContextPallete.IsActive, ParentWindow.WindowContextPallete.CurrentPosition );

			// Master Draw
			nodeCount = m_nodes.Count;
			for( int i = 0; i < nodeCount; i++ )
			{
				node = m_nodes[ i ];
				bool restoreMouse = false;
				if( drawInfo.CurrentEventType == EventType.MouseDown && m_nodeClicked > -1 && node.UniqueId != m_nodeClicked )
				{
					restoreMouse = true;
					drawInfo.CurrentEventType = EventType.Ignore;
				}

				node.Draw( drawInfo );

				if( restoreMouse )
				{
					drawInfo.CurrentEventType = EventType.MouseDown;
				}
			}

			// Draw Tooltip
			if( drawInfo.CurrentEventType == EventType.Repaint || drawInfo.CurrentEventType == EventType.MouseDown )
			{
				nodeCount = m_nodes.Count;
				for( int i = nodeCount - 1; i >= 0; i-- )
				{
					node = m_nodes[ i ];
					if( node.IsVisible && !node.IsMoving )
					{
						bool showing = node.ShowTooltip( drawInfo );
						if( showing )
							break;
					}
				}
			}

			if( repaintMaterialInspector )
			{
				if( ASEMaterialInspector.Instance != null )
				{
					ASEMaterialInspector.Instance.Repaint();
				}
			}

			if( m_checkSelectedWireHighlights )
			{
				m_checkSelectedWireHighlights = false;
				ResetHighlightedWires();
				for( int i = 0; i < m_selectedNodes.Count; i++ )
				{
					HighlightWiresStartingNode( m_selectedNodes[ i ] );
				}
			}

			if( EditorGUI.EndChangeCheck() )
			{
				SaveIsDirty = true;
				repaint = true;
			}

			if( drawInfo.CurrentEventType == EventType.Repaint )
			{
				// Revert LOD changes to LOD0 (only if it's different)
				if( UIUtils.MainSkin.textField.border.left != 4 )
				{
					UIUtils.MainSkin.textField.border = UIUtils.RectOffsetFour;
					nodeStyleOff.border = UIUtils.RectOffsetSix;
					UIUtils.NodeWindowOffSquare.border = UIUtils.RectOffsetFour;

					nodeStyleOn.border = UIUtils.RectOffsetSix;
					UIUtils.NodeWindowOnSquare.border = UIUtils.RectOffsetSix;

					nodeTitle.border.left = 6;
					nodeTitle.border.right = 6;
					nodeTitle.border.top = 6;
					nodeTitle.border.bottom = 4;

					UIUtils.NodeHeaderSquare.border = UIUtils.RectOffsetFour;
					commentaryBackground.border = UIUtils.RectOffsetSix;
				}
			}

			//if ( nullCount == m_nodes.Count )
			//	m_nodes.Clear();

			ChangedLightingModel = false;

			return repaint;
		}

		public bool UpdateMarkForDeletion()
		{
			if( m_markedForDeletion.Count != 0 )
			{
				DeleteMarkedForDeletionNodes();
				return true;
			}
			return false;
		}

		public void DrawWires( Texture2D wireTex, DrawInfo drawInfo, bool contextPaletteActive, Vector3 contextPalettePos )
		{
			//Handles.BeginGUI();
			//Debug.Log(GUI.depth);
			// Draw connected node wires
			m_wireBezierCount = 0;
			for( int nodeIdx = 0; nodeIdx < m_nodes.Count; nodeIdx++ )
			{
				ParentNode node = m_nodes[ nodeIdx ];
				if( (object)node == null )
					return;

				for( int inputPortIdx = 0; inputPortIdx < node.InputPorts.Count; inputPortIdx++ )
				{
					InputPort inputPort = node.InputPorts[ inputPortIdx ];
					if(  inputPort.ExternalReferences.Count > 0 && inputPort.Visible )
					{
						bool cleanInvalidConnections = false;
						for( int wireIdx = 0; wireIdx < inputPort.ExternalReferences.Count; wireIdx++ )
						{
							WireReference reference = inputPort.ExternalReferences[ wireIdx ];
							if( reference.NodeId != -1 && reference.PortId != -1 )
							{
								ParentNode outputNode = GetNode( reference.NodeId );
								if( outputNode != null )
								{
									OutputPort outputPort = outputNode.GetOutputPortByUniqueId( reference.PortId );
									Vector3 endPos = new Vector3( inputPort.Position.x, inputPort.Position.y );
									Vector3 startPos = new Vector3( outputPort.Position.x, outputPort.Position.y );
									float x = ( startPos.x < endPos.x ) ? startPos.x : endPos.x;
									float y = ( startPos.y < endPos.y ) ? startPos.y : endPos.y;
									float width = Mathf.Abs( startPos.x - endPos.x ) + outputPort.Position.width;
									float height = Mathf.Abs( startPos.y - endPos.y ) + outputPort.Position.height;
									Rect portsBoundingBox = new Rect( x, y, width, height );

									bool isVisible = node.IsVisible || outputNode.IsVisible;
									if( !isVisible )
									{
										isVisible = drawInfo.TransformedCameraArea.Overlaps( portsBoundingBox );
									}

									if( isVisible )
									{

										Rect bezierBB = DrawBezier( drawInfo.InvertedZoom, startPos, endPos, inputPort.DataType, outputPort.DataType, node.GetInputPortVisualDataTypeByArrayIdx( inputPortIdx ), outputNode.GetOutputPortVisualDataTypeById( reference.PortId ), reference.WireStatus, wireTex, node, outputNode );
										bezierBB.x -= Constants.OUTSIDE_WIRE_MARGIN;
										bezierBB.y -= Constants.OUTSIDE_WIRE_MARGIN;

										bezierBB.width += Constants.OUTSIDE_WIRE_MARGIN * 2;
										bezierBB.height += Constants.OUTSIDE_WIRE_MARGIN * 2;

										if( m_wireBezierCount < m_bezierReferences.Count )
										{
											m_bezierReferences[ m_wireBezierCount ].UpdateInfo( ref bezierBB, inputPort.NodeId, inputPort.PortId, outputPort.NodeId, outputPort.PortId );
										}
										else
										{
											m_bezierReferences.Add( new WireBezierReference( ref bezierBB, inputPort.NodeId, inputPort.PortId, outputPort.NodeId, outputPort.PortId ) );
										}
										m_wireBezierCount++;

									}
								}
								else
								{
									if( DebugConsoleWindow.DeveloperMode )
										UIUtils.ShowMessage( "Detected Invalid connection from node " + node.UniqueId + " port " + inputPortIdx + " to Node " + reference.NodeId + " port " + reference.PortId, MessageSeverity.Error );
									cleanInvalidConnections = true;
									inputPort.ExternalReferences[ wireIdx ].Invalidate();
								}
							}
						}

						if( cleanInvalidConnections )
						{
							inputPort.RemoveInvalidConnections();
						}
					}
				}
			}

			//Draw selected wire
			if( m_parentWindow.WireReferenceUtils.ValidReferences() )
			{
				if( m_parentWindow.WireReferenceUtils.InputPortReference.IsValid )
				{
					InputPort inputPort = GetNode( m_parentWindow.WireReferenceUtils.InputPortReference.NodeId ).GetInputPortByUniqueId( m_parentWindow.WireReferenceUtils.InputPortReference.PortId );
					Vector3 endPos = Vector3.zero;
					if( m_parentWindow.WireReferenceUtils.SnapEnabled )
					{
						Vector2 pos = ( m_parentWindow.WireReferenceUtils.SnapPosition + drawInfo.CameraOffset ) * drawInfo.InvertedZoom;
						endPos = new Vector3( pos.x, pos.y ) + UIUtils.ScaledPortsDelta;
					}
					else
					{
						endPos = contextPaletteActive ? contextPalettePos : new Vector3( Event.current.mousePosition.x, Event.current.mousePosition.y );
					}

					Vector3 startPos = new Vector3( inputPort.Position.x, inputPort.Position.y );
					DrawBezier( drawInfo.InvertedZoom, endPos, startPos, inputPort.DataType, inputPort.DataType, inputPort.DataType, inputPort.DataType, WireStatus.Default, wireTex );
				}

				if( m_parentWindow.WireReferenceUtils.OutputPortReference.IsValid )
				{
					OutputPort outputPort = GetNode( m_parentWindow.WireReferenceUtils.OutputPortReference.NodeId ).GetOutputPortByUniqueId( m_parentWindow.WireReferenceUtils.OutputPortReference.PortId );
					Vector3 endPos = Vector3.zero;
					if( m_parentWindow.WireReferenceUtils.SnapEnabled )
					{
						Vector2 pos = ( m_parentWindow.WireReferenceUtils.SnapPosition + drawInfo.CameraOffset ) * drawInfo.InvertedZoom;
						endPos = new Vector3( pos.x, pos.y ) + UIUtils.ScaledPortsDelta;
					}
					else
					{
						endPos = contextPaletteActive ? contextPalettePos : new Vector3( Event.current.mousePosition.x, Event.current.mousePosition.y );
					}
					Vector3 startPos = new Vector3( outputPort.Position.x, outputPort.Position.y );
					DrawBezier( drawInfo.InvertedZoom, startPos, endPos, outputPort.DataType, outputPort.DataType, outputPort.DataType, outputPort.DataType, WireStatus.Default, wireTex );
				}
			}
			//Handles.EndGUI();
		}

		Rect DrawBezier( float invertedZoom, Vector3 startPos, Vector3 endPos, WirePortDataType inputDataType, WirePortDataType outputDataType, WirePortDataType inputVisualDataType, WirePortDataType outputVisualDataType, WireStatus wireStatus, Texture2D wireTex, ParentNode inputNode = null, ParentNode outputNode = null )
		{
			startPos += UIUtils.ScaledPortsDelta;
			endPos += UIUtils.ScaledPortsDelta;

			// Calculate the 4 points for bezier taking into account wire nodes and their automatic tangents
			float mag = ( endPos - startPos ).magnitude;
			float resizedMag = Mathf.Min( mag * 0.66f, Constants.HORIZONTAL_TANGENT_SIZE * invertedZoom );

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

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

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

			///////////////Draw tangents
			//Rect box1 = new Rect( new Vector2( startTangent.x, startTangent.y ), new Vector2( 10, 10 ) );
			//box1.x -= box1.width * 0.5f;
			//box1.y -= box1.height * 0.5f;
			//GUI.Label( box1, string.Empty, UIUtils.Box );

			//Rect box2 = new Rect( new Vector2( endTangent.x, endTangent.y ), new Vector2( 10, 10 ) );
			//box2.x -= box2.width * 0.5f;
			//box2.y -= box2.height * 0.5f;
			//GUI.Label( box2, string.Empty, UIUtils.Box );

			//m_auxRect.Set( 0, 0, UIUtils.CurrentWindow.position.width, UIUtils.CurrentWindow.position.height );
			//GLDraw.BeginGroup( m_auxRect );

			int ty = 1;
			float wireThickness = 0;


			if( ParentWindow.Options.MultiLinePorts )
			{
				GLDraw.MultiLine = true;
				Shader.SetGlobalFloat( "_InvertedZoom", invertedZoom );

				WirePortDataType smallest = ( (int)outputDataType < (int)inputDataType ? outputDataType : inputDataType );
				smallest = ( (int)smallest < (int)outputVisualDataType ? smallest : outputVisualDataType );
				smallest = ( (int)smallest < (int)inputVisualDataType ? smallest : inputVisualDataType );

				switch( smallest )
				{
					case WirePortDataType.FLOAT2: ty = 2; break;
					case WirePortDataType.FLOAT3: ty = 3; break;
					case WirePortDataType.FLOAT4:
					case WirePortDataType.COLOR:
					{
						ty = 4;
					}
					break;
					default: ty = 1; break;
				}
				wireThickness = Mathf.Lerp( Constants.WIRE_WIDTH * ( ty * invertedZoom * -0.05f + 0.15f ), Constants.WIRE_WIDTH * ( ty * invertedZoom * 0.175f + 0.3f ), invertedZoom + 0.4f );
			}
			else
			{
				GLDraw.MultiLine = false;
				wireThickness = Mathf.Lerp( Constants.WIRE_WIDTH * ( invertedZoom * -0.05f + 0.15f ), Constants.WIRE_WIDTH * ( invertedZoom * 0.175f + 0.3f ), invertedZoom + 0.4f );
			}

			Rect boundBox = new Rect();
			int segments = 11;
			if( LodLevel <= ParentGraph.NodeLOD.LOD4 )
				segments = Mathf.Clamp( Mathf.FloorToInt( mag * 0.2f * invertedZoom ), 11, 35 );
			else
				segments = (int)( invertedZoom * 14.28f * 11 );

			if( ParentWindow.Options.ColoredPorts && wireStatus != WireStatus.Highlighted )
				boundBox = GLDraw.DrawBezier( startPos, startTangent, endPos, endTangent, UIUtils.GetColorForDataType( outputVisualDataType, false, false ), UIUtils.GetColorForDataType( inputVisualDataType, false, false ), wireThickness, segments, ty );
			else
				boundBox = GLDraw.DrawBezier( startPos, startTangent, endPos, endTangent, UIUtils.GetColorFromWireStatus( wireStatus ), wireThickness, segments, ty );
			//GLDraw.EndGroup();

			//GUI.Box( m_auxRect, string.Empty, UIUtils.CurrentWindow.CustomStylesInstance.Box );
			//GUI.Box( boundBox, string.Empty, UIUtils.CurrentWindow.CustomStylesInstance.Box );
			//if ( UIUtils.CurrentWindow.Options.ColoredPorts && wireStatus != WireStatus.Highlighted )
			//	Handles.DrawBezier( startPos, endPos, startTangent, endTangent, UIUtils.GetColorForDataType( outputDataType, false, false ), wireTex, wiresTickness );
			//else
			//	Handles.DrawBezier( startPos, endPos, startTangent, endTangent, UIUtils.GetColorFromWireStatus( wireStatus ), wireTex, wiresTickness );

			//Handles.DrawLine( startPos, startTangent );
			//Handles.DrawLine( endPos, endTangent );

			float extraBound = 30 * invertedZoom;
			boundBox.xMin -= extraBound;
			boundBox.xMax += extraBound;
			boundBox.yMin -= extraBound;
			boundBox.yMax += extraBound;

			return boundBox;
		}

		public void DrawBezierBoundingBox()
		{
			for( int i = 0; i < m_wireBezierCount; i++ )
			{
				m_bezierReferences[ i ].DebugDraw();
			}
		}

		public WireBezierReference GetWireBezierInPos( Vector2 position )
		{
			for( int i = 0; i < m_wireBezierCount; i++ )
			{
				if( m_bezierReferences[ i ].Contains( position ) )
					return m_bezierReferences[ i ];
			}
			return null;
		}


		public List<WireBezierReference> GetWireBezierListInPos( Vector2 position )
		{
			List<WireBezierReference> list = new List<WireBezierReference>();
			for( int i = 0; i < m_wireBezierCount; i++ )
			{
				if( m_bezierReferences[ i ].Contains( position ) )
					list.Add( m_bezierReferences[ i ] );
			}

			return list;
		}


		public void MoveSelectedNodes( Vector2 delta, bool snap = false )
		{
			//bool validMovement = delta.magnitude > 0.001f;
			//if ( validMovement )
			//{
			//	UndoUtils.RegisterCompleteObjectUndo( ParentWindow, Constants.UndoMoveNodesId );
			//	for ( int i = 0; i < m_selectedNodes.Count; i++ )
			//	{
			//		if ( !m_selectedNodes[ i ].MovingInFrame )
			//		{
			//			UndoUtils.RecordObject( m_selectedNodes[ i ], Constants.UndoMoveNodesId );
			//			m_selectedNodes[ i ].Move( delta, snap );
			//		}
			//	}
			//	IsDirty = true;
			//}

			bool performUndo = delta.magnitude > 0.01f;
			if( performUndo )
			{
				UndoUtils.RegisterCompleteObjectUndo( ParentWindow, Constants.UndoMoveNodesId );
				UndoUtils.RegisterCompleteObjectUndo( this, Constants.UndoMoveNodesId );
			}

			for( int i = 0; i < m_selectedNodes.Count; i++ )
			{
				if( !m_selectedNodes[ i ].MovingInFrame )
				{
					if( performUndo )
						m_selectedNodes[ i ].RecordObject( Constants.UndoMoveNodesId );
					m_selectedNodes[ i ].Move( delta, snap );
				}
			}

			IsDirty = true;
		}

		public void SetConnection( int InNodeId, int InPortId, int OutNodeId, int OutPortId )
		{
			ParentNode inNode = GetNode( InNodeId );
			ParentNode outNode = GetNode( OutNodeId );
			InputPort inputPort = null;
			OutputPort outputPort = null;
			if( inNode != null && outNode != null )
			{
				inputPort = inNode.GetInputPortByUniqueId( InPortId );
				outputPort = outNode.GetOutputPortByUniqueId( OutPortId );
				if( inputPort != null && outputPort != null )
				{
					if( inputPort.IsConnectedTo( OutNodeId, OutPortId ) || outputPort.IsConnectedTo( InNodeId, InPortId ) )
					{
						if( DebugConsoleWindow.DeveloperMode )
							UIUtils.ShowMessage( "Node/Port already connected " + InNodeId, MessageSeverity.Error );
						return;
					}

					if( !inputPort.CheckValidType( outputPort.DataType ) )
					{
						if( DebugConsoleWindow.DeveloperMode )
							UIUtils.ShowIncompatiblePortMessage( true, inNode, inputPort, outNode, outputPort );
						return;
					}

					if( !outputPort.CheckValidType( inputPort.DataType ) )
					{

						if( DebugConsoleWindow.DeveloperMode )
							UIUtils.ShowIncompatiblePortMessage( false, outNode, outputPort, inNode, inputPort );
						return;
					}
					if( !inputPort.Available || !outputPort.Available )
					{
						if( DebugConsoleWindow.DeveloperMode )
							UIUtils.ShowMessage( "Ports not available to connection", MessageSeverity.Warning );

						return;
					}

					if( inputPort.ConnectTo( OutNodeId, OutPortId, outputPort.DataType, false ) )
					{
						inNode.OnInputPortConnected( InPortId, OutNodeId, OutPortId );
					}


					if( outputPort.ConnectTo( InNodeId, InPortId, inputPort.DataType, inputPort.TypeLocked ) )
					{
						outNode.OnOutputPortConnected( OutPortId, InNodeId, InPortId );
					}
				}
				else if( (object)inputPort == null )
				{
					if( DebugConsoleWindow.DeveloperMode )
						UIUtils.ShowMessage( "Input Port " + InPortId + " doesn't exist on node " + InNodeId, MessageSeverity.Error );
				}
				else
				{
					if( DebugConsoleWindow.DeveloperMode )
						UIUtils.ShowMessage( "Output Port " + OutPortId + " doesn't exist on node " + OutNodeId, MessageSeverity.Error );
				}
			}
			else if( (object)inNode == null )
			{
				if( DebugConsoleWindow.DeveloperMode )
					UIUtils.ShowMessage( "Input node " + InNodeId + " doesn't exist", MessageSeverity.Error );
			}
			else
			{
				if( DebugConsoleWindow.DeveloperMode )
					UIUtils.ShowMessage( "Output node " + OutNodeId + " doesn't exist", MessageSeverity.Error );
			}
		}

		public void CreateConnection( int inNodeId, int inPortId, int outNodeId, int outPortId, bool registerUndo = true )
		{
			ParentNode outputNode = GetNode( outNodeId );
			if( outputNode != null )
			{
				OutputPort outputPort = outputNode.GetOutputPortByUniqueId( outPortId );
				if( outputPort != null )
				{
					ParentNode inputNode = GetNode( inNodeId );
					InputPort inputPort = inputNode.GetInputPortByUniqueId( inPortId );

					if( !inputPort.CheckValidType( outputPort.DataType ) )
					{
						UIUtils.ShowIncompatiblePortMessage( true, inputNode, inputPort, outputNode, outputPort );
						return;
					}

					if( !outputPort.CheckValidType( inputPort.DataType ) )
					{
						UIUtils.ShowIncompatiblePortMessage( false, outputNode, outputPort, inputNode, inputPort );
						return;
					}

					inputPort.DummyAdd( outputPort.NodeId, outputPort.PortId );
					outputPort.DummyAdd( inNodeId, inPortId );

					if( UIUtils.DetectNodeLoopsFrom( inputNode, new Dictionary<int, int>() ) )
					{
						inputPort.DummyRemove();
						outputPort.DummyRemove();
						m_parentWindow.WireReferenceUtils.InvalidateReferences();
						UIUtils.ShowMessage( "Infinite Loop detected" );
						Event.current.Use();
						return;
					}

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

					if( inputPort.IsConnected )
					{
						DeleteConnection( true, inNodeId, inPortId, true, false, registerUndo );
					}

					//link output to input
					if( outputPort.ConnectTo( inNodeId, inPortId, inputPort.DataType, inputPort.TypeLocked ) )
						outputNode.OnOutputPortConnected( outputPort.PortId, inNodeId, inPortId );

					//link input to output
					if( inputPort.ConnectTo( outputPort.NodeId, outputPort.PortId, outputPort.DataType, inputPort.TypeLocked ) )
						inputNode.OnInputPortConnected( inPortId, outputNode.UniqueId, outputPort.PortId );

					MarkWireHighlights();
				}
				SaveIsDirty = true;
				//ParentWindow.ShaderIsModified = true;
			}
		}

		public void DeleteInvalidConnections()
		{
			int count = m_nodes.Count;
			for( int nodeIdx = 0; nodeIdx < count; nodeIdx++ )
			{
				{
					int inputCount = m_nodes[ nodeIdx ].InputPorts.Count;
					for( int inputIdx = 0; inputIdx < inputCount; inputIdx++ )
					{
						if( !m_nodes[ nodeIdx ].InputPorts[ inputIdx ].Visible &&
							m_nodes[ nodeIdx ].InputPorts[ inputIdx ].IsConnected &&
							!m_nodes[ nodeIdx ].InputPorts[ inputIdx ].IsDummy )
						{
							DeleteConnection( true, m_nodes[ nodeIdx ].UniqueId, m_nodes[ nodeIdx ].InputPorts[ inputIdx ].PortId, true, true );
						}
					}
				}
				{
					int outputCount = m_nodes[ nodeIdx ].OutputPorts.Count;
					for( int outputIdx = 0; outputIdx < outputCount; outputIdx++ )
					{
						if( !m_nodes[ nodeIdx ].OutputPorts[ outputIdx ].Visible && m_nodes[ nodeIdx ].OutputPorts[ outputIdx ].IsConnected )
						{
							DeleteConnection( false, m_nodes[ nodeIdx ].UniqueId, m_nodes[ nodeIdx ].OutputPorts[ outputIdx ].PortId, true, true );
						}
					}
				}
			}
		}

		public void DeleteAllConnectionFromNode( int nodeId, bool registerOnLog, bool propagateCallback, bool registerUndo )
		{
			ParentNode node = GetNode( nodeId );
			if( (object)node == null )
				return;
			DeleteAllConnectionFromNode( node, registerOnLog, propagateCallback, registerUndo );
		}

		public void DeleteAllConnectionFromNode( ParentNode node, bool registerOnLog, bool propagateCallback, bool registerUndo )
		{

			for( int i = 0; i < node.InputPorts.Count; i++ )
			{
				if( node.InputPorts[ i ].IsConnected )
					DeleteConnection( true, node.UniqueId, node.InputPorts[ i ].PortId, registerOnLog, propagateCallback, registerUndo );
			}

			for( int i = 0; i < node.OutputPorts.Count; i++ )
			{
				if( node.OutputPorts[ i ].IsConnected )
					DeleteConnection( false, node.UniqueId, node.OutputPorts[ i ].PortId, registerOnLog, propagateCallback, registerUndo );
			}
		}

		public void DeleteConnection( bool isInput, int nodeId, int portId, bool registerOnLog, bool propagateCallback, bool registerUndo = true )
		{
			ParentNode node = GetNode( nodeId );
			if( (object)node == null )
				return;

			if( registerUndo )
			{
				UIUtils.MarkUndoAction();
				UndoUtils.RegisterCompleteObjectUndo( ParentWindow, Constants.UndoDeleteConnectionId );
				UndoUtils.RegisterCompleteObjectUndo( this, Constants.UndoDeleteConnectionId );
				node.RecordObject( Constants.UndoDeleteConnectionId );
			}

			if( isInput )
			{
				InputPort inputPort = node.GetInputPortByUniqueId( portId );
				if( inputPort != null && inputPort.IsConnected )
				{

					if( node.ConnStatus == NodeConnectionStatus.Connected )
					{
						node.DeactivateInputPortNode( portId, false );
						//inputPort.GetOutputNode().DeactivateNode( portId, false );
						m_checkSelectedWireHighlights = true;
					}

					for( int i = 0; i < inputPort.ExternalReferences.Count; i++ )
					{
						WireReference inputReference = inputPort.ExternalReferences[ i ];
						ParentNode outputNode = GetNode( inputReference.NodeId );
						if( registerUndo )
							outputNode.RecordObject( Constants.UndoDeleteConnectionId );
						outputNode.GetOutputPortByUniqueId( inputReference.PortId ).InvalidateConnection( inputPort.NodeId, inputPort.PortId );
						if( propagateCallback )
							outputNode.OnOutputPortDisconnected( inputReference.PortId );
					}
					inputPort.InvalidateAllConnections();
					if( propagateCallback )
						node.OnInputPortDisconnected( portId );
				}
			}
			else
			{
				OutputPort outputPort = node.GetOutputPortByUniqueId( portId );
				if( outputPort != null && outputPort.IsConnected )
				{
					if( propagateCallback )
						node.OnOutputPortDisconnected( portId );

					for( int i = 0; i < outputPort.ExternalReferences.Count; i++ )
					{
						WireReference outputReference = outputPort.ExternalReferences[ i ];
						ParentNode inputNode = GetNode( outputReference.NodeId );
						if( registerUndo )
							inputNode.RecordObject( Constants.UndoDeleteConnectionId );
						if( inputNode.ConnStatus == NodeConnectionStatus.Connected )
						{
							node.DeactivateNode( portId, false );
							m_checkSelectedWireHighlights = true;
						}
						inputNode.GetInputPortByUniqueId( outputReference.PortId ).InvalidateConnection( outputPort.NodeId, outputPort.PortId );
						if( propagateCallback )
						{
							// Removing WireNodes fires this after the rewiring ( and the OnInputPortConnected callback ) which causes incorrect behaviors
							// If is connected is true then we're on that case so we don't fire the OnInputPortDisconnected
							if( !inputNode.GetInputPortByUniqueId( outputReference.PortId ).IsConnected )
								inputNode.OnInputPortDisconnected( outputReference.PortId );
						}
					}
					outputPort.InvalidateAllConnections();
				}
			}
			IsDirty = true;
			SaveIsDirty = true;
		}

		//public void DeleteSelectedNodes()
		//{
		//	bool invalidateMasterNode = false;
		//	int count = m_selectedNodes.Count;
		//	for( int nodeIdx = 0; nodeIdx < count; nodeIdx++ )
		//	{
		//		ParentNode node = m_selectedNodes[ nodeIdx ];
		//		if( node.UniqueId == m_masterNodeId )
		//		{
		//			invalidateMasterNode = true;
		//		}
		//		else
		//		{
		//			DestroyNode( node );
		//		}
		//	}

		//	if( invalidateMasterNode )
		//	{
		//		CurrentOutputNode.Selected = false;
		//	}
		//	//Clear all references
		//	m_selectedNodes.Clear();
		//	IsDirty = true;
		//}

		public void DeleteNodesOnArray( ref ParentNode[] nodeArray )
		{
			bool invalidateMasterNode = false;
			for( int nodeIdx = 0; nodeIdx < nodeArray.Length; nodeIdx++ )
			{
				ParentNode node = nodeArray[ nodeIdx ];
				if( node.UniqueId == m_masterNodeId )
				{
					FunctionOutput fout = node as FunctionOutput;
					if( fout != null )
					{
						for( int i = 0; i < m_nodes.Count; i++ )
						{
							FunctionOutput secondfout = m_nodes[ i ] as FunctionOutput;
							if( secondfout != null && secondfout != fout )
							{
								secondfout.Function = fout.Function;
								AssignMasterNode( secondfout, false );

								DeselectNode( fout );
								DestroyNode( fout );
								break;
							}
						}
					}
					invalidateMasterNode = true;
				}
				else
				{
					DeselectNode( node );
					DestroyNode( node );
				}
				nodeArray[ nodeIdx ] = null;
			}

			if( invalidateMasterNode && CurrentMasterNode != null )
			{
				CurrentMasterNode.Selected = false;
			}

			//Clear all references
			nodeArray = null;
			IsDirty = true;
		}

		public void MarkWireNodeSequence( WireNode node, bool isInput )
		{
			if( node == null )
			{
				return;
			}

			if( m_markedForDeletion.Contains( node ) )
				return;

			m_markedForDeletion.Add( node );

			if( isInput && node.InputPorts[ 0 ].IsConnected )
			{
				MarkWireNodeSequence( GetNode( node.InputPorts[ 0 ].ExternalReferences[ 0 ].NodeId ) as WireNode, isInput );
			}
			else if( !isInput && node.OutputPorts[ 0 ].IsConnected )
			{
				MarkWireNodeSequence( GetNode( node.OutputPorts[ 0 ].ExternalReferences[ 0 ].NodeId ) as WireNode, isInput );
			}
		}

		public void UndoableDeleteSelectedNodes( List<ParentNode> nodeList )
		{
			if( nodeList.Count == 0 )
				return;

			List<ParentNode> validNode = new List<ParentNode>();

			for( int i = 0; i < nodeList.Count; i++ )
			{
				if( nodeList[ i ] != null && nodeList[ i ].UniqueId != m_masterNodeId )
				{
					validNode.Add( nodeList[ i ] );
				}
			}
			UIUtils.ClearUndoHelper();
			ParentNode[] selectedNodes = new ParentNode[ validNode.Count ];
			for( int i = 0; i < selectedNodes.Length; i++ )
			{
				if( validNode[ i ] != null )
				{
					selectedNodes[ i ] = validNode[ i ];
					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
				if( selectedNodes[ selectedNodeIdx ] != null )
				{
					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 = GetNode( nodeIdx );
									if( node != null && UIUtils.CheckUndoNode( node ) )
									{
										extraNodes.Add( node );
									}
								}
							}
						}
					}
				}

				// Check outputs
				if( selectedNodes[ selectedNodeIdx ] != null )
				{
					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 = GetNode( nodeIdx );
										if( UIUtils.CheckUndoNode( node ) )
										{
											extraNodes.Add( node );
										}
									}
								}
							}
						}
					}

				}
			}

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

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

			extraNodes.Clear();
			extraNodes = null;

			EditorUtility.SetDirty( ParentWindow );

			ParentWindow.ForceRepaint();
		}

		public void DeleteMarkedForDeletionNodes()
		{
			UndoableDeleteSelectedNodes( m_markedForDeletion );
			m_markedForDeletion.Clear();
			IsDirty = true;

			//bool invalidateMasterNode = false;
			//int count = m_markedForDeletion.Count;
			//for ( int nodeIdx = 0; nodeIdx < count; nodeIdx++ )
			//{
			//	ParentNode node = m_markedForDeletion[ nodeIdx ];
			//	if ( node.UniqueId == m_masterNodeId )
			//	{
			//		invalidateMasterNode = true;
			//	}
			//	else
			//	{
			//		if ( node.Selected )
			//		{
			//			m_selectedNodes.Remove( node );
			//			node.Selected = false;
			//		}
			//		DestroyNode( node );
			//	}
			//}

			//if ( invalidateMasterNode )
			//{
			//	CurrentMasterNode.Selected = false;
			//}
			////Clear all references
			//m_markedForDeletion.Clear();
			//IsDirty = true;
		}

		public void DestroyNode( int nodeId )
		{
			ParentNode node = GetNode( nodeId );
			DestroyNode( node );
		}

		public void DestroyNode( ParentNode node, bool registerUndo = true, bool destroyMasterNode = false )
		{
			if( node == null )
			{
				UIUtils.ShowMessage( "Attempting to destroying a inexistant node ", MessageSeverity.Warning );
				return;
			}

			if( node.ConnStatus == NodeConnectionStatus.Connected && !m_checkSelectedWireHighlights )
			{
				ResetHighlightedWires();
				m_checkSelectedWireHighlights = true;
			}

			//TODO: check better placement of this code (reconnects wires from wire nodes)
			//if ( node.GetType() == typeof( WireNode ) )
			//{
			//	if ( node.InputPorts[ 0 ].ExternalReferences != null && node.InputPorts[ 0 ].ExternalReferences.Count > 0 )
			//	{
			//		WireReference backPort = node.InputPorts[ 0 ].ExternalReferences[ 0 ];
			//		for ( int i = 0; i < node.OutputPorts[ 0 ].ExternalReferences.Count; i++ )
			//		{
			//			UIUtils.CurrentWindow.ConnectInputToOutput( node.OutputPorts[ 0 ].ExternalReferences[ i ].NodeId, node.OutputPorts[ 0 ].ExternalReferences[ i ].PortId, backPort.NodeId, backPort.PortId );
			//		}
			//	}
			//}
			if( destroyMasterNode || ( node.UniqueId != m_masterNodeId && !( node is TemplateMultiPassMasterNode )/*!m_multiPassMasterNodes.HasNode( node.UniqueId )*/ ) )
			{
				m_nodeGrid.RemoveNodeFromGrid( node, false );
				//Send Deactivation signal if active
				if( node.ConnStatus == NodeConnectionStatus.Connected )
				{
					node.DeactivateNode( -1, true );
				}

				//Invalidate references
				//Invalidate input references
				for( int inputPortIdx = 0; inputPortIdx < node.InputPorts.Count; inputPortIdx++ )
				{
					InputPort inputPort = node.InputPorts[ inputPortIdx ];
					if( inputPort.IsConnected )
					{
						for( int wireIdx = 0; wireIdx < inputPort.ExternalReferences.Count; wireIdx++ )
						{
							WireReference inputReference = inputPort.ExternalReferences[ wireIdx ];
							ParentNode outputNode = GetNode( inputReference.NodeId );
							outputNode.GetOutputPortByUniqueId( inputReference.PortId ).InvalidateConnection( inputPort.NodeId, inputPort.PortId );
							outputNode.OnOutputPortDisconnected( inputReference.PortId );
						}
						inputPort.InvalidateAllConnections();
					}
				}

				//Invalidate output reference
				for( int outputPortIdx = 0; outputPortIdx < node.OutputPorts.Count; outputPortIdx++ )
				{
					OutputPort outputPort = node.OutputPorts[ outputPortIdx ];
					if( outputPort.IsConnected )
					{
						for( int wireIdx = 0; wireIdx < outputPort.ExternalReferences.Count; wireIdx++ )
						{
							WireReference outputReference = outputPort.ExternalReferences[ wireIdx ];
							ParentNode outnode = GetNode( outputReference.NodeId );
							if( outnode != null )
							{
								outnode.GetInputPortByUniqueId( outputReference.PortId ).InvalidateConnection( outputPort.NodeId, outputPort.PortId );
								outnode.OnInputPortDisconnected( outputReference.PortId );
							}
						}
						outputPort.InvalidateAllConnections();
					}
				}

				//Remove node from main list
				//UndoUtils.RecordObject( node, "Destroying node " + ( node.Attributes != null? node.Attributes.Name: node.GetType().ToString() ) );
				if( registerUndo )
				{
					UIUtils.MarkUndoAction();
					UndoUtils.RegisterCompleteObjectUndo( ParentWindow, Constants.UndoDeleteNodeId );
					UndoUtils.RegisterCompleteObjectUndo( this, Constants.UndoDeleteNodeId );
					node.RecordObjectOnDestroy( Constants.UndoDeleteNodeId );
				}

				if( OnNodeRemovedEvent != null )
					OnNodeRemovedEvent( node );

				m_nodes.Remove( node );
				m_nodesDict.Remove( node.UniqueId );
				node.Destroy();
				if ( registerUndo )
				{
					UndoUtils.DestroyObjectImmediate( node );
				}
				else
				{
					DestroyImmediate( node );
				}					
				IsDirty = true;
				m_markToReOrder = true;
			}
			//else if( node.UniqueId == m_masterNodeId && node.GetType() == typeof(FunctionOutput) )
			//{
			//	Debug.Log( "Attempting to destroy a output node" );
			//	DeselectNode( node );
			//	UIUtils.ShowMessage( "Attempting to destroy a output node" );
			//}
			else
			{
				TemplateMultiPassMasterNode templateMasterNode = node as TemplateMultiPassMasterNode;
				if( templateMasterNode != null && templateMasterNode.InvalidNode )
				{
					DestroyNode( node, false, true );
					return;
				}

				DeselectNode( node );
				UIUtils.ShowMessage( "Attempting to destroy a master node" );
			}
		}

		void AddToSelectedNodes( ParentNode node )
		{
			node.Selected = true;
			m_selectedNodes.Add( node );
			node.OnNodeStoppedMovingEvent += OnNodeFinishMoving;
			if( node.ConnStatus == NodeConnectionStatus.Connected )
			{
				HighlightWiresStartingNode( node );
			}
		}

		void RemoveFromSelectedNodes( ParentNode node )
		{
			node.Selected = false;
			m_selectedNodes.Remove( node );
			node.OnNodeStoppedMovingEvent -= OnNodeFinishMoving;
		}

		public void SelectNode( ParentNode node, bool append, bool reorder )
		{
			if( node == null )
				return;

			if( append )
			{
				if( !m_selectedNodes.Contains( node ) )
				{
					AddToSelectedNodes( node );
				}
			}
			else
			{
				DeSelectAll();
				AddToSelectedNodes( node );
			}
			if( reorder && !node.ReorderLocked )
			{
				m_nodes.Remove( node );
				m_nodes.Add( node );
				m_markToReOrder = true;
			}
		}

		public void MultipleSelection( Rect selectionArea, bool appendSelection = true )
		{
			if( !appendSelection )
			{
				for( int i = 0; i < m_nodes.Count; i++ )
				{
					if( selectionArea.Overlaps( m_nodes[ i ].Position, true ) )
					{
						RemoveFromSelectedNodes( m_nodes[ i ] );
					}
				}

				m_markedToDeSelect = false;
				ResetHighlightedWires();
			}
			else
			{
				for( int i = 0; i < m_nodes.Count; i++ )
				{
					if( !m_nodes[ i ].Selected && selectionArea.Overlaps( m_nodes[ i ].Position, true ) )
					{
						AddToSelectedNodes( m_nodes[ i ] );
					}
				}
			}

			// reorder nodes and highlight them
			for( int i = 0; i < m_selectedNodes.Count; i++ )
			{
				if( !m_selectedNodes[ i ].ReorderLocked )
				{
					m_nodes.Remove( m_selectedNodes[ i ] );
					m_nodes.Add( m_selectedNodes[ i ] );
					m_markToReOrder = true;
					if( m_selectedNodes[ i ].ConnStatus == NodeConnectionStatus.Connected )
					{
						HighlightWiresStartingNode( m_selectedNodes[ i ] );
					}
				}
			}
		}

		public void SelectAll()
		{
			for( int i = 0; i < m_nodes.Count; i++ )
			{
				if( !m_nodes[ i ].Selected )
					AddToSelectedNodes( m_nodes[ i ] );
			}
		}

		public void SelectMasterNode()
		{
			if( m_masterNodeId != Constants.INVALID_NODE_ID )
			{
				SelectNode( CurrentMasterNode, false, false );
			}
		}

		public void SelectOutputNode()
		{
			if( m_masterNodeId != Constants.INVALID_NODE_ID )
			{
				SelectNode( CurrentOutputNode, false, false );
			}
		}

		public void DeselectNode( int nodeId )
		{
			ParentNode node = GetNode( nodeId );
			if( node )
			{
				m_selectedNodes.Remove( node );
				node.Selected = false;
			}
		}

		public void DeselectNode( ParentNode node )
		{
			m_selectedNodes.Remove( node );
			node.Selected = false;
			PropagateHighlightDeselection( node );
		}



		public void DeSelectAll()
		{
			m_markedToDeSelect = false;
			for( int i = 0; i < m_selectedNodes.Count; i++ )
			{
				m_selectedNodes[ i ].Selected = false;
				m_selectedNodes[ i ].OnNodeStoppedMovingEvent -= OnNodeFinishMoving;
			}
			m_selectedNodes.Clear();
			ResetHighlightedWires();
		}

		public void AssignMasterNode()
		{
			if( m_selectedNodes.Count == 1 )
			{
				OutputNode newOutputNode = m_selectedNodes[ 0 ] as OutputNode;
				MasterNode newMasterNode = newOutputNode as MasterNode;
				if( newOutputNode != null )
				{
					if( m_masterNodeId != Constants.INVALID_NODE_ID && m_masterNodeId != newOutputNode.UniqueId )
					{
						OutputNode oldOutputNode = GetNode( m_masterNodeId ) as OutputNode;
						MasterNode oldMasterNode = oldOutputNode as MasterNode;
						if( oldOutputNode != null )
						{
							oldOutputNode.IsMainOutputNode = false;
							if( oldMasterNode != null )
							{
								oldMasterNode.ClearUpdateEvents();
							}
						}
					}
					m_masterNodeId = newOutputNode.UniqueId;
					newOutputNode.IsMainOutputNode = true;
					if( newMasterNode != null )
					{
						newMasterNode.OnMaterialUpdatedEvent += OnMaterialUpdatedEvent;
						newMasterNode.OnShaderUpdatedEvent += OnShaderUpdatedEvent;
					}
				}
			}

			IsDirty = true;
		}

		public void AssignMasterNode( OutputNode node, bool onlyUpdateGraphId )
		{
			AssignMasterNode( node.UniqueId, onlyUpdateGraphId );
			MasterNode masterNode = node as MasterNode;
			if( masterNode != null )
			{
				masterNode.OnMaterialUpdatedEvent += OnMaterialUpdatedEvent;
				masterNode.OnShaderUpdatedEvent += OnShaderUpdatedEvent;
			}
		}

		public void AssignMasterNode( int nodeId, bool onlyUpdateGraphId )
		{
			if( nodeId < 0 || m_masterNodeId == nodeId )
				return;

			if( m_masterNodeId > Constants.INVALID_NODE_ID )
			{
				OutputNode oldOutputNode = ( GetNode( nodeId ) as OutputNode );
				MasterNode oldMasterNode = oldOutputNode as MasterNode;
				if( oldOutputNode != null )
				{
					oldOutputNode.IsMainOutputNode = false;
					if( oldMasterNode != null )
					{
						oldMasterNode.ClearUpdateEvents();
					}
				}
			}

			if( onlyUpdateGraphId )
			{
				m_masterNodeId = nodeId;
			}
			else
			{
				OutputNode outputNode = ( GetNode( nodeId ) as OutputNode );
				if( outputNode != null )
				{
					outputNode.IsMainOutputNode = true;
					m_masterNodeId = nodeId;
				}
			}

			IsDirty = true;
		}

		public void RefreshOnUndo()
		{
			if( m_nodes != null )
			{
				int count = m_nodes.Count;
				for( int i = 0; i < count; i++ )
				{
					if( m_nodes[ i ] != null )
					{
						m_nodes[ i ].RefreshOnUndo();
					}
				}
			}
		}

		public void DrawGrid( DrawInfo drawInfo )
		{
			m_nodeGrid.DrawGrid( drawInfo );
		}

		public float MaxNodeDist
		{
			get { return m_nodeGrid.MaxNodeDist; }
		}

		public List<ParentNode> GetNodesInGrid( Vector2 transformedMousePos )
		{
			return m_nodeGrid.GetNodesOn( transformedMousePos );
		}

		public void FireMasterNode( Shader selectedShader )
		{
			( GetNode( m_masterNodeId ) as MasterNode ).Execute( selectedShader );
		}

		public Shader FireMasterNode( string pathname, bool isFullPath )
		{
			return ( GetNode( m_masterNodeId ) as MasterNode ).Execute( pathname, isFullPath );
		}

		private void ForceSignalPropagationOnMasterNodeInternal( UsageListTemplateMultiPassMasterNodes masterNodes )
		{
			int mpCount = masterNodes.Count;
			for( int i = 0; i < mpCount; i++ )
			{
				masterNodes.NodesList[ i ].GenerateSignalPropagation();
			}
		}

		public void ForceSignalPropagationOnMasterNode()
		{
			if( m_multiPassMasterNodes.Count > 0 )
			{
				ForceSignalPropagationOnMasterNodeInternal( m_multiPassMasterNodes );
				for( int i = 0; i < m_lodMultiPassMasterNodes.Count; i++ )
				{
					ForceSignalPropagationOnMasterNodeInternal( m_lodMultiPassMasterNodes[ i ] );
				}
			}
			else if( CurrentOutputNode != null )
				CurrentOutputNode.GenerateSignalPropagation();

			List<FunctionOutput> allOutputs = m_functionOutputNodes.NodesList;
			for( int i = 0; i < allOutputs.Count; i++ )
			{
				allOutputs[ i ].GenerateSignalPropagation();
			}

			//List<RegisterLocalVarNode> localVarNodes = m_localVarNodes.NodesList;
			//int count = localVarNodes.Count;
			//for( int i = 0; i < count; i++ )
			//{
			//	localVarNodes[ i ].GenerateSignalPropagation();
			//}
		}

		public void UpdateShaderOnMasterNode( Shader newShader )
		{
			MasterNode mainMasterNode = ( GetNode( m_masterNodeId ) as MasterNode );
			if( mainMasterNode == null )
			{
				Debug.LogError( "No Master Node was detected. Aborting update!" );
				return;
			}
			mainMasterNode.UpdateFromShader( newShader );

			if( HasLODs )
			{
				int passIdx = ( (TemplateMultiPassMasterNode)mainMasterNode ).PassIdx;
				for( int i = 0; i < m_lodMultiPassMasterNodes.Count; i++ )
				{
					if( m_lodMultiPassMasterNodes.Count != 0 && m_lodMultiPassMasterNodes[ i ].NodesList.Count > 0 )
					{
						if( m_lodMultiPassMasterNodes[ i ].NodesList[ passIdx ] != null )
						{
							m_lodMultiPassMasterNodes[ i ].NodesList[ passIdx ].UpdateFromShader( newShader );
						}
						else
						{
							Debug.LogError( "Null master node detected. Aborting update!" );
							return;
						}
					}
					else break;
				}
			}
		}

		public void CopyValuesFromMaterial( Material material )
		{
			Material currMaterial = CurrentMaterial;
			if( currMaterial == material )
			{
				for( int i = 0; i < m_nodes.Count; i++ )
				{
					m_nodes[ i ].ForceUpdateFromMaterial( material );
				}
			}
		}

		public void UpdateMaterialOnMasterNode( Material material )
		{
			MasterNode mainMasterNode = ( GetNode( m_masterNodeId ) as MasterNode );
			mainMasterNode.UpdateMasterNodeMaterial( material );
			if( HasLODs )
			{
				int passIdx = ( (TemplateMultiPassMasterNode)mainMasterNode ).PassIdx;
				for( int i = 0; i < m_lodMultiPassMasterNodes.Count; i++ )
				{
					if( m_lodMultiPassMasterNodes.Count != 0 && m_lodMultiPassMasterNodes[ i ].NodesList.Count > 0 )
					{
						m_lodMultiPassMasterNodes[ i ].NodesList[ passIdx ].UpdateMasterNodeMaterial( material );
					}
					else break;
				}
			}
		}

		public void UpdateMaterialOnPropertyNodes( Material material )
		{
			int propertyCount = m_propertyNodes.Count;
			for(int i = 0;i< propertyCount;i++ )
			{
				m_propertyNodes.NodesList[i].UpdateMaterial( material );
			}
		}

		public void SetMaterialModeOnGraph( Material mat, bool fetchMaterialValues = true )
		{
			for( int i = 0; i < m_nodes.Count; i++ )
			{
				m_nodes[ i ].SetMaterialMode( mat, fetchMaterialValues );
			}
		}

		public ParentNode CheckNodeAt( Vector3 pos, bool checkForRMBIgnore = false )
		{
			ParentNode selectedNode = null;

			// this is checked on the inverse order to give priority to nodes that are drawn on top  ( last on the list )
			for( int i = m_nodes.Count - 1; i > -1; i-- )
			{
				if( m_nodes[ i ].Contains( pos ) )
				{
					if( checkForRMBIgnore )
					{
						if( !m_nodes[ i ].RMBIgnore )
						{
							selectedNode = m_nodes[ i ];
							break;
						}
					}
					else
					{
						selectedNode = m_nodes[ i ];
						break;
					}
				}
			}
			return selectedNode;
		}

		public void ResetNodesLocalVariables()
		{
			for( int i = 0; i < m_nodes.Count; i++ )
			{
				m_nodes[ i ].Reset();
				m_nodes[ i ].ResetOutputLocals();

				FunctionNode fnode = m_nodes[ i ] as FunctionNode;
				if( fnode != null )
				{
					if( fnode.Function != null )
						fnode.FunctionGraph.ResetNodesLocalVariables();
				}
			}
		}

		public void ResetNodesLocalVariablesIfNot( MasterNodePortCategory category )
		{
			for( int i = 0; i < m_nodes.Count; i++ )
			{
				m_nodes[ i ].Reset();
				m_nodes[ i ].ResetOutputLocalsIfNot( category );

				FunctionNode fnode = m_nodes[ i ] as FunctionNode;
				if( fnode != null )
				{
					if( fnode.Function != null )
						fnode.FunctionGraph.ResetNodesLocalVariablesIfNot( category );
				}
			}
		}

		public void ResetNodesLocalVariables( ParentNode node )
		{
			if( node is GetLocalVarNode )
			{
				GetLocalVarNode localVarNode = node as GetLocalVarNode;
				if( localVarNode.CurrentSelected != null )
				{
					node = localVarNode.CurrentSelected;
				}
			}

			node.Reset();
			node.ResetOutputLocals();
			int count = node.InputPorts.Count;
			for( int i = 0; i < count; i++ )
			{
				if( node.InputPorts[ i ].IsConnected )
				{
					ResetNodesLocalVariables( m_nodesDict[ node.InputPorts[ i ].GetConnection().NodeId ] );
				}
			}
		}

		public void ResetNodesLocalVariablesIfNot( ParentNode node, MasterNodePortCategory category )
		{
			if( node is GetLocalVarNode )
			{
				GetLocalVarNode localVarNode = node as GetLocalVarNode;
				if( localVarNode.CurrentSelected != null )
				{
					node = localVarNode.CurrentSelected;
				}
			}

			node.Reset();
			node.ResetOutputLocalsIfNot( category );
			int count = node.InputPorts.Count;
			for( int i = 0; i < count; i++ )
			{
				if( node.InputPorts[ i ].IsConnected )
				{
					ResetNodesLocalVariablesIfNot( m_nodesDict[ node.InputPorts[ i ].GetConnection().NodeId ], category );
				}
			}
		}


		public override string ToString()
		{
			string dump = ( "Parent Graph \n" );
			for( int i = 0; i < m_nodes.Count; i++ )
			{
				dump += ( m_nodes[ i ] + "\n" );
			}
			return dump;
		}

		public void OrderNodesByGraphDepth()
		{
			if( CurrentMasterNode != null )
			{
				//CurrentMasterNode.SetupNodeCategories();
				int count = m_nodes.Count;
				for( int i = 0; i < count; i++ )
				{
					if( m_nodes[ i ].ConnStatus == NodeConnectionStatus.Island )
					{
						m_nodes[ i ].CalculateCustomGraphDepth();
					}
				}
			}
			else
			{
				//TODO: remove this dynamic list
				List<OutputNode> allOutputs = new List<OutputNode>();
				for( int i = 0; i < AllNodes.Count; i++ )
				{
					OutputNode temp = AllNodes[ i ] as OutputNode;
					if( temp != null )
						allOutputs.Add( temp );
				}

				for( int j = 0; j < allOutputs.Count; j++ )
				{
					allOutputs[ j ].SetupNodeCategories();
					int count = m_nodes.Count;
					for( int i = 0; i < count; i++ )
					{
						if( m_nodes[ i ].ConnStatus == NodeConnectionStatus.Island )
						{
							m_nodes[ i ].CalculateCustomGraphDepth();
						}
					}
				}
			}
			
			// @diogo: replaced Sort() with OrderBy() for stable sorting
			m_nodes.OrderBy( s => s.GraphDepth );
		}

		public void WriteToString( ref string nodesInfo, ref string connectionsInfo )
		{
			for( int i = 0; i < m_nodes.Count; i++ )
			{
				m_nodes[ i ].FullWriteToString( ref nodesInfo, ref connectionsInfo );
				IOUtils.AddLineTerminator( ref nodesInfo );
			}
		}

		public void Reset()
		{
			SaveIsDirty = false;
			IsDirty = false;
		}

		public void OnBeforeSerialize()
		{
			//DeSelectAll();
		}

		public void OnAfterDeserialize()
		{
			m_afterDeserializeFlag = true;
		}

		public void CleanCorruptedNodes()
		{
			for( int i = 0; i < m_nodes.Count; i++ )
			{
				if( (object)m_nodes[ i ] == null )
				{
					m_nodes.RemoveAt( i );
					CleanCorruptedNodes();
				}
			}
		}

		public void OnDuplicateEventWrapper()
		{
			if( OnDuplicateEvent != null )
			{
				AmplifyShaderEditorWindow temp = UIUtils.CurrentWindow;
				UIUtils.CurrentWindow = ParentWindow;
				OnDuplicateEvent();
				UIUtils.CurrentWindow = temp;
			}
		}

		public ParentNode CreateNode( AmplifyShaderFunction shaderFunction, bool registerUndo, int nodeId = -1, bool addLast = true )
		{
			FunctionNode newNode = ScriptableObject.CreateInstance<FunctionNode>();
			if( newNode )
			{
				newNode.ContainerGraph = this;
				newNode.CommonInit( shaderFunction, nodeId );
				newNode.UniqueId = nodeId;
				AddNode( newNode, nodeId < 0, addLast, registerUndo );
			}
			return newNode;
		}

		public ParentNode CreateNode( AmplifyShaderFunction shaderFunction, bool registerUndo, Vector2 pos, int nodeId = -1, bool addLast = true )
		{
			ParentNode newNode = CreateNode( shaderFunction, registerUndo, nodeId, addLast );
			if( newNode )
			{
				newNode.Vec2Position = pos;
			}
			return newNode;
		}

		public TemplateMultiPassMasterNode CreateMultipassMasterNode( int lodId, bool registerUndo, int nodeId = -1, bool addLast = true )
		{
			TemplateMultiPassMasterNode newNode = ScriptableObject.CreateInstance<TemplateMultiPassMasterNode>();
			if( newNode )
			{
				newNode.LODIndex = lodId;
				newNode.ContainerGraph = this;
				if( newNode.IsStubNode )
				{
					TemplateMultiPassMasterNode stubNode = newNode.ExecuteStubCode() as TemplateMultiPassMasterNode;
					ScriptableObject.DestroyImmediate( newNode, true );
					newNode = stubNode;
				}
				else
				{
					newNode.UniqueId = nodeId;
					AddNode( newNode, nodeId < 0, addLast, registerUndo );
				}
			}
			return newNode;
		}

		public ParentNode CreateNode( System.Type type, bool registerUndo, int nodeId = -1, bool addLast = true )
		{
			ParentNode newNode = ScriptableObject.CreateInstance( type ) as ParentNode;
			if( newNode )
			{
				newNode.ContainerGraph = this;
				if( newNode.IsStubNode )
				{
					ParentNode stubNode = newNode.ExecuteStubCode();
					ScriptableObject.DestroyImmediate( newNode, true );
					newNode = stubNode;
				}
				else
				{
					newNode.UniqueId = nodeId;
					AddNode( newNode, nodeId < 0, addLast, registerUndo );
				}
			}
			return newNode;
		}

		public ParentNode CreateNode( System.Type type, bool registerUndo, Vector2 pos, int nodeId = -1, bool addLast = true )
		{
			ParentNode newNode = CreateNode( type, registerUndo, nodeId, addLast );
			if( newNode )
			{
				newNode.Vec2Position = pos;
			}
			return newNode;
		}

		public void FireMasterNodeReplacedEvent()
		{
			MasterNode masterNode = CurrentMasterNode;
			int count = m_nodes.Count;
			for( int i = 0; i < count; i++ )
			{
				if( m_nodes[ i ].UniqueId != m_masterNodeId )
				{
					m_nodes[ i ].OnMasterNodeReplaced( masterNode );
				}
			}
		}

		//Used over shader functions to propagate signal into their graphs
		public void FireMasterNodeReplacedEvent( MasterNode masterNode )
		{
			int count = m_nodes.Count;
			for( int i = 0; i < count; i++ )
			{
				if( m_nodes[ i ].UniqueId != masterNode.UniqueId )
				{
					m_nodes[ i ].OnMasterNodeReplaced( masterNode );
				}
			}
		}


		public void CrossCheckTemplateNodes( TemplateDataParent templateData , List<TemplateMultiPassMasterNode> mpNodesList , int lodId )
		{
			/*Paulo*/
			DeSelectAll();
			TemplateMultiPassMasterNode newMasterNode = null;
			Dictionary<string, TemplateReplaceHelper> nodesDict = new Dictionary<string, TemplateReplaceHelper>();
			int mpNodeCount = mpNodesList.Count;
			for( int i = 0; i < mpNodeCount; i++ )
			{
				string masterNodeId = mpNodesList[ i ].InvalidNode ? mpNodesList[ i ].OriginalPassName + "ASEInvalidMasterNode" + i : mpNodesList[ i ].OriginalPassName;
				nodesDict.Add( masterNodeId, new TemplateReplaceHelper( mpNodesList[ i ] ) );
			}

			TemplateMultiPassMasterNode currMasterNode = GetNode( m_masterNodeId ) as TemplateMultiPassMasterNode;

			TemplateMultiPass multipassData = templateData as TemplateMultiPass;
			m_currentSRPType = multipassData.SubShaders[ 0 ].Modules.SRPType;

			bool sortTemplatesNodes = false;
			Vector2 currentPosition = currMasterNode.Vec2Position;
			for( int subShaderIdx = 0; subShaderIdx < multipassData.SubShaders.Count; subShaderIdx++ )
			{
				for( int passIdx = 0; passIdx < multipassData.SubShaders[ subShaderIdx ].Passes.Count; passIdx++ )
				{
					string currPassName = multipassData.SubShaders[ subShaderIdx ].Passes[ passIdx ].PassNameContainer.Data;
					if( nodesDict.ContainsKey( currPassName ) )
					{
						bool wasMainNode = nodesDict[ currPassName ].MasterNode.IsMainOutputNode;

						currentPosition.y += nodesDict[ currPassName ].MasterNode.Position.height + 10;
						nodesDict[ currPassName ].Used = true;
						nodesDict[ currPassName ].MasterNode.SetTemplate( multipassData, false, false, subShaderIdx, passIdx, SetTemplateSource.NewShader );
						if( wasMainNode && !nodesDict[ currPassName ].MasterNode.IsMainOutputNode )
						{
							nodesDict[ currPassName ].MasterNode.ReleaseResources();
						}
						else if( !wasMainNode && nodesDict[ currPassName ].MasterNode.IsMainOutputNode )
						{
							newMasterNode = nodesDict[ currPassName ].MasterNode;
						}
					}
					else
					{
						sortTemplatesNodes = true;
						TemplateMultiPassMasterNode masterNode = CreateMultipassMasterNode( lodId, false );
						if( multipassData.SubShaders[ subShaderIdx ].Passes[ passIdx ].IsMainPass )
						{
							newMasterNode = masterNode;
							currMasterNode.ReleaseResources();
						}
						masterNode.Vec2Position = currentPosition;
						masterNode.SetTemplate( multipassData, true, true, subShaderIdx, passIdx, SetTemplateSource.NewShader );
						//currentPosition.y += masterNode.HeightEstimate + 10;
					}
				}
			}

			foreach( KeyValuePair<string, TemplateReplaceHelper> kvp in nodesDict )
			{
				if( !kvp.Value.Used )
					DestroyNode( kvp.Value.MasterNode, false, true );
			}
			nodesDict.Clear();

			if( newMasterNode != null )
			{
				if( lodId == -1 )
				{
					m_masterNodeId = newMasterNode.UniqueId;
				}
				newMasterNode.OnMaterialUpdatedEvent += OnMaterialUpdatedEvent;
				newMasterNode.OnShaderUpdatedEvent += OnShaderUpdatedEvent;
				newMasterNode.IsMainOutputNode = true;
			}

			if( sortTemplatesNodes )
			{
				mpNodesList.Sort( ( x, y ) => ( x.PassIdx.CompareTo( y.PassIdx ) ) );
			}
		}

		public void OnRefreshLinkedPortsComplete()
		{
			OnRefreshLinkedPortsCompleteInternal( m_multiPassMasterNodes );
			for( int i = 0; i < m_lodMultiPassMasterNodes.Count; i++ )
			{
				OnRefreshLinkedPortsCompleteInternal( m_lodMultiPassMasterNodes[ i ] );
			}
		}

		private void OnRefreshLinkedPortsCompleteInternal( UsageListTemplateMultiPassMasterNodes masterNodes )
		{
			int mpCount = masterNodes.Count;
			for( int i = 0; i < mpCount; i++ )
			{
				masterNodes.NodesList[ i ].OnRefreshLinkedPortsComplete();
			}
		}

		public void RefreshLinkedMasterNodes( bool optionsUpdate = false )
		{
			if( DebugConsoleWindow.DeveloperMode )
				Debug.Log( "Refresh linked master nodes" );

			RefreshLinkedMasterNodesInternal( m_multiPassMasterNodes, optionsUpdate );
			for( int i = 0; i < m_lodMultiPassMasterNodes.Count; i++ )
			{
				RefreshLinkedMasterNodesInternal( m_lodMultiPassMasterNodes[i], optionsUpdate );
			}
		}

		private void RefreshLinkedMasterNodesInternal( UsageListTemplateMultiPassMasterNodes masterNodes, bool optionsUpdate )
		{
			int mpCount = masterNodes.Count;
			if( mpCount > 1 )
			{
				Dictionary<string, List<InputPort>> registeredLinks = new Dictionary<string, List<InputPort>>();
				for( int i = 0; i < mpCount; i++ )
				{
					CheckLinkedPorts( ref registeredLinks, masterNodes.NodesList[ mpCount - 1 - i ] );
				}

				foreach( KeyValuePair<string, List<InputPort>> kvp in registeredLinks )
				{
					int linkCount = kvp.Value.Count;
					if( linkCount == 1 )
					{
						kvp.Value[ 0 ].Visible = true;
					}
					else
					{
						kvp.Value[ 0 ].Visible = true;
						for( int i = 1; i < linkCount; i++ )
						{
							kvp.Value[ i ].SetExternalLink( kvp.Value[ 0 ].NodeId, kvp.Value[ 0 ].PortId );
							kvp.Value[ i ].Visible = false;
						}
					}
					kvp.Value.Clear();
				}
				registeredLinks.Clear();
				registeredLinks = null;
			}

			masterNodes.NodesList.Sort( ( x, y ) => ( x.SubShaderIdx * 1000 + x.PassIdx ).CompareTo( y.SubShaderIdx * 1000 + y.PassIdx ) );
			masterNodes.UpdateNodeArr();

			m_parentWindow.TemplatesManagerInstance.ResetOptionsSetupData();
			for( int i = 0; i < mpCount; i++ )
			{
				int visiblePorts = 0;
				for( int j = 0; j < masterNodes.NodesList[ i ].InputPorts.Count; j++ )
				{
					if( masterNodes.NodesList[ i ].InputPorts[ j ].Visible )
					{
						visiblePorts++;
					}
				}

				if( masterNodes.NodesList[ i ].VisiblePorts != visiblePorts )
				{
					masterNodes.NodesList[ i ].VisiblePorts = visiblePorts;
					ForceRepositionCheck = true;
				}

				masterNodes.NodesList[ i ].Docking = visiblePorts <= 0;
				if( optionsUpdate )
				{
					masterNodes.NodesList[ i ].ForceOptionsRefresh();
				}
			}
		}

		void CheckLinkedPorts( ref Dictionary<string, List<InputPort>> registeredLinks, TemplateMultiPassMasterNode masterNode )
		{
			if( masterNode.HasLinkPorts )
			{
				int inputCount = masterNode.InputPorts.Count;
				for( int i = 0; i < inputCount; i++ )
				{
					if( !string.IsNullOrEmpty( masterNode.InputPorts[ i ].ExternalLinkId ) )
					{
						string linkId = masterNode.InputPorts[ i ].ExternalLinkId;
						if( !registeredLinks.ContainsKey( masterNode.InputPorts[ i ].ExternalLinkId ) )
						{
							registeredLinks.Add( linkId, new List<InputPort>() );
						}

						if( masterNode.IsMainOutputNode )
						{
							registeredLinks[ linkId ].Insert( 0, masterNode.InputPorts[ i ] );
						}
						else
						{
							registeredLinks[ linkId ].Add( masterNode.InputPorts[ i ] );
						}
					}
					else
					{
						masterNode.InputPorts[ i ].Visible = true;
					}
				}
			}
			else
			{
				int inputCount = masterNode.InputPorts.Count;
				for( int i = 0; i < inputCount; i++ )
				{
					masterNode.InputPorts[ i ].Visible = true;
				}
			}
			
		}

		public MasterNode ReplaceMasterNode( AvailableShaderTypes newType, bool writeDefaultData = false, TemplateDataParent templateData = null )
		{
			DeSelectAll();
			ResetNodeConnStatus();
			MasterNode newMasterNode = null;
			List<TemplateMultiPassMasterNode> nodesToDelete = null;
			int mpNodeCount = m_multiPassMasterNodes.NodesList.Count;
			if( mpNodeCount > 0 )
			{
				nodesToDelete = new List<TemplateMultiPassMasterNode>();
				for( int i = 0; i < mpNodeCount; i++ )
				{
					if( m_multiPassMasterNodes.NodesList[ i ].UniqueId != m_masterNodeId )
					{
						nodesToDelete.Add( m_multiPassMasterNodes.NodesList[ i ] );
					}
				}

				for( int lod = 0; lod < m_lodMultiPassMasterNodes.Count; lod++ )
				{
					int lodNodeCount = m_lodMultiPassMasterNodes[ lod ].Count;
					for( int i = 0; i < lodNodeCount; i++ )
					{
						nodesToDelete.Add( m_lodMultiPassMasterNodes[ lod ].NodesList[ i ] );
					}
				}
			}

			MasterNode currMasterNode = GetNode( m_masterNodeId ) as MasterNode;
			if( currMasterNode != null )
			{
				currMasterNode.ReleaseResources();
			}

			bool refreshLinkedMasterNodes = false;
			switch( newType )
			{
				default:
				case AvailableShaderTypes.SurfaceShader:
				{
					CurrentCanvasMode = NodeAvailability.SurfaceShader;
					m_currentSRPType = TemplateSRPType.BiRP;
					newMasterNode = CreateNode( typeof( StandardSurfaceOutputNode ), false ) as MasterNode;
				}
				break;
				case AvailableShaderTypes.Template:
				{
					CurrentCanvasMode = NodeAvailability.TemplateShader;
					if( templateData.TemplateType == TemplateDataType.LegacySinglePass )
					{
						newMasterNode = CreateNode( typeof( TemplateMasterNode ), false ) as MasterNode;
						( newMasterNode as TemplateMasterNode ).SetTemplate( templateData as TemplateData, writeDefaultData, false );
						m_currentSRPType = TemplateSRPType.BiRP;
					}
					else
					{
						/*Paulo*/
						TemplateMultiPass multipassData = templateData as TemplateMultiPass;
						m_currentSRPType = multipassData.SubShaders[ 0 ].Modules.SRPType;

						Vector2 currentPosition = currMasterNode.Vec2Position;

						for( int subShaderIdx = 0; subShaderIdx < multipassData.SubShaders.Count; subShaderIdx++ )
						{
							for( int passIdx = 0; passIdx < multipassData.SubShaders[ subShaderIdx ].Passes.Count; passIdx++ )
							{
								TemplateMultiPassMasterNode masterNode = CreateNode( typeof( TemplateMultiPassMasterNode ), false ) as TemplateMultiPassMasterNode;
								if( multipassData.SubShaders[ subShaderIdx ].Passes[ passIdx ].IsMainPass )
								{
									newMasterNode = masterNode;
									ParentWindow.IsShaderFunctionWindow = false;
									CurrentCanvasMode = NodeAvailability.TemplateShader;
								}
								masterNode.Vec2Position = currentPosition;
								masterNode.SetTemplate( multipassData, true, true, subShaderIdx, passIdx, SetTemplateSource.NewShader );
								//currentPosition.y += masterNode.HeightEstimate + 10;
							}
						}
						refreshLinkedMasterNodes = true;
						//RefreshLinkedMasterNodes();
					}
				}
				break;
			}

			if( currMasterNode != null )
			{
				newMasterNode.CopyFrom( currMasterNode );
				m_masterNodeId = -1;
				DestroyNode( currMasterNode, false, true );
			}

			if( nodesToDelete != null )
			{
				for( int i = 0; i < nodesToDelete.Count; i++ )
				{
					DestroyNode( nodesToDelete[ i ], false, true );
				}
				nodesToDelete.Clear();
			}

			m_masterNodeId = newMasterNode.UniqueId;

			if( refreshLinkedMasterNodes )
				RefreshLinkedMasterNodes( true );

			newMasterNode.OnMaterialUpdatedEvent += OnMaterialUpdatedEvent;
			newMasterNode.OnShaderUpdatedEvent += OnShaderUpdatedEvent;
			newMasterNode.IsMainOutputNode = true;
			OnRefreshLinkedPortsComplete();
			FullCleanUndoStack();
			return newMasterNode;
		}

		private void RepositionTemplateNodes( MasterNode newMasterNode )
		{
			m_forceRepositionCheck = false;

			int dockedElementsBefore = 0;
			int dockedElementsAfter = 0;
			int masterIndex = 0;
			bool foundMaster = false;
			for( int i = 0; i < MultiPassMasterNodes.Count; i++ )
			{
				if( MultiPassMasterNodes.NodesList[ i ].UniqueId == m_masterNodeId )
				{
					foundMaster = true;
					masterIndex = i;
				}

				if( !MultiPassMasterNodes.NodesList[ i ].IsInvisible && MultiPassMasterNodes.NodesList[ i ].Docking )
				{
					if( foundMaster )
						dockedElementsAfter++;
					else
						dockedElementsBefore++;
				}
			}

			if( dockedElementsBefore > 0 )
			{
				newMasterNode.UseSquareNodeTitle = true;
			}

			for( int i = masterIndex - 1; i >= 0; i-- )
			{
				float forwardTracking = 0;
				for( int j = i + 1; j <= masterIndex; j++ )
				{
					if( !MultiPassMasterNodes.NodesList[ i ].IsInvisible && !MultiPassMasterNodes.NodesList[ j ].Docking )
					{
						forwardTracking += MultiPassMasterNodes.NodesList[ j ].HeightEstimate + 10;
					}
				}
				MasterNode node = MultiPassMasterNodes.NodesList[ i ];
				node.Vec2Position = new Vector2( node.Vec2Position.x, newMasterNode.Position.y - forwardTracking - 33 * ( dockedElementsBefore ) );
			}

			for( int i = masterIndex + 1; i < MultiPassMasterNodes.Count; i++ )
			{
				if( MultiPassMasterNodes.NodesList[ i ].UniqueId == newMasterNode.UniqueId || MultiPassMasterNodes.NodesList[ i ].Docking )
					continue;

				float backTracking = 0;
				for( int j = i - 1; j >= masterIndex; j-- )
				{
					if( !MultiPassMasterNodes.NodesList[ i ].IsInvisible && !MultiPassMasterNodes.NodesList[ j ].Docking )
					{
						backTracking += MultiPassMasterNodes.NodesList[ j ].HeightEstimate + 10;
					}
				}
				MasterNode node = MultiPassMasterNodes.NodesList[ i ];
				node.Vec2Position = new Vector2( node.Vec2Position.x, newMasterNode.Position.y + backTracking + 33 * ( dockedElementsAfter ) );
			}
		}

		public void CreateNewEmpty( string name )
		{
			CleanNodes();
			if( m_masterNodeDefaultType == null )
				m_masterNodeDefaultType = typeof( StandardSurfaceOutputNode );

			MasterNode newMasterNode = CreateNode( m_masterNodeDefaultType, false ) as MasterNode;
			newMasterNode.SetName( name );
			m_masterNodeId = newMasterNode.UniqueId;

			ParentWindow.IsShaderFunctionWindow = false;
			CurrentCanvasMode = NodeAvailability.SurfaceShader;

			newMasterNode.OnMaterialUpdatedEvent += OnMaterialUpdatedEvent;
			newMasterNode.OnShaderUpdatedEvent += OnShaderUpdatedEvent;
			newMasterNode.IsMainOutputNode = true;
			LoadedShaderVersion = VersionInfo.FullNumber;
		}

		public void CreateNewEmptyTemplate( string templateGUID )
		{
			CleanNodes();
			TemplateDataParent templateData = m_parentWindow.TemplatesManagerInstance.GetTemplate( templateGUID );
			if( templateData.TemplateType == TemplateDataType.LegacySinglePass )
			{
				TemplateMasterNode newMasterNode = CreateNode( typeof( TemplateMasterNode ), false ) as TemplateMasterNode;
				m_masterNodeId = newMasterNode.UniqueId;

				ParentWindow.IsShaderFunctionWindow = false;
				CurrentCanvasMode = NodeAvailability.TemplateShader;
				m_currentSRPType = TemplateSRPType.BiRP;
				newMasterNode.OnMaterialUpdatedEvent += OnMaterialUpdatedEvent;
				newMasterNode.OnShaderUpdatedEvent += OnShaderUpdatedEvent;
				newMasterNode.IsMainOutputNode = true;

				newMasterNode.SetTemplate( templateData as TemplateData, true, true );
			}
			else
			{
				/*Paulo*/
				TemplateMultiPass multipassData = templateData as TemplateMultiPass;
				m_currentSRPType = multipassData.SubShaders[ 0 ].Modules.SRPType;

				Vector2 currentPosition = Vector2.zero;
				for( int subShaderIdx = 0; subShaderIdx < multipassData.SubShaders.Count; subShaderIdx++ )
				{
					for( int passIdx = 0; passIdx < multipassData.SubShaders[ subShaderIdx ].Passes.Count; passIdx++ )
					{
						TemplateMultiPassMasterNode newMasterNode = CreateNode( typeof( TemplateMultiPassMasterNode ), false ) as TemplateMultiPassMasterNode;
						if( multipassData.SubShaders[ subShaderIdx ].Passes[ passIdx ].IsMainPass )
						{
							m_masterNodeId = newMasterNode.UniqueId;

							ParentWindow.IsShaderFunctionWindow = false;
							CurrentCanvasMode = NodeAvailability.TemplateShader;

							newMasterNode.OnMaterialUpdatedEvent += OnMaterialUpdatedEvent;
							newMasterNode.OnShaderUpdatedEvent += OnShaderUpdatedEvent;
							newMasterNode.IsMainOutputNode = true;
						}
						newMasterNode.Vec2Position = currentPosition;
						newMasterNode.SetTemplate( multipassData, true, true, subShaderIdx, passIdx, SetTemplateSource.NewShader );

						//currentPosition.y += newMasterNode.HeightEstimate + 10;
					}
				}
				
				RefreshLinkedMasterNodes( false );
				OnRefreshLinkedPortsComplete();
			}

			LoadedShaderVersion = VersionInfo.FullNumber;
		}

		public void CreateNewEmptyFunction( AmplifyShaderFunction shaderFunction )
		{
			CleanNodes();
			FunctionOutput newOutputNode = CreateNode( typeof( FunctionOutput ), false ) as FunctionOutput;
			m_masterNodeId = newOutputNode.UniqueId;

			ParentWindow.IsShaderFunctionWindow = true;
			CurrentCanvasMode = NodeAvailability.ShaderFunction;

			newOutputNode.IsMainOutputNode = true;
		}

		public void ForceCategoryRefresh() { m_forceCategoryRefresh = true; }
		public void RefreshExternalReferences()
		{
			int count = m_nodes.Count;
			for( int i = 0; i < count; i++ )
			{
				m_nodes[ i ].RefreshExternalReferences();
			}
		}

		public Vector2 SelectedNodesCentroid
		{
			get
			{
				if( m_selectedNodes.Count == 0 )
					return Vector2.zero;
				Vector2 pos = new Vector2( 0, 0 );
				for( int i = 0; i < m_selectedNodes.Count; i++ )
				{
					pos += m_selectedNodes[ i ].Vec2Position;
				}

				pos /= m_selectedNodes.Count;
				return pos;
			}
		}

		public void AddVirtualTextureCount()
		{
			m_virtualTextureCount += 1;
		}

		public void RemoveVirtualTextureCount()
		{
			m_virtualTextureCount -= 1;
			if( m_virtualTextureCount < 0 )
			{
				Debug.LogWarning( "Invalid virtual texture count" );
			}
		}

		public bool HasVirtualTexture { get { return m_virtualTextureCount > 0; } }

		public void AddInstancePropertyCount()
		{
			m_instancePropertyCount += 1;
//			Debug.Log( "AddInstancePropertyCount "+this.GetInstanceID() + " " + m_instancePropertyCount );
		}

		public void RemoveInstancePropertyCount()
		{
			m_instancePropertyCount -= 1;
	//		Debug.Log( "RemoveInstancePropertyCount " + this.GetInstanceID() + " " + m_instancePropertyCount );

			if( m_instancePropertyCount < 0 )
			{
				Debug.LogWarning( "Invalid property instance count" );
			}
		}

		public int InstancePropertyCount { get { return m_instancePropertyCount; } set { m_instancePropertyCount = value; } }

		public bool IsInstancedShader { get { return m_instancePropertyCount > 0; } }

		public void AddNormalDependentCount() { m_normalDependentCount += 1; }

		public void RemoveNormalDependentCount()
		{
			m_normalDependentCount -= 1;
			if( m_normalDependentCount < 0 )
			{
				Debug.LogWarning( "Invalid normal dependentCount count" );
			}
		}

		public void SetModeFromMasterNode()
		{
			MasterNode masterNode = CurrentMasterNode;
			if( masterNode != null )
			{
				switch( masterNode.CurrentMasterNodeCategory )
				{
					default:
					case AvailableShaderTypes.SurfaceShader:
					{
						if( masterNode is StandardSurfaceOutputNode )
							CurrentCanvasMode = ParentWindow.CurrentNodeAvailability;
						else
							CurrentCanvasMode = NodeAvailability.SurfaceShader;
					}
					break;
					case AvailableShaderTypes.Template:
					{
						CurrentCanvasMode = NodeAvailability.TemplateShader;
					}
					break;
				}
			}
			else
			{

				CurrentCanvasMode = NodeAvailability.SurfaceShader;
			}
		}

		public void MarkToDelete( ParentNode node )
		{
			m_markedForDeletion.Add( node );
		}
		public bool IsMasterNode( ParentNode node )
		{
			return ( node.UniqueId == m_masterNodeId ) ||
					m_multiPassMasterNodes.HasNode( node.UniqueId );
		}

		public TemplateMultiPassMasterNode GetMainMasterNodeOfLOD( int lod )
		{
			if( lod == -1 )
				return CurrentMasterNode as TemplateMultiPassMasterNode;

			return m_lodMultiPassMasterNodes[ lod ].NodesList.Find( x => x.IsMainOutputNode );
		}

		public TemplateMultiPassMasterNode GetMasterNodeOfPass( string passName, int lod )
		{
			if( lod == -1 )
				return m_multiPassMasterNodes.NodesList.Find( x => x.PassName.Equals( passName ) );

			return  m_lodMultiPassMasterNodes[lod].NodesList.Find( x => x.PassName.Equals( passName ) );
		}

		public void ForceMultiPassMasterNodesRefresh()
		{
			int mainOutputId = 0;
			int count = m_multiPassMasterNodes.Count;
			for( int i = 0; i < count; i++ )
			{
				m_multiPassMasterNodes.NodesList[ i ].ForceTemplateRefresh();
				if( m_multiPassMasterNodes.NodesList[ i ].IsMainOutputNode )
					mainOutputId = i;
			}

			int lodCount = m_lodMultiPassMasterNodes.Count;
			for( int i = 0; i < lodCount; i++ )
			{
				if( m_lodMultiPassMasterNodes[ i ] != null )
				{
					count = m_lodMultiPassMasterNodes[ i ].Count;
					for( int j = 0; j < count; j++ )
					{
						m_lodMultiPassMasterNodes[ i ].NodesList[ j ].ForceTemplateRefresh();
					}
				}
			}

			m_multiPassMasterNodes.NodesList[ mainOutputId ].CheckTemplateChanges();
		}

		public void SetLateOptionsRefresh()
		{
			m_lateOptionsRefresh = true;
		}

		public void CreateLodMasterNodes( TemplateMultiPass templateMultiPass,int index, Vector2 initialPosition )
		{
			for( int lod = 0; lod < m_lodMultiPassMasterNodes.Count; lod++ )
			{
				if( m_lodMultiPassMasterNodes[ lod ].Count == 0 )
				{
					TemplateMultiPassMasterNode reference = CurrentMasterNode as TemplateMultiPassMasterNode;

					int shaderLod = -1;
					if( lod == 0 )
					{
						shaderLod = reference.ShaderLOD - MasterNodeLODIncrement;
					}
					else
					{
						//index == -2 is when user clicks on +/- buttons over the foldout UI
						if( index == -2 )
						{
							shaderLod = m_lodMultiPassMasterNodes[ lod - 1 ].NodesList[ reference.PassIdx ].ShaderLOD - MasterNodeLODIncrement;
						}
						//index == -1 is when user clicks on + button over the main lod master node
						else if( index == -1 )
						{
							int mainShaderLOD = m_lodMultiPassMasterNodes[ 0 ].NodesList[ reference.PassIdx ].ShaderLOD;
							shaderLod = ( reference.ShaderLOD + mainShaderLOD )/2;
						}
						else
						{
							if( m_lodMultiPassMasterNodes[ index ].Count > 0 )
							{
								if( m_lodMultiPassMasterNodes[ index + 1 ].Count > 0 )
								{
									shaderLod = (m_lodMultiPassMasterNodes[ index ].NodesList[ reference.PassIdx ].ShaderLOD +
												m_lodMultiPassMasterNodes[ index + 1 ].NodesList[ reference.PassIdx ].ShaderLOD )/2;
								}
								else
								{
									shaderLod = m_lodMultiPassMasterNodes[ index ].NodesList[ reference.PassIdx ].ShaderLOD - MasterNodeLODIncrement;
								}
							}
						}
					}

					int nodeId = 0;
					TemplateMultiPassMasterNode mainMasterNode = null;
					for( int subShaderIdx = 0; subShaderIdx < templateMultiPass.SubShaders.Count; subShaderIdx++ )
					{
						for( int passIdx = 0; passIdx < templateMultiPass.SubShaders[ subShaderIdx ].Passes.Count; passIdx++ )
						{
							TemplateMultiPassMasterNode masterNode = ScriptableObject.CreateInstance( typeof( TemplateMultiPassMasterNode ) ) as TemplateMultiPassMasterNode;
							masterNode.LODIndex = lod;
							masterNode.ContainerGraph = this;
							masterNode.Vec2Position = initialPosition;
							AddNode( masterNode, true );
							masterNode.SetTemplate( templateMultiPass, true, true, subShaderIdx, passIdx, SetTemplateSource.NewShader );
							masterNode.CopyOptionsFrom( m_multiPassMasterNodes.NodesList[ nodeId++ ] );
							if( masterNode.IsMainOutputNode || ( subShaderIdx == 0 && passIdx == 0 ) )
							{
								masterNode.SetShaderLODValueAndLabel( shaderLod );
								mainMasterNode = masterNode;
							}
						}
					}

					mainMasterNode.ForceOptionsRefresh();
					SortLODMasterNodes();
					if( OnLODMasterNodesAddedEvent != null )
					{
						OnLODMasterNodesAddedEvent( lod );
					}

					TemplateMultiPassMasterNode lodMainMasterNode = CurrentMasterNode as TemplateMultiPassMasterNode;
					lodMainMasterNode.SetShaderLODValueAndLabel( lodMainMasterNode.ShaderLOD );
					return;
				}
			}
		}

		public void DestroyLodMasterNodes( int index )
		{
			if( index < 0 )
			{
				for( int lod = m_lodMultiPassMasterNodes.Count - 1; lod >= 0; lod-- )
				{
					if( m_lodMultiPassMasterNodes[ lod ].Count > 0 )
					{
						while( m_lodMultiPassMasterNodes[ lod ].Count > 0 )
						{
							DestroyNode( m_lodMultiPassMasterNodes[ lod ].NodesList[ 0 ], false, true );
						}
						break;
					}
				}
			}
			else
			{
				while( m_lodMultiPassMasterNodes[ index ].Count > 0 )
				{
					DestroyNode( m_lodMultiPassMasterNodes[ index ].NodesList[ 0 ], false, true );
				}
			}
			SortLODMasterNodes();
			TemplateMultiPassMasterNode lodMainMasterNode = CurrentMasterNode as TemplateMultiPassMasterNode;
			lodMainMasterNode.SetShaderLODValueAndLabel( lodMainMasterNode.ShaderLOD );
		}

		public void SortLODMasterNodes()
		{
			int idx = (CurrentMasterNode as TemplateMultiPassMasterNode).PassIdx;
			m_lodMultiPassMasterNodes.Sort( ( x, y ) =>
			{
				if( x.Count > 0 )
				{
					if( y.Count > 0 )
					{
						return -x.NodesList[ idx ].ShaderLOD.CompareTo( y.NodesList[ idx ].ShaderLOD );
					}
					else
					{
						return -1;
					}
				}
				else
				{
					if( y.Count > 0 )
					{
						return 1;
					}
				}
				return 0;
			});
			
			for( int lodIdx = 0; lodIdx < m_lodMultiPassMasterNodes.Count; lodIdx++ )
			{
				for( int nodeIdx = 0; nodeIdx < m_lodMultiPassMasterNodes[ lodIdx ].Count; nodeIdx++ )
				{
					m_lodMultiPassMasterNodes[ lodIdx ].NodesList[ nodeIdx ].LODIndex = lodIdx;
				}	
			}
		}

		public List<TemplateMultiPassMasterNode> GetMultiPassMasterNodes( int lod )
		{
			if( lod == -1 )
				return m_multiPassMasterNodes.NodesList;

			return m_lodMultiPassMasterNodes[ lod ].NodesList;
		}

		public bool IsNormalDependent { get { return m_normalDependentCount > 0; } }

		public void MarkToDeselect() { m_markedToDeSelect = true; }
		public void MarkToSelect( int nodeId ) { m_markToSelect = nodeId; }
		public void MarkWireHighlights() { m_checkSelectedWireHighlights = true; }
		public List<ParentNode> SelectedNodes { get { return m_selectedNodes; } }
		public List<ParentNode> MarkedForDeletionNodes { get { return m_markedForDeletion; } }
		public int CurrentMasterNodeId { get { return m_masterNodeId; } set { m_masterNodeId = value; } }

		public Shader CurrentShader
		{
			get
			{
				MasterNode masterNode = GetNode( m_masterNodeId ) as MasterNode;
				if( masterNode != null )
					return masterNode.CurrentShader;
				return null;
			}
		}

		public Material CurrentMaterial
		{
			get
			{
				MasterNode masterNode = GetNode( m_masterNodeId ) as MasterNode;
				if( masterNode != null )
					return masterNode.CurrentMaterial;
				return null;
			}
		}
		


		public NodeAvailability CurrentCanvasMode { get { return m_currentCanvasMode; } set { m_currentCanvasMode = value; ParentWindow.LateRefreshAvailableNodes(); } }
		public OutputNode CurrentOutputNode { get { return GetNode( m_masterNodeId ) as OutputNode; } }
		public FunctionOutput CurrentFunctionOutput { get { return GetNode( m_masterNodeId ) as FunctionOutput; } }
		public MasterNode CurrentMasterNode { get { return GetNode( m_masterNodeId ) as MasterNode; } }
		public StandardSurfaceOutputNode CurrentStandardSurface { get { return GetNode( m_masterNodeId ) as StandardSurfaceOutputNode; } }
		public List<ParentNode> AllNodes { get { return m_nodes; } }
		public int NodeCount { get { return m_nodes.Count; } }
		//public List<ParentNode> VisibleNodes { get { return m_visibleNodes; } }

		public int NodeClicked
		{
			set { m_nodeClicked = value; }
			get { return m_nodeClicked; }
		}

		public bool IsDirty
		{
			set { m_isDirty = value && UIUtils.DirtyMask; }
			get
			{
				bool value = m_isDirty;
				m_isDirty = false;
				return value;
			}
		}

		public bool SaveIsDirty
		{
			set { m_saveIsDirty = value && UIUtils.DirtyMask; }
			get { return m_saveIsDirty; }
		}
		public int LoadedShaderVersion
		{
			get { return m_loadedShaderVersion; }
			set { m_loadedShaderVersion = value; }
		}

		public AmplifyShaderFunction CurrentShaderFunction
		{
			get { if( CurrentFunctionOutput != null ) return CurrentFunctionOutput.Function; else return null; }
			set { if( CurrentFunctionOutput != null ) CurrentFunctionOutput.Function = value; }
		}

		public bool HasUnConnectedNodes { get { return m_hasUnConnectedNodes; } }
		public UsageListSamplerNodes SamplerNodes { get { return m_samplerNodes; } }
		public UsageListFloatIntNodes FloatIntNodes { get { return m_floatNodes; } }
		public UsageListTexturePropertyNodes TexturePropertyNodes { get { return m_texturePropertyNodes; } }
		public UsageListTextureArrayNodes TextureArrayNodes { get { return m_textureArrayNodes; } }
		public UsageListPropertyNodes PropertyNodes { get { return m_propertyNodes; } }
		public UsageListPropertyNodes RawPropertyNodes { get { return m_rawPropertyNodes; } }
		public UsageListCustomExpressionsOnFunctionMode CustomExpressionOnFunctionMode { get { return m_customExpressionsOnFunctionMode; } }
		public UsageListStaticSwitchNodes StaticSwitchNodes { get { return m_staticSwitchNodes; } }
		public UsageListScreenColorNodes ScreenColorNodes { get { return m_screenColorNodes; } }
		public UsageListRegisterLocalVarNodes LocalVarNodes { get { return m_localVarNodes; } }
		public UsageListGlobalArrayNodes GlobalArrayNodes { get { return m_globalArrayNodes; } }
		public UsageListFunctionInputNodes FunctionInputNodes { get { return m_functionInputNodes; } }
		public UsageListFunctionNodes FunctionNodes { get { return m_functionNodes; } }
		public UsageListFunctionOutputNodes FunctionOutputNodes { get { return m_functionOutputNodes; } }
		public UsageListFunctionSwitchNodes FunctionSwitchNodes { get { return m_functionSwitchNodes; } }
		public UsageListFunctionSwitchCopyNodes FunctionSwitchCopyNodes { get { return m_functionSwitchCopyNodes; } }
		public UsageListTemplateMultiPassMasterNodes MultiPassMasterNodes { get { return m_multiPassMasterNodes; } set { m_multiPassMasterNodes = value; } }
		public List<UsageListTemplateMultiPassMasterNodes> LodMultiPassMasternodes { get { return m_lodMultiPassMasterNodes; } }


		public PrecisionType CurrentPrecision
		{
			get { return m_currentPrecision; }
			set { m_currentPrecision = value; }
		}

		public NodeLOD LodLevel
		{
			get { return m_lodLevel; }
		}

		public List<ParentNode> NodePreviewList { get { return m_nodePreviewList; } set { m_nodePreviewList = value; } }

		public void SetGraphId( int id )
		{
			m_graphId = id;
		}

		public int GraphId
		{
			get { return m_graphId; }
		}

		public AmplifyShaderEditorWindow ParentWindow
		{
			get { return m_parentWindow; }
			set { m_parentWindow = value; }
		}


		public bool ChangedLightingModel
		{
			get { return m_changedLightingModel; }
			set { m_changedLightingModel = value; }
		}

		public bool ForceRepositionCheck
		{
			get { return m_forceRepositionCheck; }
			set { m_forceRepositionCheck = value; }
		}

		public bool IsLoading { get { return m_isLoading; } set { m_isLoading = value; } }
		public bool IsDuplicating { get { return m_isDuplicating; } set { m_isDuplicating = value; } }
		public TemplateSRPType CurrentSRPType { get { return m_currentSRPType; }set { m_currentSRPType = value; } }
		public bool IsSRP { get { return m_currentSRPType == TemplateSRPType.URP || m_currentSRPType == TemplateSRPType.HDRP; } }
		public bool IsHDRP { get { return m_currentSRPType == TemplateSRPType.HDRP; } }
		public bool IsLWRP { get { return m_currentSRPType == TemplateSRPType.URP; } }
		public bool IsStandardSurface { get { return GetNode( m_masterNodeId ) is StandardSurfaceOutputNode; } }
		
		public bool SamplingMacros { 
			get { return m_samplingThroughMacros; }
			set { m_samplingThroughMacros = value; } 
		}
		public bool HasLODs { get { return m_lodMultiPassMasterNodes[ 0 ].Count > 0; } }
		//public bool HasLodMultiPassNodes
		//{
		//	get
		//	{
		//		for( int i = 0; i < m_lodMultiPassMasterNodes.Count; i++ )
		//		{
		//			if( m_lodMultiPassMasterNodes[ i ].Count > 0 )
		//				return true;
		//		}
		//		return false;
		//	}
		//}
	}
}
