fcf.module({
  name: "fcf:NRender/NDetails/TemplateProcessor.js",
  dependencies: ["fcf:NRender/NDetails/TemplateRender.js", "fcf:NRender/NDetails/Helper.js"],
  module: function(TemplateRender, helper) {
    var NDetails = fcf.prepareObject(fcf, "NRender.NDetails");

    NDetails.TemplateProcessor = function() {
      var self = this;

      /**
      * @fn void build(a_options)
      * @param object a_options
      *         - string                          template      адрес шаблона
      *         - object                          rawTemplate   объект информации о шаблоне
      *         - object                          route         объект информации о маршруте
      *         - object                          context       объект информации о маршруте
      *         - object                          args
      *         - object                          sources       объект с исходными данными аргументов шаблона
      *         - object                          aliases       Алиасы путей шаблонов
      *         - object                          state         состояние рекурсивного рендеринга
      *         - bool                            update [optional] флаг обновления повторной отрисовки на клиенте
      *         - function  onResult                            Обратный вызов результата \n
      *                  Сигнатура: void onResult(fcf::Exception a_error, a_content)
      **/
      this.build = async function(a_options) {
        var currentTemplate = fcf.getContext().currentTemplate;
        var template = a_options.template.split("+")[0];
        var templateRender = new TemplateRender({
                                    template:     a_options.template,
                                    route:        a_options.route, 
                                    args:         a_options.args, 
                                    state:        a_options.state, 
                                    rawTemplate:  a_options.rawTemplate
                                  });
        var renderingBlocks = self._getRenderingBlocks(a_options.template, a_options.rawTemplate, a_options.aliases ? a_options.aliases : {});
        var decor = typeof a_options.state.theme.getDecor() == "object" ?  a_options.state.theme.getDecor() : {};
        var aliases = typeof a_options.state.theme.getAliases() == "object" ?  a_options.state.theme.getAliases() : {};
        fcf.setContext(a_options.context);
        try {
          let libFCF = {};
          libFCF.__proto__ = fcf;
          libFCF.argRef = function(){
            if (arguments.length == 1)
              return fcf.argRef(a_options.args.fcfId, arguments[0]);
            else
              return fcf.argRef(arguments[0], arguments[1]);
          }
          await self._renderBlock(
            a_options.rawTemplate.template.template,
            {
              render: templateRender,
              args:a_options.args,
              route: a_options.route,
              decor: decor,
              fcf:   libFCF,
            },
            a_options.rawTemplate.template.path,
            a_options.rawTemplate.template.stringNumber,
            template);
        } catch (e){
          fcf.setContext(a_options.context);
          a_options.onResult(e);
          return;
        }
        fcf.setContext(a_options.context);

        let result = undefined;
        try {
          result = await templateRender.build();
        } catch (e) {
          fcf.setContext(a_options.context);
          a_options.onResult(e);
          return;
        }
        fcf.setContext(a_options.context);

        let isRoot = a_options.args.fcfId && a_options.state.root && a_options.state.root.id === a_options.args.fcfId;

        if (a_options.args.fcfWrapper && !isRoot) {
          var content = "<div ";
          content += !a_options.rawTemplate.wrapperExists ? "nowrapper " : "";
          content += "fcftemplate=\"" + a_options.state.theme.resolveAlias(a_options.template) + "\" ";
          content += " id='" + a_options.args.fcfId + "' ";
          content += " class=' fcfwrapper " + fcf.str(a_options.args.fcfClassInner) + " " + fcf.str(a_options.args.fcfClass) + "' "
          content += a_options.args.fcfEvntid ? " fcfevntid='" + a_options.args.fcfEvntid + "'" : "";
          
          if (a_options.args.fcfStyle || a_options.args.fcfStyleInner)
            content += " style='" + fcf.str(a_options.args.fcfStyleInner) + ";" + fcf.str(a_options.args.fcfStyle) + "' ";
          if (typeof a_options.args.fcfAttributes == "object"){
            for(var k in a_options.args.fcfAttributes)
              content += " " + k + "=\"" + a_options.args.fcfAttributes[k] + "\" ";
          }
          if (a_options.args.fcfParent)
            content += "fcfparent=\"" + a_options.args.fcfParent + "\" ";
          if (a_options.args.fcfAlias)
            content += "fcfalias=\"" + a_options.args.fcfAlias + "\" ";
          content +=">" + result + "</div>";
          result = content;
        }

        a_options.onResult(undefined, result);
      }

      this._renderBlock = async function(a_code, a_args, a_path, a_stringNumber, a_template) {
        fcf.getContext().currentTemplate = {id: a_args.args.fcfId};
        let ov = fcf.NDetails.currentTemplate;
        fcf.NDetails.currentTemplate = a_template;
        await fcf.scriptExecutor.execute(a_code, a_args, a_path, a_stringNumber, 0, true);
        fcf.NDetails.currentTemplate = ov;
      }

      this._getRenderingBlocks = function(a_templatePath, a_templateInfo, a_aliases) {
        a_templatePath = fcf.getPath(a_templatePath, a_aliases, true);
        var octothorpePos = a_templatePath.indexOf("+");
        if (octothorpePos !== -1) {
          return [a_templatePath.substr(octothorpePos+1, a_templatePath.length - octothorpePos - 1)];
        } else {
          return a_templateInfo.defaultBlocks;
        }
      }

    }

    return NDetails.TemplateProcessor;
  }
});
