/**
 * Create a control to show or hide values
 *
 * Possible options are:
 *
 *  - froms: an Element, an Array of Element, or a NodeList. A
 *    listener will be attached to **all** input of those elements
 *    and will trigger the check on changes
 *  - test: a function which will test the element and will return true
 *    if the content must be shown, false if it must be hidden.
 *    The function will receive the `froms` as first argument, and the
 *    event as second argument.
 *  - container: an Element, an Array of Element, or a Node List. The
 *    child nodes will be hidden / shown inside this container
 *  - event_name: the name of the event to listen to. `'change'` by default.
 *
 * @param object options
 */
var ShowHide = function (options) {
  var froms =
      typeof options.froms[Symbol.iterator] === "function"
        ? options.froms
        : [options.froms], //options.froms;
    test = options.test,
    container =
      typeof options.container[Symbol.iterator] === "function"
        ? options.container
        : [options.container],
    is_shown = true,
    event_name = "event_name" in options ? options.event_name : "change",
    container_content = [],
    debug = "debug" in options ? options.debug : false,
    load_event = "load_event" in options ? options.load_event : "load",
    id = "uid" in options ? options.id : Math.random(),
    toggle_callback =
      "toggle_callback" in options ? options.toggle_callback : null;
  var bootstrap = function (event) {
    if (debug) {
      console.log("debug is activated on this show-hide", this);
    }
    // keep the content in memory
    for (let c of container.values()) {
      let contents = [];
      for (let el of c.childNodes.values()) {
        contents.push(el);
      }
      container_content.push(contents);
    }

    // attach the listener on each input
    for (let f of froms.values()) {
      let inputs = f.querySelectorAll("input"),
        selects = f.querySelectorAll("select");

      for (let input of inputs.values()) {
        if (debug) {
          console.log("attaching event to input", input);
        }
        input.addEventListener(event_name, function (e) {
          onChange(e);
        });
      }
      for (let input of selects.values()) {
        if (debug) {
          console.log("attaching event to selects", input);
        }
        input.addEventListener(event_name, function (e) {
          onChange(e);
        });
      }
    }

    // first launch of the show/hide
    onChange(event);
  };

  var onChange = function (event) {
    var result = test(froms, event),
      me;

    if (result === true) {
      if (is_shown === false) {
        forceShow();
        me = new CustomEvent("show-hide-show", {
          detail: { id: id, container: container, froms: froms },
        });
        window.dispatchEvent(me);
      }
    } else if (result === false) {
      if (is_shown) {
        forceHide();
        me = new CustomEvent("show-hide-hide", {
          detail: { id: id, container: container, froms: froms },
        });
        window.dispatchEvent(me);
      }
    } else {
      throw "the result of test is not a boolean";
    }
  };

  var forceHide = function () {
    if (debug) {
      console.log("force hide");
    }
    if (toggle_callback !== null) {
      toggle_callback(container, "hide");
    } else {
      for (let contents of container_content.values()) {
        for (let el of contents.values()) {
          el.remove();
        }
      }
    }
    is_shown = false;
  };

  var forceShow = function () {
    if (debug) {
      console.log("show");
    }
    if (toggle_callback !== null) {
      toggle_callback(container, "show");
    } else {
      for (let i of container_content.keys()) {
        var contents = container_content[i];
        for (let el of contents.values()) {
          container[i].appendChild(el);
        }
      }
    }
    is_shown = true;
  };

  var forceCompute = function (event) {
    onChange(event);
  };

  if (load_event !== null) {
    window.addEventListener("load", bootstrap);
  } else {
    bootstrap(null);
  }

  return {
    forceHide: forceHide,
    forceShow: forceShow,
    forceCompute: forceCompute,
  };
};

export { ShowHide };
