/// <summary>
	/// Helper to parse spare parm containing the filter key words.
	/// </summary>
	/// <param name="session">Houdini Engine session that the TOP node is in</param>
	/// <param name="topNodeID">TOP node to get spare parm from</param>
	/// <param name="nodeInfo">Previously queried TOP node info</param>
	/// <param name="nodeTags">Tag data to populate</param>
	private static void ParseHEngineData(HEU_SessionBase session, HAPI_NodeId topNodeID, ref HAPI_NodeInfo nodeInfo, ref TOPNodeTags nodeTags)
	{
	    // Turn off session logging error when querying string parm that might not be there
	    bool bLogError = session.LogErrorOverride;
	    session.LogErrorOverride = false;

	    int numStrings = nodeInfo.parmStringValueCount;
	    HAPI_StringHandle henginedatash = 0;
	    if (numStrings > 0 && session.GetParamStringValue(topNodeID, "henginedata", 0, out henginedatash))
	    {
		string henginedatastr = HEU_SessionManager.GetString(henginedatash, session);
		//Debug.Log("HEngine data: " + henginedatastr);

		if (!string.IsNullOrEmpty(henginedatastr))
		{
		    string[] tags = henginedatastr.Split(',');
		    if (tags != null && tags.Length > 0)
		    {
			foreach (string t in tags)
			{
			    if (t.Equals("show"))
			    {
				nodeTags._show = true;
			    }
			    else if (t.Equals("autoload"))
			    {
				nodeTags._autoload = true;
			    }
			}
		    }
		}
	    }

	    // Logging error back on
	    session.LogErrorOverride = bLogError;
	}
	/// <summary>
	/// Given TOP nodes from a TOP network, populate internal state from each TOP node.
	/// </summary>
	/// <param name="session">Houdini Engine session</param>
	/// <param name="topNetwork">TOP network to query TOP nodes from</param>
	/// <param name="topNodeIDs">List of TOP nodes in the TOP network</param>
	/// <param name="useHEngineData">Whether or not to use HEngine data for filtering</param>
	/// <returns>True if successfully populated data</returns>
	public static bool PopulateTOPNodes(HEU_SessionBase session, HEU_TOPNetworkData topNetwork, HAPI_NodeId[] topNodeIDs, bool useHEngineData)
	{
	    // Holds list of found TOP nodes
	    List<HEU_TOPNodeData> newNodes = new List<HEU_TOPNodeData>();

	    foreach (HAPI_NodeId topNodeID in topNodeIDs)
	    {
		// Not necessary. Blocks main thread.
		//session.CookNode(childNodeID, HEU_PluginSettings.CookTemplatedGeos);

		HAPI_NodeInfo childNodeInfo = new HAPI_NodeInfo();
		if (!session.GetNodeInfo(topNodeID, ref childNodeInfo))
		{
		    return false;
		}

		string nodeName = HEU_SessionManager.GetString(childNodeInfo.nameSH, session);
		//Debug.LogFormat("TOP Node: name={0}, type={1}", nodeName, childNodeInfo.type);

		TOPNodeTags tags = new TOPNodeTags();
		if (useHEngineData)
		{
		    ParseHEngineData(session, topNodeID, ref childNodeInfo, ref tags);

		    if (!tags._show)
		    {
			continue;
		    }
		}
		else
		{
		    tags._show = true;
		}

		HEU_TOPNodeData topNodeData = GetTOPNodeByName(nodeName, topNetwork._topNodes);
		if (topNodeData == null)
		{
		    topNodeData = new HEU_TOPNodeData();
		}
		else
		{
		    topNetwork._topNodes.Remove(topNodeData);
		}

		newNodes.Add(topNodeData);

		//topNodeData.Reset();
		topNodeData._nodeID = topNodeID;
		topNodeData._nodeName = nodeName;
		topNodeData._parentName = topNetwork._parentName + "_" + topNetwork._nodeName;
		topNodeData._tags = tags;
	    }

	    // Clear old unused TOP nodes
	    for (int i = 0; i < topNetwork._topNodes.Count; ++i)
	    {
		ClearTOPNodeWorkItemResults(topNetwork._topNodes[i]);
	    }
	    topNetwork._topNodes = newNodes;

	    // Get list of updated TOP node names
	    topNetwork._topNodeNames = new string[topNetwork._topNodes.Count];
	    for (int i = 0; i < topNetwork._topNodes.Count; ++i)
	    {
		topNetwork._topNodeNames[i] = topNetwork._topNodes[i]._nodeName;
	    }

	    return true;
	}
	/// <summary>
	/// Given TOP nodes from a TOP network, populate internal state from each TOP node.
	/// </summary>
	/// <param name="session">Houdini Engine session</param>
	/// <param name="topNetwork">TOP network to query TOP nodes from</param>
	/// <param name="topNodeIDs">List of TOP nodes in the TOP network</param>
	/// <param name="useHEngineData">Whether or not to use HEngine data for filtering</param>
	/// <returns>True if successfully populated data</returns>
	private bool PopulateTOPNodes(HEU_SessionBase session, HEU_TOPNetworkData topNetwork, HAPI_NodeId[] topNodeIDs, bool useHEngineData)
	{
	    // Holds list of found TOP nodes
	    List<HEU_TOPNodeData> newNodes = new List<HEU_TOPNodeData>();

	    foreach (HAPI_NodeId topNodeID in topNodeIDs)
	    {
		// Not necessary. Blocks main thread.
		//session.CookNode(childNodeID, HEU_PluginSettings.CookTemplatedGeos);

		HAPI_NodeInfo childNodeInfo = new HAPI_NodeInfo();
		if (!session.GetNodeInfo(topNodeID, ref childNodeInfo))
		{
		    return false;
		}

		string nodeName = HEU_SessionManager.GetString(childNodeInfo.nameSH, session);
		//HEU_Logger.LogFormat("TOP Node: name={0}, type={1}", nodeName, childNodeInfo.type);

		TOPNodeTags tags = new TOPNodeTags();
		if (useHEngineData)
		{
		    ParseHEngineData(session, topNodeID, ref childNodeInfo, ref tags);

		    if (!tags._showHEngineData)
		    {
			continue;
		    }
		}
		else
		{
		    tags._show = true;
		    tags._showHEngineData = true;
		}

		HEU_TOPNodeData topNodeData = GetTOPNodeByName(nodeName, topNetwork._topNodes);
		if (topNodeData == null)
		{
		    topNodeData = new HEU_TOPNodeData();
		}
		else
		{
		    topNetwork._topNodes.Remove(topNodeData);
		}

		newNodes.Add(topNodeData);

		//topNodeData.Reset();
		topNodeData._nodeID = topNodeID;
		topNodeData._nodeName = nodeName;
		topNodeData._parentName = topNetwork._parentName + "_" + topNetwork._nodeName;
		topNodeData._tags = tags;

		// Note: Don't have to compare with _showHEngineData because it won't exist in network if false
		if (_bUseTOPOutputFilter && _topNodeFilter != "")
		{
		    if (!nodeName.StartsWith(_topNodeFilter))
		    {
			topNodeData._tags._show = false;
		    }
		}

		if (_bUseTOPOutputFilter)
		{
		    bool bAutoLoad = false;
		    if (_topOutputFilter == "")
		    {
			bAutoLoad = true;
		    }
		    else if (nodeName.StartsWith(_topOutputFilter))
		    {
			bAutoLoad = true;
		    }

		    topNodeData._tags._autoload |= bAutoLoad;
		    topNodeData._showResults = topNodeData._tags._autoload;
		}
	    }

	    // Clear old unused TOP nodes
	    for (int i = 0; i < topNetwork._topNodes.Count; ++i)
	    {
		ClearTOPNodeWorkItemResults(topNetwork._topNodes[i]);
	    }
	    topNetwork._topNodes = newNodes;

	    // Get list of updated TOP node names
	    SetupTopNetworkNames(topNetwork);

	    return true;
	}
	/// <summary>
	/// Find all TOP networks from linked HDA, as well as the TOP nodes within, and populate internal state.
	/// </summary>
	/// <returns>True if successfully populated data</returns>
	public bool PopulateTOPNetworks()
	{
	    HEU_SessionBase session = GetHAPISession();

	    HAPI_NodeInfo assetInfo = new HAPI_NodeInfo();
	    if (!session.GetNodeInfo(_assetID, ref assetInfo, true))
	    {
		return false;
	    }

	    // Get all networks within the asset, recursively.
	    // The reason to get all networks is because there can be TOP network SOP which isn't a TOP network type, but rather a SOP type
	    int nodeCount = 0;
	    if (!session.ComposeChildNodeList(_assetID, (int)(HAPI_NodeType.HAPI_NODETYPE_ANY), (int)HAPI_NodeFlags.HAPI_NODEFLAGS_NETWORK, true, ref nodeCount))
	    {
		return false;
	    }

	    HAPI_NodeId[] nodeIDs = new HAPI_NodeId[nodeCount];
	    if (!session.GetComposedChildNodeList(_assetID, nodeIDs, nodeCount))
	    {
		return false;
	    }

	    // Holds TOP networks in use
	    List<HEU_TOPNetworkData> newNetworks = new List<HEU_TOPNetworkData>();

	    // For each network, only add those with TOP child nodes (therefore guaranteeing only TOP networks are added).
	    for (int t = 0; t < nodeCount; ++t)
	    {
		HAPI_NodeInfo topNodeInfo = new HAPI_NodeInfo();
		if (!session.GetNodeInfo(nodeIDs[t], ref topNodeInfo))
		{
		    return false;
		}

		string nodeName = HEU_SessionManager.GetString(topNodeInfo.nameSH, session);
		//Debug.LogFormat("Top node: {0} - {1}", nodeName, topNodeInfo.type);

		// Skip any non TOP or SOP networks
		if (topNodeInfo.type != HAPI_NodeType.HAPI_NODETYPE_TOP && topNodeInfo.type != HAPI_NodeType.HAPI_NODETYPE_SOP)
		{
		    continue;
		}

		// Get list of all TOP nodes within this network.
		HAPI_NodeId[] topNodeIDs = null;
		if (!HEU_SessionManager.GetComposedChildNodeList(session, nodeIDs[t], (int)(HAPI_NodeType.HAPI_NODETYPE_TOP), (int)HAPI_NodeFlags.HAPI_NODEFLAGS_TOP_NONSCHEDULER, true, out topNodeIDs))
		{
		    continue;
		}

		// Skip networks without TOP nodes
		if (topNodeIDs == null || topNodeIDs.Length == 0)
		{
		    continue;
		}

		// Get any filter tags from spare parms on TOP nodes
		TOPNodeTags tags = new TOPNodeTags();
		if (_useHEngineData)
		{
		    ParseHEngineData(session, nodeIDs[t], ref topNodeInfo, ref tags);

		    if (!tags._show)
		    {
			continue;
		    }
		}
		else
		{
		    tags._show = true;
		}

		HEU_TOPNetworkData topNetworkData = GetTOPNetworkByName(nodeName, _topNetworks);
		if (topNetworkData == null)
		{
		    topNetworkData = new HEU_TOPNetworkData();
		}
		else
		{
		    // Found previous TOP network, so remove it from old list. This makes
		    // sure to not remove it when cleaning up old nodes.
		    _topNetworks.Remove(topNetworkData);
		}

		newNetworks.Add(topNetworkData);

		topNetworkData._nodeID = nodeIDs[t];
		topNetworkData._nodeName = nodeName;
		topNetworkData._parentName = _assetName;
		topNetworkData._tags = tags;

		PopulateTOPNodes(session, topNetworkData, topNodeIDs, _useHEngineData);
	    }

	    // Clear old TOP networks and nodes
	    ClearAllTOPData();
	    _topNetworks = newNetworks;

	    // Update latest TOP network names
	    _topNetworkNames = new string[_topNetworks.Count];
	    for (int i = 0; i < _topNetworks.Count; ++i)
	    {
		_topNetworkNames[i] = _topNetworks[i]._nodeName;
	    }

	    return true;
	}
	/// <summary>
	/// Find all TOP networks from linked HDA, as well as the TOP nodes within, and populate internal state.
	/// </summary>
	/// <returns>True if successfully populated data</returns>
	public bool PopulateTOPNetworks()
	{
	    HEU_SessionBase session = GetHAPISession();

	    HAPI_NodeId[] allNetworkNodeIds = HEU_PDGSession.GetNonBypassedNetworkIds(session, _assetID);
	    if (allNetworkNodeIds == null || allNetworkNodeIds.Length == 0)
	    {
		return false;
	    }

	    // Holds TOP networks in use
	    List<HEU_TOPNetworkData> newNetworks = new List<HEU_TOPNetworkData>();

	    // Find nodes with TOP child nodes
	    foreach (HAPI_NodeId currentNodeId in allNetworkNodeIds)
	    {
		HAPI_NodeInfo topNodeInfo = new HAPI_NodeInfo();
		if (!session.GetNodeInfo(currentNodeId, ref topNodeInfo))
		{
		    return false;
		}

		string nodeName = HEU_SessionManager.GetString(topNodeInfo.nameSH, session);
		//HEU_Logger.LogFormat("Top node: {0} - {1}", nodeName, topNodeInfo.type);

		// Skip any non TOP or SOP networks
		if (topNodeInfo.type != HAPI_NodeType.HAPI_NODETYPE_TOP && topNodeInfo.type != HAPI_NodeType.HAPI_NODETYPE_SOP)
		{
		    continue;
		}

		// Get list of all TOP nodes within this network.
		HAPI_NodeId[] topNodeIDs = null;
		if (!HEU_SessionManager.GetComposedChildNodeList(session, currentNodeId, (int)(HAPI_NodeType.HAPI_NODETYPE_TOP), (int)HAPI_NodeFlags.HAPI_NODEFLAGS_TOP_NONSCHEDULER, true, out topNodeIDs))
		{
		    continue;
		}

		// Skip networks without TOP nodes
		if (topNodeIDs == null || topNodeIDs.Length == 0)
		{
		    continue;
		}

		// Get any filter tags from spare parms on TOP nodes
		TOPNodeTags tags = new TOPNodeTags();
		if (_useHEngineData)
		{
		    ParseHEngineData(session, currentNodeId, ref topNodeInfo, ref tags);

		    if (!tags._showHEngineData)
		    {
			continue;
		    }
		}
		else
		{
		    tags._show = true;
		    tags._showHEngineData = true;
		}

		HEU_TOPNetworkData topNetworkData = GetTOPNetworkByName(nodeName, _topNetworks);
		if (topNetworkData == null)
		{
		    topNetworkData = new HEU_TOPNetworkData();
		}
		else
		{
		    // Found previous TOP network, so remove it from old list. This makes
		    // sure to not remove it when cleaning up old nodes.
		    _topNetworks.Remove(topNetworkData);
		}

		newNetworks.Add(topNetworkData);

		topNetworkData._nodeID = currentNodeId;
		topNetworkData._nodeName = nodeName;
		topNetworkData._parentName = _assetName;
		topNetworkData._tags = tags;

		if (PopulateTOPNodes(session, topNetworkData, topNodeIDs, _useHEngineData))
		{
		    for (int i = 0; i < topNetworkData._topNodes.Count; i++)
		    {
			if (topNetworkData._topNodes[i]._tags._show)
			{
			    topNetworkData._selectedTOPIndex = i;
			    break;
			}
		    }
		}
	    }

	    // Clear old TOP networks and nodes
	    ClearAllTOPData();
	    _topNetworks = newNetworks;

	    // Update latest TOP network names
	    _topNetworkNames = new string[_topNetworks.Count];
	    for (int i = 0; i < _topNetworks.Count; ++i)
	    {
		_topNetworkNames[i] = _topNetworks[i]._nodeName;
	    }

	    return true;
	}