﻿var fObjectEditorIdSeed = 0;
//{
//  DIV: div,
//  content: {}
//  type : {
//    id: { type: "text" },
//    nls: {
//      type: "nls",
//      options: {n: {}}
//    }
//  }
//}
published.ObjectEditor = function(init) {
  if (typeof (init) === tUNDEFINED || init === null || typeof (init.DIV) === tUNDEFINED || init.DIV === null) throw "ObjectEditor.ctor(init.DIV)";

  var editor = this;
  WAVE.extend(editor, WAVE.EventManager);

  fObjectEditorIdSeed++;
  var fSeed = "wvObjEditor_" + fObjectEditorIdSeed;

  var fDIV = init.DIV;
  var fMeta = WAVE.get(init, "meta", {});
  var fType = WAVE.strDefault(fMeta.type, "text");
  var fSchema = WAVE.get(fMeta, "schema", {});
  var fPlh = WAVE.strDefault(fMeta.plh);
  var fSets = WAVE.mergeArrays([], fMeta.sets, function(a, b) { return a.toLowerCase() === b.toLowerCase(); }, function(a) { return a.toLowerCase(); });
  var fDflt = fMeta.dflt;
  var fContent = WAVE.get(init, "content", typeof(fDflt) !== tUNDEFINED ? fDflt : {});

  var fReadOnly = WAVE.strAsBool(init.readonly, false);
  var fDisabled = WAVE.strAsBool(init.disabled, false);
  var fCanRaw = WAVE.strAsBool(init.canRaw, false) && !fReadOnly && !fDisabled;
  var fCanDel = WAVE.strAsBool(init.canDel, true) && !fReadOnly && !fDisabled;
  var fCanAdd = WAVE.strAsBool(init.canAdd, true) && !fReadOnly && !fDisabled;
  var fMaxLength = WAVE.intValidPositive(fMeta.maxLength) ? fMeta.maxLength : 2;
  var fOutputFormFieldName = WAVE.strDefault(init.outputFormFieldName, "");

  var fCurrentLocal = WAVE.strDefault(init.local, "eng");
  var fLocalizerSchema = "ObjectEditor";
  var fLocalizerField = "objEd";
  var fValidators = [];
  var fEditors = [];

  var fControlsIds = {
    editorContainerId : "edit_cont_" + fSeed,
    buttonsContainerId : "edit_buttons_" + fSeed,
    inputId : "edit_input_" + fSeed,
    outputHiddenId: "edit_conf_out_" + fSeed,
    btnRawId: "edit_raw_" + fSeed,
    btnDelId: "edit_del_" + fSeed
  };

  var fTexts = WAVE.get(init, "texts", {});
    fTexts.addTitle = getText("add-title", "Add New Language");
    fTexts.addBody = getText("add-body", "");
    fTexts.rawTitle= getText("raw-title", "Raw Edit");
    fTexts.rawBody = getText("raw-body");
    fTexts.clearConfirmTitle = getText("clear-confirm-title", "Confirm Clear");
    fTexts.clearConfirmBody = getText("clear-confirm-body", "Clear all language data?");
    fTexts.delConfirmTitle = getText("del-confirm-title", "Confirm Delete");
    fTexts.resetConfirmTitle = getText("reset-confirm-title", "Confirm Reset");
    fTexts.delConfirmBody = getText("del-confirm-body");
    fTexts.resetConfirmBody = getText("reset-confirm-body");
    fTexts.btnDel = getText("btn-del", "Delete");
    fTexts.btnAdd = getText("btn-add", "Add");
    fTexts.btnRaw = getText("btn-raw", "Edit Raw");
    fTexts.btnClear = getText("btn-clear", "Clear");
    fTexts.btnReset = getText("btn-reset", "Reset");
    fTexts.required = getText("err-meg", "Required");

  var Control = function(name, nodeDIV) {
    var meta = fSchema[name];
    var DIV = WAVE.get(meta , "DIV", nodeDIV);
    var dflt = meta.dflt;
    var content = WAVE.get(fContent, name, dflt);
    var type = WAVE.strDefault(meta.type, "text");
    var schema = WAVE.get(meta, "schema", {});
    var plh = WAVE.strDefault(meta.plh, "");
    var fIdx = WAVE.intValidPositiveOrZero(meta.index) ? meta.index : -1;
    var sets = WAVE.mergeArrays([], meta.sets, function(a, b) { return a.toLowerCase() === b.toLowerCase(); }, function(a) { return a.toLowerCase(); });
    var items = WAVE.isObject(meta.items) ? meta.items : {};
    var disabled = WAVE.strAsBool(meta.disabled, fDisabled);
    var readonly = WAVE.strAsBool(meta.readonly, fReadOnly);
    var canRaw = WAVE.strAsBool(meta.canRaw, false);
    var canDel = WAVE.strAsBool(meta.canDel, true);
    var canAdd = WAVE.strAsBool(meta.canAdd, true);
    var required = WAVE.strAsBool(meta.required, false);
    var nodeClass = WAVE.strDefault(meta.cls);
    var errMsg = WAVE.strDefault(meta.errMsg, required ? fTexts.required : "");
    var showError = WAVE.isFunction(meta.showError) ? meta.showError : function() { WAVE.id(errorDivId).innerText = errMsg; };
    var hideError = WAVE.isFunction(meta.hideError) ? meta.hideError : function() { WAVE.id(errorDivId).innerText = ""; };
    var validFunction = WAVE.isFunction(meta.validate) ?
      meta.validate :
      function(sender, v, inIndex) {
      var res = true;
      if (required) {
        if (type === "text" || type === "textarea" || type === "radio" || type === "combo") res = !WAVE.strEmpty(v);
        if (type === "array" || type === "multiple") res = WAVE.isArray(v) && v.length > 0;
        if (type === "object" || type === "ps") res = WAVE.isObject(v) && !WAVE.empty(v);
      }
      return {ok: res, value: v, index: inIndex};
    };

    var divId = uName(name, fControlsIds.editorContainerId);
    var inputId = uName(name, fControlsIds.inputId);
    var errorDivId = divId + "_err";
    var ed = null;
    var i, l, nId, radios, key, html, vFunc, arr;

    if (type === "text" || type === "textarea" || type === "combo" || type === "check") {
      vFunc = function() {
        var ctrl = WAVE.id(inputId);
        var vl = type === "check" ? ctrl.checked : ctrl.value;
        return validateField(validFunction, ctrl, vl, fIdx, errorDivId, errMsg, showError, hideError);
      };
    }
    if (type === "array" || type === "object" || type === "ps") {
      vFunc = function() {
        return validateField(validFunction, ed, ed.jsonContent(), fIdx, errorDivId, errMsg, showError, hideError);
      };
    }
    if (type === "radio" || type === "multiple") {
      vFunc = function() {
        radios = document.getElementsByName(inputId);
        l = radios.length;
        arr = [];
        for (i = 0; i < l; i++) {
          if (radios[i].checked) {
            arr.push(radios[i].value);
          }
        }
        return validateField(validFunction, radios, type === "multiple" ? arr : (arr.length > 0 ? arr[0] : null), fIdx, errorDivId, errMsg, showError, hideError);
      };
    }
    fValidators.push(vFunc);

    var strHtml =
      "<div class='@nodeCont@'>" +
        (WAVE.strEmpty(plh) ? "" : "<label class='@lblCls@' for='@labelFor@'>@plh@</label>") +
        "<div class='@errorDivCls@' id='@errorDivId@'></div>" +
        "<div id='@divId@' class='@ccCls@'></div>" +
      "</div>";
    var strDisabled = disabled ? "disabled" : "";
    var strReadonly = readonly ? "readonly" : "";

    DIV.insertAdjacentHTML("beforeend", WAVE.strHTMLTemplate(strHtml, {
      nodeCont: published.CLS_OBJECT_EDITOR_NODE_CONTAIER + " " + nodeClass,
      labelFor: inputId,
      lblCls: required ? "wvRequired" : "",
      plh: plh,
      errorDivCls: "wvError",
      errorDivId: errorDivId,
      divId: divId,
      ccCls: type === "array" ?
        published.CLS_OBJECT_EDITOR_ARRAY_CONTAIER :
        ((type === "object" || type === "ps") ?
          published.CLS_OBJECT_EDITOR_OBJECT_CONTAIER :
          published.CLS_OBJECT_EDITOR_INPUT_CONTAIER)
    }));

    var gDiv = WAVE.id(divId);
    var erDiv = WAVE.id(errorDivId);
    var cinit = {
      DIV : gDiv,
      id : inputId,
      value : WAVE.strDefault(content),
      meta: meta,
      content: content,
      disabled : disabled,
      readonly : readonly,
      addLabel : false,
      placeholder : plh,
      sets: sets,
      canRaw : canRaw,
      canAdd : canAdd,
      canDel : canDel
    };

    function checkAndSave() {
      var res = vFunc();
      if (!res.ok) return;

      fContent = WAVE.isObject(fContent) ? fContent : {};
      var value = res.value;
      fContent[name] = value;
      editor.content(fContent);
    }

    if(type === "textarea"){
      ed = new published.MultiLineTextBox(cinit);
      ed.eventBind(published.EVT_MULTI_LINE_TEXT_UPDATED, function (e, args) {
        checkAndSave();
      });
    } else if(type === "object" || type === "array") {
      ed = new published.ObjectEditor(cinit);
      ed.eventBind(published.EVT_OBJECT_EDITOR_UPDATED, function (e, d) {
        checkAndSave();
      });
    } else if (type === "ps") {
      ed = new published.PropSetEditor(cinit);
      ed.eventBind(published.EVT_PS_EDITOR_UPDATED, function (e, d) {
        checkAndSave();
      });
    } else if(type === "check") {
      gDiv.innerHTML = WAVE.strHTMLTemplate("<input id='@id@' @disabled@ @readonly@ type='checkbox' placeholder='@plh@' title='@plh@' />",
      {
        id: inputId,
        disabled: strDisabled,
        readonly: strReadonly,
        plh: plh,
        value: WAVE.strDefault(content)
      });
      nId = WAVE.id(inputId);
      nId.checked = WAVE.strAsBool(content, false);
      nId.onclick = function change(e) {
        checkAndSave();
      };
    } else if(type === "multiple") {
      html = "";
      for(key in items) {
        nId = inputId + "_+ " + key;
        html += WAVE.strHTMLTemplate("<label for='@id@'>@name@</label>",
        {
          id: nId,
          name: WAVE.strDefault(items[key], key)
        });
        html += WAVE.strHTMLTemplate("<input type='checkbox' @checked@ name='@name@' value='@value@' id='@id@' @disabled@ @readonly@/>",
        {
          checked: ((WAVE.isArray(content) && WAVE.inArray(content, key)) ? "checked='checked'" : ""),
          name: inputId,
          value: key,
          id: nId,
          disabled: strDisabled,
          readonly: strReadonly
        });
      }
      gDiv.innerHTML = html;
      radios = document.getElementsByName(inputId);
      l = radios.length;
      for(i = 0; i < l; i++) {
        radios[i].onclick = checkAndSave;
      }
    } else if (type === "radio") {
      html = "";
      for(key in items){
        nId = inputId + "_+ " + key;
        html += WAVE.strHTMLTemplate("<label for='@id@'>@name@</label>",
        {
          id: nId,
          name: WAVE.strDefault(items[key], key)
        });
        html += WAVE.strHTMLTemplate("<input type='radio' @checked@ name='@name@' value='@value@' id='@id@' @disabled@ @readonly@/>",
        {
          checked: content === key ? "checked='checked'" : "",
          name: inputId,
          value: key,
          id: nId,
          disabled: strDisabled,
          readonly: strReadonly
        });
      }
      gDiv.innerHTML = html;
      radios = document.getElementsByName(inputId);
      l = radios.length;
      for(i = 0; i < l; i++) {
        radios[i].onclick = checkAndSave;
      }
    }
    else if(type === "combo") {
      html = "<select id='@id@' @disabled@ @readonly@>";
      if(WAVE.strEmpty(dflt)) {
        html += "<option value=''></option>";
      }
      for(key in items) {
        html += WAVE.strHTMLTemplate("<option @selected@ value='@value@'>@text@</option>",
        {
          selected: content === key ? "selected" : "",
          value: key,
          text: WAVE.strDefault(items[key], key)
        });
      }
      html += "</select>";
      gDiv.innerHTML = WAVE.strHTMLTemplate(html,
      {
        id: inputId,
        name: WAVE.strDefault(plh, key),
        disabled: strDisabled,
        readonly: readonly ? "disabled" : ""
      });
      if (!WAVE.strEmpty(dflt) && WAVE.strEmpty(fContent[name])) {
        fContent[name] = dflt;
      }
      WAVE.id(inputId).onchange = function change(e) {
        checkAndSave();
      };
    } else if(type === "text") {
      gDiv.innerHTML = WAVE.strHTMLTemplate("<input id='@id@' @disabled@ @readonly@ type='text' placeholder='@plh@' title='@plh@' value='@value@'/>",
      {
        id: inputId,
        disabled: strDisabled,
        readonly: strReadonly,
        plh: plh,
        value: WAVE.strDefault(content)
      });
      WAVE.id(inputId).onchange = function change(e) {
        checkAndSave();
      };
    }
    vFunc();
  };

  function getText(n, dflt){
    return localize(fTexts[n], dflt);
  }

  function localize(text, dflt){
    return WAVE.strLocalize(fCurrentLocal, fLocalizerSchema, fLocalizerField, WAVE.strDefault(text, dflt));
  }

  var fClasses = WAVE.get(init, "btnClasses", {});
    fClasses.addBtnCls = WAVE.strDefault(fClasses.addBtnCls, published.CLS_PS_BTN_ADD);
    fClasses.delBtnCls = WAVE.strDefault(fClasses.delBtnCls, published.CLS_PS_BTN_DEL);
    fClasses.rawBtnCls = WAVE.strDefault(fClasses.rawBtnCls, published.CLS_PS_BTN_RAW);
    fClasses.clearBtnCls = WAVE.strDefault(fClasses.clearBtnCls, published.CLS_PS_BTN_CLEAR);
    fClasses.modalBtnCls = WAVE.strDefault(fClasses.modalBtnCls, "");

  function setHiddenValue(){
      if (fOutputFormFieldName !== "") WAVE.id(fControlsIds.outputHiddenId).value = editor.content();
  }

  this.content = function(value, rebuild) {
    if (typeof (value) === tUNDEFINED)
      return WAVE.empty(fContent) ? "" : JSON.stringify(fContent, null, 2);

    var parsed = WAVE.tryParseJSON(value);
    if (parsed.ok) {
      fContent = parsed.obj;

      setHiddenValue();
      editor.eventInvoke(published.EVT_OBJECT_EDITOR_UPDATED, fContent);
      if(rebuild === true) {
        build();
      }
    }
    return value;
  };

  this.jsonContent = function() {
    return fContent;
  };

  this.maxLength = function() {
    return fMaxLength;
  };

  this.DIV = function() {
    return fDIV;
  };

  this.validate = function() {
    var vals = [];
    var ok = true;
    for(var f in fValidators) {
      var r = fValidators[f]();
      ok = ok && r.ok;
      vals.push(r);
    }
    return {ok: ok, result: vals};
  };

  this.validators = function() {
    return fValidators;
  };

  function validateField(func, control ,value, idx, errorDivId, errMsg, showError, hideError) {
    var res = func(editor, value, idx);
    if (res.ok) {
      hideError();
      editor.eventInvoke(published.EVT_OBJECT_EDITOR_VALIDATION_SUCCESS, { phase: published.EVT_PHASE_AFTER, target: control });
    } else {
      showError();
      editor.eventInvoke(published.EVT_OBJECT_EDITOR_VALIDATION_ERROR, { phase: published.EVT_PHASE_AFTER, target: control });
    }
    return res;
  }

  function uName(filedName, id){
    return filedName + id;
  }

  function build() {
    fValidators = [];
    fDIV.innerHTML = WAVE.strHTMLTemplate("<div id='@contId@'></div><div id='@buttonsId@'></div>",
      {
        contId: fControlsIds.editorContainerId,
        buttonsId: fControlsIds.buttonsContainerId
      });
    var nodeDIV = WAVE.id(fControlsIds.editorContainerId);
    var buttonsDIV = WAVE.id(fControlsIds.buttonsContainerId);
    if (fType === "array") {
      if(WAVE.isArray(fContent)){
        for(var i = 0; i < fContent.length; i++){
          createArrItem(i, nodeDIV);
        }
      }
    } else {
      for(var o in fSchema){
        var ed = new Control(o, nodeDIV);
      }
    }

    if(fType === "array" && fCanAdd && !fReadOnly && !fDisabled && (!WAVE.isArray(fContent) || fContent.length < fMaxLength)){
      var buttonAddId = "add_" + fSeed;
      buttonsDIV.insertAdjacentHTML("beforeend", "<input type='button' value='" + fTexts.btnAdd + "' id='" + buttonAddId + "' class='" + fClasses.addBtnCls + "'/>");
      WAVE.id(buttonAddId).onclick = function () {
        if(!WAVE.isArray(fContent)) fContent = [];
        fContent.push({});
        editor.content(fContent, true);
        editor.validate();
      };
    }

    if (fCanRaw) {
      buttonsDIV.insertAdjacentHTML("beforeend", "<input type='button' value='" + fTexts.btnRaw + "' id='" + fControlsIds.btnRawId + "' class='" + fClasses.addBtnCls + "'/>");
      WAVE.id(fControlsIds.btnRawId).onclick = function () {
        var inputId = "raw" + fSeed;
        published.showConfirmationDialog(
          fTexts.rawTitle,
          "<div>" + fTexts.rawBody + "</div>" +
          "<div><textarea style='width: 70vw; height: 70vh;' class='" + published.CLS_PS_TEXTAREA_RAW_EDITOR + "' type='text' id='" + inputId + "'></textarea></div>",
          [published.DLG_OK, published.DLG_CANCEL],
          function (sender, result) {
            if (result === published.DLG_OK) {
              var val = WAVE.id(inputId).value;
              var res = WAVE.tryParseJSON(val);
              if (res.ok){
                  if (fType !== 'array' && WAVE.checkKeysUnique(res.obj)){
                    published.toast(WAVE.strLocalize(fCurrentLocal, fLocalizerSchema, fLocalizerField, "JSON is valid, but some keys are duplicated"), "error");
                    return false;
                  }
                  editor.content(val, true);
                }
              else {
                published.toast(WAVE.strLocalize(fCurrentLocal, fLocalizerSchema, fLocalizerField, "Invalid JSON"), "error");
                return false;
              }
            }
            return true;
          },
          { btnCls: fClasses.modalBtnCls });
        WAVE.id(inputId).value = editor.content();
        WAVE.id(inputId).focus();
      };
    }
  }

  var arrItemTemplate = "<div class='@divCLS@' id='@divId@'></div>" +
    (fCanDel && !fReadOnly && !fDisabled ? "<input type='button' id='@BtnId@' class='@BtnCls@' value='@delText@'/>" : "") +
    "<div class='@arrSep@'></div>";
  function createArrItem(idx, nodeDIV){
    var m = WAVE.memberClone(fMeta);
    m.type = "object";
    for(var nm in m.schema){
      m.schema[nm].index = idx;
    }
    var id = fControlsIds.editorContainerId + "_" + idx;
    var delButtonId = "del" + id;
    nodeDIV.insertAdjacentHTML("beforeend",WAVE.strHTMLTemplate(arrItemTemplate,
    {
      divCLS: published.CLS_OBJECT_EDITOR_ARRAY_ITEM,
      divId: id,
      BtnId: delButtonId,
      BtnCls: fClasses.delBtnCls,
      delText: fTexts.btnDel,
      arrSep: published.CLS_OBJECT_EDITOR_ARRAY_ITEM_SEPARATOR
    }));
    var ed = new published.ObjectEditor({
      DIV: WAVE.id(id),
      content: fContent[idx],
      meta: m,
      disabled : fDisabled,
      readonly : fReadOnly
    });
    ed.eventBind(published.EVT_OBJECT_EDITOR_UPDATED, function (e, data) {
      fContent[idx] = data;
      editor.content(fContent);
    });
    ed.eventBind(published.EVT_OBJECT_EDITOR_VALIDATION_ERROR,function(e, args) {
      editor.eventInvoke(published.EVT_OBJECT_EDITOR_VALIDATION_ERROR, { phase: published.EVT_PHASE_AFTER, target: args.target });
    });
    ed.eventBind(published.EVT_OBJECT_EDITOR_VALIDATION_SUCCESS,function(e, args) {
      editor.eventInvoke(published.EVT_OBJECT_EDITOR_VALIDATION_SUCCESS, { phase: published.EVT_PHASE_AFTER, target: args.target });
    });
    var vls = ed.validators();
    for(var vl in vls) {
      fValidators.push(vls[vl]);
    }
    if (fCanDel && !fReadOnly && !fDisabled) {
      WAVE.id(delButtonId).onclick = function () {
        published.showConfirmationDialog(
          fTexts.delConfirmTitle,
          "<div>"+fTexts.delConfirmBody+"</div>",
          [published.DLG_OK, published.DLG_CANCEL],
          function (sender, result) {
            if (result === published.DLG_OK) {
              fContent.splice(idx, 1);
              editor.content(fContent, true);
            }
            return true;
          },
          { btnCls: fClasses.modalBtnCls });
      };
    }
  }

  build();
};//ObjectEditor