/// <summary>
 /// The AdditionalData of a node is always populated with the query string data, this method performs this
 /// operation and ensures that special values are not inserted or that duplicate keys are not added.
 /// </summary>
 /// <param name="node"></param>
 /// <param name="queryStrings"></param>
 protected void AddQueryStringsToAdditionalData(TreeNode node, FormDataCollection queryStrings)
 {
     foreach (var q in queryStrings.Where(x => node.AdditionalData.ContainsKey(x.Key) == false))
     {
         node.AdditionalData.Add(q.Key, q.Value);
     }
 }
 /// <summary>
 /// Helper method to create tree nodes
 /// </summary>
 /// <param name="id"></param>
 /// <param name="parentId"></param>
 /// <param name="queryStrings"></param>
 /// <param name="title"></param>
 /// <param name="routePath"></param>
 /// <param name="icon"></param>
 /// <returns></returns>
 public TreeNode CreateTreeNode(string id, string parentId, FormDataCollection queryStrings, string title, string icon, string routePath)
 {
     var jsonUrl = Url.GetTreeUrl(GetType(), id, queryStrings);
     var menuUrl = Url.GetMenuUrl(GetType(), id, queryStrings);
     var node = new TreeNode(id, parentId, jsonUrl, menuUrl) { Name = title, RoutePath = routePath, Icon = icon };
     return node;
 }
 public TreeNodeRenderingEventArgs(TreeNode node, FormDataCollection queryStrings)
     : base(queryStrings)
 {
     Node = node;
 }
        /// <summary>
        /// Helper method to create a root model for a tree
        /// </summary>
        /// <returns></returns>
        protected virtual TreeNode CreateRootNode(FormDataCollection queryStrings)
        {
            var rootNodeAsString = Constants.System.Root.ToString(CultureInfo.InvariantCulture);
            var currApp = queryStrings.GetValue<string>(TreeQueryStringParameters.Application);

            var node = new TreeNode(
                rootNodeAsString,
                null, //this is a root node, there is no parent
                Url.GetTreeUrl(GetType(), rootNodeAsString, queryStrings),
                Url.GetMenuUrl(GetType(), rootNodeAsString, queryStrings))
                {
                    HasChildren = true,
                    RoutePath = currApp,
                    Name = RootNodeDisplayName
                };

            return node;
        }
        /// <summary>
        /// Converts a legacy XmlTreeNode to a new TreeNode
        /// </summary>
        /// <param name="parentId"></param>
        /// <param name="xmlTreeNode"></param>
        /// <param name="urlHelper"></param>
        /// <param name="currentSection"></param>
        /// <param name="currentQueryStrings">
        /// The current query strings for the request - this is used to append the query strings to the menu URL of the item being rendered since the menu
        /// actually belongs to this same node (request) the query strings need to exist so the menu can be rendered in some cases.
        /// </param>
        /// <param name="isRoot"></param>
        /// <returns></returns>
        internal static TreeNode ConvertFromLegacy(string parentId, XmlTreeNode xmlTreeNode, UrlHelper urlHelper, string currentSection, FormDataCollection currentQueryStrings, bool isRoot = false)
        {
            //  /umbraco/tree.aspx?rnd=d0d0ff11a1c347dabfaa0fc75effcc2a&id=1046&treeType=content&contextMenu=false&isDialog=false

            //we need to convert the node source to our legacy tree controller
            var childNodesSource = urlHelper.GetUmbracoApiService<LegacyTreeController>("GetNodes");

            var childQuery = (xmlTreeNode.Source.IsNullOrWhiteSpace() || xmlTreeNode.Source.IndexOf('?') == -1)
                ? ""
                : xmlTreeNode.Source.Substring(xmlTreeNode.Source.IndexOf('?'));

            //append the query strings
            childNodesSource = childNodesSource.AppendQueryStringToUrl(childQuery);

            //for the menu source we need to detect if this is a root node since we'll need to set the parentId and id to -1
            // for which we'll handle correctly on the server side.            
            //if there are no menu items, then this will be empty
            var menuSource = "";
            if (xmlTreeNode.Menu != null && xmlTreeNode.Menu.Any())
            {
                menuSource = urlHelper.GetUmbracoApiService<LegacyTreeController>("GetMenu");
                //these are the absolute required query strings
                var menuQueryStrings = new Dictionary<string, object>
                    {
                        {"id", (isRoot ? "-1" : xmlTreeNode.NodeID)},
                        {"treeType", xmlTreeNode.TreeType},
                        {"parentId", (isRoot ? "-1" : parentId)},
                        {"section", currentSection}
                    };
                //append the extra ones on this request
                foreach (var i in currentQueryStrings.Where(x => menuQueryStrings.Keys.Contains(x.Key) == false))
                {
                    menuQueryStrings.Add(i.Key, i.Value);
                }
                
                menuSource = menuSource.AppendQueryStringToUrl(menuQueryStrings.ToQueryString());    
            }
            

            //TODO: Might need to add stuff to additional attributes

            var node = new TreeNode(xmlTreeNode.NodeID, isRoot ? null : parentId, childNodesSource, menuSource)
            {
                HasChildren = xmlTreeNode.HasChildren,
                Icon = xmlTreeNode.Icon,
                Name = xmlTreeNode.Text,
                NodeType = xmlTreeNode.NodeType
            };
            if (isRoot)
            {
                node.AdditionalData.Add("treeAlias", xmlTreeNode.TreeType);
            }

            //This is a special case scenario, we know that content/media works based on the normal Belle routing/editing so we'll ensure we don't
            // pass in the legacy JS handler so we do it the new way, for all other trees (Currently, this is a WIP), we'll render
            // the legacy js callback,.
            var knownNonLegacyNodeTypes = new[] { "content", "contentRecycleBin", "mediaRecyleBin", "media" };
            if (knownNonLegacyNodeTypes.InvariantContains(xmlTreeNode.NodeType) == false)
            {
                node.AssignLegacyJsCallback(xmlTreeNode.Action);
            }
            return node;
        }