//------------------------------------------------------------------------------
// Template-Engine
//------------------------------------------------------------------------------

'use strict'

class Generator_cl {
   constructor () {
      this.reset_px();
   }
   reset_px () {
      this.code_a = ['var result_a = [];\n'];
   }
   write_px (text_spl) {
      // Escape-Zeichen bei Strings ergänzen
      var text_s = text_spl.replace(/"/g, '\\"');
      text_s = text_s.replace(/'/g, "\\'");
      this.code_a.push('result_a.push("' + text_s + '");\n');
   }
   code_px (code_spl) {
      if (code_spl.startsWith('if')) {
         this.code_a.push('if (' + code_spl.substr(2) + ') {\n');
      } else if (code_spl.startsWith('else')) {
         this.code_a.push('} else {\n');
      } else if (code_spl.startsWith('endif')) {
         this.code_a.push('}\n');
      } else if (code_spl.startsWith('for')) {
         this.code_a.push('for (' + code_spl.substr(3) + ') {\n');
      } else if (code_spl.startsWith('endfor')) {
         this.code_a.push('}\n');
      } else {
         this.code_a.push(code_spl + '\n');
      }
   }
   substitute_px (subst_spl) {
      let value_s = subst_spl == null ? '' : String(subst_spl);
      this.code_a.push('result_a.push(' + value_s + ');\n');
   }
   generate_px () {
      var result_s = this.code_a.join('') + ' return result_a.join("");';
      var f_o = new Function ('context', result_s);
      return f_o;
   }
}

class TemplateCompiler_cl {
   constructor () {
      this.gen_o = new Generator_cl();
      this.reset_px();
   }
   reset_px () {
      this.gen_o.reset_px();
      this.preservePreWS_b = false;
      this.preservePostWS_b = false;
   }
   setPreWS_px (on_bpl) {
      if ((on_bpl == undefined) || (on_bpl == false)) {
         this.preservePreWS_b = false;
      } else {
         this.preservePreWS_b = true;
      }
   }
   setPostWS_px (on_bpl) {
      if ((on_bpl == undefined) || (on_bpl == false)) {
         this.preservePostWS_b = false;
      } else {
         this.preservePostWS_b = true;
      }
   }
   compile_px (source_spl) {
      var state_i = 0;
      var pos_i = 0;
      var len_i = source_spl.length;
      var text_s = '';
      var code_s = '';
      var subst_s = '';
      this.gen_o.reset_px();

      var doubletest_f = function (char_spl) {
         var status_b = false;
         if ((pos_i + 1) < len_i) {
            if ((source_spl[pos_i] == char_spl) && (source_spl[pos_i+1] == char_spl)) {
               status_b = true;
            }
         }
         return status_b;
      }

      while (pos_i < len_i) {
         switch (state_i) {
         case 0:
            // outside
            if (source_spl[pos_i] == '@') { // ECMA 5!
               if (doubletest_f('@') == false) {
                  state_i = 2;
                  code_s = '';
               } else {
                  // als normales Zeichen verarbeiten, ein Zeichen überlesen
                  state_i = 1;
                  text_s = '@';
                  pos_i++;
               }
            } else if (source_spl[pos_i] == '#') {
               if (doubletest_f('#') == false) {
                  state_i = 3;
                  subst_s = '';
               } else {
                  // als normales Zeichen verarbeiten, ein Zeichen überlesen
                  state_i = 1;
                  text_s = '#';
                  pos_i++;
               }
            } else if ((source_spl[pos_i] == ' ') || (source_spl[pos_i] == '\t')) {
               state_i = 4;
               text_s = '';
               pos_i--; // Zeichen erneut verarbeiten
            } else {
               state_i = 1;
               text_s = '';
               pos_i--; // Zeichen erneut verarbeiten
            }
            break;
         case 1:
            // inText
            if (source_spl[pos_i] == '@') {
               if (doubletest_f('@') == false) {
                  // Textende, neuer Code
                  state_i = 0;
                  this.gen_o.write_px(text_s);
                  pos_i--; // Zeichen erneut verarbeiten
               } else {
                  // als normales Zeichen verarbeiten, ein Zeichen überlesen
                  text_s += '@';
                  pos_i++;
               }
            } else if (source_spl[pos_i] == '#') {
               if (doubletest_f('#') == false) {
                  // Textende, neue Substitution
                  state_i = 0;
                  this.gen_o.write_px(text_s);
                  pos_i--; // Zeichen erneut verarbeiten
                  // Textende, neuer Code
               } else {
                  // als normales Zeichen verarbeiten, ein Zeichen überlesen
                  text_s += '#';
                  pos_i++;
               }
            } else if ((source_spl[pos_i] == ' ') || (source_spl[pos_i] == '\t')) {
               // Textende
               state_i = 0;
               this.gen_o.write_px(text_s);
               pos_i--; // Zeichen erneut verarbeiten
            } else {
               // sammeln
               if ((source_spl[pos_i] != '\n') && (source_spl[pos_i] != '\r')) {
                  text_s += source_spl[pos_i];
               } else if (source_spl[pos_i] == '\n') {
                  text_s += '\\n';
               } else {
                  text_s += '\\r';
               }
            }
            break;
         case 2:
            // inCode
            if (source_spl[pos_i] == '@') {
               if (doubletest_f('@') == false) {
                  // zu Ende, erzeugen
                  this.gen_o.code_px(code_s);
                  state_i = 5;  //0
               } else {
                  // als normales Zeichen verarbeiten, ein Zeichen überlesen
                  code_s += '@';
                  pos_i++;
               }
            } else {
               // sammeln
               code_s += source_spl[pos_i];
            }
            break;
         case 3:
            // inSubst
            // Verdopplung # hier nicht zulässig!
            if (source_spl[pos_i] == '#') {
               // zu Ende, erzeugen
               this.gen_o.substitute_px(subst_s);
               state_i = 0;
            } else {
               // sammeln
               subst_s += source_spl[pos_i];
            }
            break;
         case 4:
            // pre-code-whitespace
            switch (source_spl[pos_i]) {
            case ' ':
            case '\t':
               // sammeln
               text_s += source_spl[pos_i];
               break;
            default:
               state_i = 0;
               if (source_spl[pos_i] != '@') {
                  this.gen_o.write_px(text_s);
               } else {
                  if (doubletest_f('@') == false) {
                     // Whitespace vor Code-Beginn erkannt
                     if (this.preservePreWS_b == true) {
                        // trotzdem ausgeben
                        this.gen_o.write_px(text_s);
                     }
                  } // ansonsten wie normales Zeichen behandeln
               }
               pos_i--; // Zeichen erneut verarbeiten
            }
            break;
         case 5:
            // post-code-whitespace
            switch (source_spl[pos_i]) {
            case '\n':
               text_s += '\\n';
               state_i = 0;
               break;
            case '\r':
               text_s += '\\r';
               state_i = 0;
               break;
            case ' ':
            case '\t':
               // ggf. sammeln
               text_s += source_spl[pos_i];
               break;
            default:
               // Whitespace nach Code erkannt
               if (this.preservePostWS_b == true) {
                  // trotzdem ausgeben
                  this.gen_o.write_px(text_s);
               }
               state_i = 0;
               pos_i--; // Zeichen erneut verarbeiten
            }
            break;
         }
         pos_i++;
      }
      // welcher Zustand liegt abschließend vor?
      if (state_i == 1) {
         this.gen_o.write_px(text_s);
      } else if (state_i == 2) {
         this.gen_o.code_px(code_s);
      } else if (state_i == 3) {
         this.gen_o.substitute_px(subst_s);
      } else if (state_i == 4) {
         if (this.preservePreWS_b == true) {
            this.gen_o.write_px(text_s);
         }
      } else if (state_i == 5) {
         if (this.preservePostWS_b == true) {
            this.gen_o.write_px(text_s);
         }
      }

      return this.gen_o.generate_px();
   }
}
// EOF