fcf.module({
  name:         "fcfControls:templates/menu.helper.js",
  dependencies: [],
  lazy:         [],
  module: function(){
    var Namespace = fcf.prepareObject(fcf, "packages/fcfControls/templates");

    class MenuHelper {

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

      nodeToTree(a_node, a_extend, a_dynamic, a_visibleLevel, a_loadingDepth, a_level){
        let self = this;
        a_level = a_level || 1;
        let extendItem   = a_extend ? this.findItem(a_extend, a_node.uri) : undefined;

        let root = {
          title:   a_node.title ? a_node.title : a_node.uri,
          uri:     a_node.uri,
          childs:  {},
          visible: a_level <= a_visibleLevel+1,
          end:     false,
          open:    a_level <= a_visibleLevel,
        };

        if (extendItem && "visible" in extendItem && extendItem.visible)
          root.visible = true;


        if (!extendItem)
          extendItem = a_extend;

        let extendChilds = extendItem ? extendItem.childs : {};
        fcf.each(extendChilds, (a_key, a_item)=>{
          a_item.visible = "visible" in a_item ? a_item.visible : true;
          a_item.open = fcf.first(a_item.childs) && fcf.first(a_item.childs).visible ? true :
                                                                                       a_level <= a_visibleLevel;
          a_item.noempty = !fcf.empty(a_item.childs)
        });

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

        fcf.each(a_node.childs, (a_key, a_child)=>{
          let subChild = extendChilds[a_key];
          let childInfo = self.nodeToTree(a_child, subChild, a_dynamic, a_visibleLevel, a_loadingDepth, a_level + 1);
          root.childs[a_key] = childInfo;
        })


        function fillProperies(a_item){
          a_item.visible = "visible" in a_item ? a_item.visible : true;
          a_item.open = a_item.open = fcf.first(a_item.childs) && fcf.first(a_item.childs).visible ? true :
                                                                                                     a_level <= a_visibleLevel;
          a_item.noempty = !fcf.empty(a_item.childs)
          fcf.each(a_item.childs, (a_key, a_chlid)=>{
            fillProperies(a_chlid);
          });
        }

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

        root.noempty = !fcf.empty(root.childs);
        root.open    = fcf.first(root.childs) ? fcf.first(root.childs).visible : false;
        root.end     = extendItem && !extendItem.end ? false : a_loadingDepth < a_level;
        return root;
      }

      async getCurrentTreeImpl(a_uri, a_root, a_dynamic, a_isStart){
        let nodeInfo;
        try {
          nodeInfo = await fcf.application.getRouter().getMenuNode(a_uri, a_dynamic ? 2 : 1);
        } catch (e) {
          return undefined;
        }

        let result = undefined;

        let curItem = {
          title:   nodeInfo.title ? nodeInfo.title : nodeInfo.uri,
          uri:     nodeInfo.uri,
          visible: true,
          childs:  {},
        };
        if (a_isStart) {
          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,
            visible: true,
            childs:  {},
          }

          if (a_dynamic){
            fcf.each(nodeInfo.childs[a_alias].childsOrder, (a_key, a_subalias)=>{
              let subchild = nodeInfo.childs[a_alias].childs[a_subalias];
              curItem.childs[a_alias].childs[a_subalias] = {
                title:   subchild.title ? subchild.title : subchild.uri,
                uri:     subchild.uri,
                visible: false,
                childs:  {},
                end:     true,
              }
            });
          }
        });

        if (nodeInfo.parent !== undefined && a_uri !== a_root){
          let parentItem = nodeInfo.parent !== undefined ? await this.getCurrentTreeImpl(nodeInfo.parent, a_root, a_dynamic, 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 getCurrentTree(a_uri, a_root, a_dynamic){
        let info = await this.getCurrentTreeImpl(a_uri, a_root, a_dynamic, true);
        if (!info)
          throw new fcf.Exception("ERROR", {error: `Can't find menu node "${a_uri}"`});
        return info.root;
      }

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

      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,
            visible:  true,
            open:     true,
            noempty:  false,

          });
        }

        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,
            visible:  a_tree.childs[key].visible,
            open:     a_tree.childs[key].open,
            noempty:  a_tree.childs[key].noempty,
            end:      !!a_tree.childs[key].end,
          });
          this.treeToList(a_tree.childs[key], false, a_level + 1, a_dst);
        }

        return a_dst;
      }


    };

    return Namespace.menuHelper = new MenuHelper();
  }
});
