fcf.module({
  name: "fcfControls:templates/menu.hooks.js",
  dependencies: [],
  module: function(){

    function findItem(a_item, a_uri){
      if (a_item.uri == a_uri)
        return a_item;
      for(let key in a_item.childs){
        let found = findItem(a_item.childs[key], a_uri);
        if (found)
          return found;
      }
    }

    function nodeToTree(a_node, a_extend){
      let root = {
        title:   a_node.title ? a_node.title : a_node.uri,
        uri:     a_node.uri,
        childs:  {},
      };

      let extendItem   = a_extend ? findItem(a_extend, root.uri) : undefined;
      if (!extendItem)
        extendItem = a_extend;
      let extendChilds = extendItem ? extendItem.childs : {};

      if (extendItem && extendItem.current)
        root.current = true;

      fcf.each(a_node.childs, (a_key, a_child)=>{
        let subChild = extendChilds[a_key];
        let childInfo = nodeToTree(a_child, subChild);
        root.childs[a_key] = childInfo;
      })

      for(let key in extendChilds) {
        if (!root.childs[key])
          root.childs[key] = extendChilds[key];
      }

      return root;
    }

    async function getCurrentTreeImpl(a_uri, a_isRoot){
      let nodeInfo;
      try {
        nodeInfo = await fcf.application.getRouter().getMenuNode(a_uri, 1);
      } catch (e) {
        return undefined;
      }

      let result = undefined;

      let curItem = {
        title:   nodeInfo.title ? nodeInfo.title : nodeInfo.uri,
        uri:     nodeInfo.uri,
        childs:  {},
      };
      if (a_isRoot) {
        curItem.current = true;
      }
      fcf.each(nodeInfo.childsOrder, (a_key, a_alias)=>{
        let chlid = nodeInfo.childs[a_alias];
        curItem.childs[a_alias] = {
          title:   chlid.title ? chlid.title : chlid.uri,
          uri:     chlid.uri,
          childs:  {},
        }
      });

      if (nodeInfo.parent !== undefined){
        let parentItem = nodeInfo.parent !== undefined ? await getCurrentTreeImpl(nodeInfo.parent, false)
                                                       : undefined;
        if (parentItem) {
          parentItem.node.childs[nodeInfo.uri] = curItem;
          result = {
            node: curItem,
            root: parentItem.root
          };
        }
      }

      if (!result) {
        result = {
          node: curItem,
          root: curItem
        };
      }

      return result;
    }

    async function getCurrentTree(a_uri){
      let info = await getCurrentTreeImpl(a_uri, true);
      if (!info)
        throw new fcf.Exception("ERROR", {error: `Can't find menu node "${a_uri}"`});
      return info.root;
    }

    function getDepthTree(a_tree, a_depth){
      let depth   = !!a_depth ? a_depth : 1;
      let result  = depth;
      fcf.each(a_tree.childs, (a_key, a_item)=>{
        result = fcf.max(result, getDepthTree(a_item, depth + 1));
      });
      return result;
    }

    function treeToList(a_tree, a_enableRootElement, a_level, a_dst){
      if (!a_level)
        a_level = 1;
      if (!a_dst)
        a_dst = [];

      if (a_enableRootElement){
        a_dst.push({
          uri:      a_tree.uri,
          title:    a_tree.title,
          current:  !!a_tree.current,
          level:    a_level-1,
        });
      }

      for(let key in a_tree.childs) {
        a_dst.push({
          uri:      a_tree.childs[key].uri,
          title:    a_tree.childs[key].title,
          current:  !!a_tree.childs[key].current,
          level:    a_level,
        });
        treeToList(a_tree.childs[key], false, a_level + 1, a_dst);
      }

      return a_dst;
    }

    return {
      /**
      * void async hookRender(a_options)
      * @brief Called when rendering from the current template
      * @param a_options
      *           object args [modify] initial parameters of template arguments
      *           object options template options
      *           string path    path to the template being rendered
      **/
      // hookRender: (a_options) => {
      // },

      //
      // void hookBeforeBuild(a_taskInfo)
      // The hook is executed before assembling the template arguments
      //
      // hookBeforeBuild: function(a_taskInfo) {
      // },

      //
      // void hookAfterBuild(a_taskInfo)
      // The hook is executed after assembling the template arguments
      //
      // hookAfterBuild: function(a_taskInfo) {
      // },

      //
      // void hookAfterBuild(a_taskInfo)
      // The hook is executed after building the template's system arguments
      //
      // hookAfterSystemBuild: function(a_taskInfo) {
      // },

      //
      // Object of hooks for programmatically populated arguments
      //
      hooksProgramableArgument: {
        //
        // @result Returns the value of an argument or a Promise object
        //
        items: async(a_taskInfo)=>{
          let current   = await getCurrentTree(a_taskInfo.args.current);
          if (!current)
            return [];
          let treeInfo  = await fcf.application.getRouter().getMenuNode(a_taskInfo.args.root, a_taskInfo.args.disclosure, 1);
          if (!treeInfo)
            return [];
          let tree      = nodeToTree(treeInfo, current);
          let items     = treeToList(tree, a_taskInfo.args.enableRoot);
          return items;
        }
      },

      //
      // Object of the hooks preprocessing of the template arguments
      //
      // hooksBeforeArgument: {
      //   // 
      //   // @result Can return the value of an argument or Promise or undefined
      //   //
      //   "ARG_NAME": function(a_taskInfo) {
      //   }
      // },

      //
      // Object of the hooks postprocessing of the template arguments
      // hooksAfterArgument: {
      //   //
      //   // @result Can return the value of an argument or Promise or undefined
      //   //
      //   "ARG_NAME": function(a_taskInfo) {
      //   }
      // },
    };
  }
});
