﻿    var fChainSelectorSeed = 0;
    /*
    init : {
      DIV: html container, requred,
      outputFormFieldName : "fieldId",
      classes: css names,
      path: initail path ('Books.General.A'), optional,
      readonly: true/false,
      disabled: true/false,
      values: [
        { id: n1, val: node1, name: 'Some header', descr: 'Some text description' },
        {
          id: n2,
          val: node2,
          name: '...',
          descr: '...',
          children : [ { id: n21, val: node21, descr: '...' }, { id: n22, val: node22, descr: '...' } ]
        },
        ...
      ]
    }
    */
    published.ChainSelector = function (init) {
      if (!WAVE.exists(init)) throw "ChainSelector.ctor(init=null)";

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

      var fId = "wvChainSelector_" + fChainSelectorSeed++;
      var fDiv;
      if (WAVE.has(init, 'DIV')) fDiv = WAVE.isString(init.DIV) ? WAVE.id(init.DIV) : init.DIV;
      var fOutputFormFieldName = WAVE.strDefault(init.outputFormFieldName, null);
      var fDescr = WAVE.strDefault(init.description);
      var fValues = (WAVE.has(init, 'values') && WAVE.exists(init.values) && WAVE.isArray(init.values)) ? init.values : [];
      var fPath = WAVE.strDefault(init.path, '');
      var fReadOnly = WAVE.strAsBool(init.readonly, false);
      var fDisabled = WAVE.strAsBool(init.disabled, false);
      var fCombos = [];

      var fClasses = WAVE.get(init, "classes", {});
      fClasses.divWrapCls = WAVE.strDefault(fClasses.divWrapCls, published.CLS_CHAIN_SELECTOR);
      fClasses.divInfoCls = WAVE.strDefault(fClasses.divInfoCls, published.CLS_CS_DIV_INFO);
      fClasses.divNameCls = WAVE.strDefault(fClasses.divNameCls, published.CLS_CS_DIV_NAME);
      fClasses.divDescrCls = WAVE.strDefault(fClasses.divDescrCls, published.CLS_CS_DIV_DESCR);
      fClasses.divComboWrapCls = WAVE.strDefault(fClasses.divComboWrapCls, published.CLS_CS_DIV_COMBO_WRAP);
      fClasses.divArrowCls = WAVE.strDefault(fClasses.divArrowCls, published.CLS_CS_DIV_ARROW);

      var fSeed = "cs_" + fChainSelectorSeed;
      var fControlIds = {
        divInfoId: "divInfo_" + fSeed,
        divNameId: "divName_" + fSeed,
        divDescrId: "divDescr_" + fSeed,
        divComboWrapId: "divComboWrap_" + fSeed + "_",
        selectBodyId: "selectBodyId" + fSeed + "_",
        divArrowId: "divArrow_" + fSeed + "_",
        outputHiddenId : "hidden_" + fSeed
      };

      function getSelectorByIndex(idx) {
        var sid = fControlIds.selectBodyId + idx.toString();
        return WAVE.id(sid);
      }

      function selectorValueChanged(e) {
        var elm = e.target.parentNode;
        var lvl = fCombos.indexOf(elm);
        var newPath = __path().slice(0, lvl+1);
        __navigate(newPath);
      }

      function addCombo(values, value) {
        if (fDisabled || fReadOnly) return false;

        // construct template
        var lvl = fCombos.length;
        var wid = fControlIds.divComboWrapId + lvl.toString();
        var sid = fControlIds.selectBodyId + lvl.toString();
        var html = WAVE.strHTMLTemplate("<div id='@divComboWrapId@' class='@divComboWrapCls@' @disabled@ @readonly@>" +
                                          "<select id='@selectBodyId@'>" +
                                            "<option value=''></option>", //add blank,
                                        {
                                          divComboWrapId:  wid,
                                          divComboWrapCls: fClasses.divComboWrapCls,
                                          selectBodyId: sid,
                                          disabled: fDisabled ? "" : "disabled",
                                          readonly: fReadOnly ? "readonly" : ""
                                        });

        values.wEach(function (v) {
          html += WAVE.strHTMLTemplate("<option value='@value@' @selected@>@descr@</option>",
                                       {
                                         value:    v.id,
                                         selected: WAVE.strSame(v.id, value) ? "selected" : "",
                                         descr:    v.val
                                       });
        });

        html += WAVE.strHTMLTemplate("</select><div id='@divArrowId@' class='@divArrowCls@'></div></div>",
                                     {
                                       divArrowId:  fControlIds.divArrowId + lvl.toString(),
                                       divArrowCls: fClasses.divArrowCls
                                     });

        var divDescr = WAVE.id(fControlIds.divInfoId);
        var tmp = document.createElement('div');
        tmp.innerHTML = html;
        var divComboWrap = tmp.firstChild;
        fDiv.insertBefore(divComboWrap, divDescr);
        fCombos[lvl] = divComboWrap;

        WAVE.addEventHandler(WAVE.id(sid), "change", selectorValueChanged);

        return divComboWrap;
      }

      function removeComboxes(cnt) {
        if (fDisabled || fReadOnly) return false;

        for (var i=0; i<cnt; i++) {
          var combo = fCombos.pop();
          if (!WAVE.exists(combo)) return;

          var lvl = fCombos.length;
          WAVE.removeEventHandler(getSelectorByIndex(lvl), "change", selectorValueChanged);

          fDiv.removeChild(combo);
        }
      }

      function __navigate(path) {
        if (fDisabled || fReadOnly) return false;
        if (!WAVE.exists(path)) path = [''];
        if (WAVE.isString(path)) path = path.split('.');
        if (!WAVE.isArray(path)) return false;

        var values = WAVE.arrayWalkable(fValues);
        var children = null;
        var part;
        var node;
        var result = true;

        // init
        removeComboxes(fCombos.length - path.length);
        if (fCombos.length <= 0) addCombo(values, '');

        // navigate to tree node
        for (var i in path) {
          part = path[i];

          // special case: empty part resets current combo
          if (WAVE.strSame(part, '')) {
            removeComboxes(fCombos.length-i);
            children = values;
            break;
          }

          // seek for node with same name
          if (values === null) {
            result = false;
            removeComboxes(fCombos.length-i);
            break;
          }
          var n = values.wFirst(function (v) { return WAVE.strSame(v.id, part); });
          if (n === null)
          {
            result = false;
            removeComboxes(fCombos.length-i);
            children = values;
            break;
          }
          node = n;
          children = WAVE.exists(node.children) ? WAVE.arrayWalkable(node.children) : null;

          // add new combo if it does not exist, adjust next combos
          var combo = fCombos[i];
          if (!WAVE.exists(combo)) {
            removeComboxes(fCombos.length-i);
            addCombo(values, part);
          } else {
            var selector = getSelectorByIndex(i);
            if (!WAVE.strSame(selector.value, part)) {
              removeComboxes(fCombos.length-i-1);
              selector.value = part;
            }
          }

          values = children;
        }

        // add empty next combo if needed
        if (WAVE.exists(children)) addCombo(children, '');

        // update info
        var divName = WAVE.id(fControlIds.divNameId);
        var name = WAVE.exists(node) ? WAVE.strDefault(node.name, '') : '';
        divName.innerText = !WAVE.strSame(name, '') ? "Name: " + name : '';
        var descr = WAVE.exists(node) ? WAVE.strDefault(node.descr, '') : '';
        var divDescr = WAVE.id(fControlIds.divDescrId);
        divDescr.innerText = !WAVE.strSame(descr, '') ?"Description: " + descr : '';

        setHiddenValue();
        chain.eventInvoke(published.EVT_CHAIN_SELECTOR_UPDATED, __value());

        return result;
      }

      function __path() {
        var result = [];
        for (var i in fCombos) {
          var combo = fCombos[i];
          var selector = WAVE.id(fControlIds.selectBodyId + i.toString());
          result.push(selector.value);
        }
        return result;
      }

      function __value() {
        var res = __path().join('.');
        if (res.length > 0 && WAVE.strSame(res[res.length - 1], '.'))
          res = res.substring(0, res.length - 1);
        return res;
      }

      function setHiddenValue() {
        if (fOutputFormFieldName !== null) WAVE.id(fControlIds.outputHiddenId).value = chain.value();
      }

      function build() {
        if (!WAVE.exists(fDiv)) return;

        WAVE.addClass(fDiv, fClasses.divWrapCls);

        // build main html
        var html = WAVE.strHTMLTemplate("<input id='@outerId@' type='hidden' name='@outerName@'/>" +
                                        "<div id='@divInfoId@' class='@divInfoCls@'>" +
                                          "<div id='@divNameId@' class='@divNameCls@'></div>" +
                                          "<div id='@divDescrId@' class='@divDescrCls@'></div>" +
                                        "</div>",
                                        {
                                          outerId: fControlIds.outputHiddenId,
                                          outerName: WAVE.strDefault(fOutputFormFieldName, ''),
                                          divInfoId:  fControlIds.divInfoId,
                                          divInfoCls: fClasses.divInfoCls,
                                          divNameId: fControlIds.divNameId,
                                          divNameCls: fClasses.divNameCls,
                                          divDescrId: fControlIds.divDescrId,
                                          divDescrCls: fClasses.divDescrCls
                                        });
        fDiv.innerHTML = html;

        // inject first combobox
        __navigate(fPath);
      }

      // public functions

      this.navigate = function(path) {
        return __navigate(path);
      };

      this.path = function() {
        return __path();
      };

      this.value = function () {
        return __value();
      };

      build();

      return chain;
    };//ChainSelector