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

      //
      // 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) {
      // },

      hookAfterRender: async (a_taskInfo)=>{
        let items = a_taskInfo.args.items;
        fcf.each(items, (a_key, a_item)=>{
          a_item.animation = false;
        });
        await a_taskInfo.setValue("items", items);
        await a_taskInfo.setValue("innerOpen", false);
        await a_taskInfo.setValue("innerOpenItem", false);
      },

      //
      // 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)=>{
          if (a_taskInfo.args.items && (a_taskInfo.args.saveState || a_taskInfo.args.innerOpenItem) ){
            let levelOpen = false;
            let levelOpenUp = false;
            let currentIndex = false;
            fcf.each(a_taskInfo.args.items, (a_key, a_item)=>{
              a_item.current = a_taskInfo.args.current == a_item.uri;
              if (a_taskInfo.args.current == a_item.uri)
                currentIndex = a_key;

              if (a_taskInfo.args.innerOpen)
                return;

              if (a_taskInfo.args.current == a_item.uri){
                a_item.open = true;
                a_item.visible = true;
                levelOpen = a_item.level + 1;
                levelOpenUp = a_item.level;
              } else if (levelOpen === a_item.level) {
                a_item.visible = true;
              } else {
                levelOpen = false;
              }
            });
            if (!a_taskInfo.args.innerOpen && currentIndex !== false) {
              for(let i = currentIndex-1; i >= 0; --i){
                let item = a_taskInfo.args.items[i];
                if (item.level == levelOpenUp){
                  item.visible = true;
                } else if (item.level == (levelOpenUp-1)){
                  item.visible = true;
                  item.open = true;
                  --levelOpenUp;
                }
              }
            }
            if (!a_taskInfo.args.innerOpen){
              let items = false;
              if (fcf.isServer()){
                let current   = await menuHelper.getCurrentTree(a_taskInfo.args.current, a_taskInfo.args.root, true);
                if (current){
                  current = menuHelper.findItem(current, a_taskInfo.args.root);
                  let treeInfo  = await fcf.application.getRouter().getMenuNode(a_taskInfo.args.root, 0, false);
                  let tree      = menuHelper.nodeToTree(treeInfo,
                                                        current,
                                                        true,
                                                        0,
                                                        1);
                  items         = menuHelper.treeToList(tree, a_taskInfo.args.enableRoot);
                }
              } else {
                let wrapper = fcf.getWrapper(a_taskInfo.args.fcfId);
                if (wrapper)
                  items = await wrapper.send({command: "get_items", root: a_taskInfo.args.root, current: a_taskInfo.args.current, enableRoot: a_taskInfo.args.enableRoot});
              }

              if (items){
                let currentItems = a_taskInfo.args.items;
                let lastKey = 0;
                let resultItems = [];
                function findItem(a_uri, a_items, a_start) {
                  for(let i = a_start; i < a_items.length; ++i)
                    if (a_uri == a_items[i].uri)
                      return i;
                  for(let i = 0; i < a_start && i < a_items.length; ++i)
                    if (a_uri == a_items[i].uri)
                      return i;
                  return -1;
                }
                let insertPositions = [];
                let skeepLevel = false;
                for(let i = 0; i < items.length; ++i) {
                  if (skeepLevel !== false && skeepLevel < items[i].level){
                    continue;
                  } else {
                    skeepLevel = false;
                  }
                  let found = findItem(items[i].uri, currentItems, lastKey);
                  if (found === -1){
                    skeepLevel = items[i].level;
                    insertPositions.push({key: parseInt(lastKey), begin: i });
                  } else {
                    lastKey = found;
                    currentItems[found].open |= items[i].open;
                    currentItems[found].visible |= items[i].visible;
                    if (items[i].noempty)
                      currentItems[found].noempty = true;
                    if (currentItems[found].end && !items[i].end)
                      currentItems[found].end = false;
                  }
                }
                if (!fcf.empty(insertPositions)) {
                  fcf.each(insertPositions, (a_key, a_position)=>{
                    let item = items[a_position.begin];
                    a_position.last = a_position.begin;
                    for(let i = a_position.begin+1; i < items.length; ++i){
                      if (items[i].level > item.level){
                        a_position.last = i;
                      } else {
                        break;
                      }
                    }
                  });
                  let resultItems = [];
                  let lastKey = 0;
                  fcf.each(insertPositions, (a_key, a_position) => {
                    for(let i = lastKey; i <= a_position.key; ++i)
                      resultItems.push(currentItems[i]);
                    for(let i = a_position.begin; i <= a_position.last; ++i)
                      resultItems.push(items[i]);
                    lastKey = a_position.key + 1;
                  });
                  for(let i = lastKey; i < currentItems.length; ++i)
                    resultItems.push(currentItems[i]);
                  a_taskInfo.args.items = resultItems;
                }
              }
            }


            return a_taskInfo.args.items;
          }
          let loadingDepth = fcf.max(a_taskInfo.args.loadingDepth, a_taskInfo.args.disclosure);
          let current   = await menuHelper.getCurrentTree(a_taskInfo.args.current, a_taskInfo.args.root, a_taskInfo.args.dynamic);
          if (!current)
            return [];
          let treeInfo  = await fcf.application.getRouter().getMenuNode(a_taskInfo.args.root, a_taskInfo.args.dynamic ? loadingDepth + 1 : loadingDepth, false);
          if (!treeInfo)
            return [];
          let tree      = menuHelper.nodeToTree(treeInfo,
                                                current,
                                                a_taskInfo.args.dynamic,
                                                a_taskInfo.args.disclosure,
                                                a_taskInfo.args.dynamic ? loadingDepth + 1 : loadingDepth
                                              );
          let items     = menuHelper.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) {
      //   }
      // },
    };
  }
});
