// Input field for queries
/*
 * TODO: Support alert for query problems.
 */

"use strict";

define(['util'], function () {

  return {

    /**
     * Create a new input field.
     */
    create : function (element) {
      return Object.create(this)._init(element);
    },


    // Initialize new input field
    _init : function (element) {
      this._el = element;

      this._container = document.createElement("div");
      this._container.setAttribute('id', 'hint');

      // Create mirror for searchField
      // This is important for positioning
      // if ((this._mirror = document.getElementById("searchMirror")) === null) {
      this._mirror = document.createElement("div");
      const m = this._mirror;
      m.classList.add('hint', 'mirror');
      m.addE("span");
      m.appendChild(this._container);
      m.style.height = "0px";
      document.getElementsByTagName("body")[0].appendChild(m);

      // Update position of the mirror
      const re = this.reposition.bind(this);
      window.addEventListener('resize', re);
      this._el.addEventListener('onfocus', re);
      this.reposition();

      // Prevent multiselections
      this._el.addEventListener("select", () => {
        const start = this._el.selectionStart;
        const end = this._el.selectionEnd;
        if (start !== null && end !== null && start !== end) {
          this._el.setSelectionRange(start, end);
        }
      });

      return this;
    },


    /**
     * Get the mirrored input field.
     */
    mirror : function () {
      return this._mirror;
    },


    /**
     * Get the container element.
     * This contains the hint helper / menus
     * and probably an
     * error message.
     */
    container : function () {
      return this._container;
    },


    /**
     * Get the input element the
     * hint helper is attached to.
     */
    element : function () {
      return this._el;
    },


    /**
     * Get the value of the input field
     * the hint helper is attached to.
     */
    value : function () {
      return this._el.value;
    },


    /**
     * Get the value of the input field mirror.
     */
    mirrorValue : function () {
      return this._mirror.firstChild.textContent;
    },


    /**
     * Reset the input value
     */
    reset : function () {
      this._el.value = "";
    },


    /**
     * Update the mirror content.
     */
    update : function () {
      this._mirror.firstChild.textContent = this._split()[0];
      this._container.style.left = this._rightPos() + 'px';
      return this;
    },


    /**
     * Insert text into the mirror.
     * This is a prefix of the input field's
     * value.
     */
    insert : function (text) {
      const splittedText = this._split();
      const s = this._el;
      s.value = splittedText[0] + text + splittedText[1];
      s.selectionStart = (splittedText[0] + text).length;
      s.selectionEnd = s.selectionStart;

      // Maybe update?
      this._mirror.firstChild.textContent = splittedText[0] + text;
      return this;
    },


    /**
     * Move hinthelper to given character position
     */
    moveto : function (charpos) {
      const e = this._el;
      e.selectionStart = charpos;
      e.selectionEnd = charpos;
      e.focus();
      return this.update();
    },


    /**
     * Reposition the input mirror directly
     * below the input box.
     */
    reposition : function () {
      const inputClientRect = this._el.getBoundingClientRect();
      const inputStyle = window.getComputedStyle(this._el, null);

      const bodyClientRect = 
            document.getElementsByTagName('body')[0].getBoundingClientRect();

      // Reset position
      const mirrorStyle = this._mirror.style;
      mirrorStyle.left  = parseInt(inputClientRect.left) + "px";
      mirrorStyle.top   = parseInt(inputClientRect.bottom - bodyClientRect.top) + "px";
      mirrorStyle.width = inputStyle.getPropertyValue("width");

      // These may be relevant in case of media depending css
      mirrorStyle.paddingLeft     = inputStyle.getPropertyValue("padding-left");
      mirrorStyle.marginLeft      = inputStyle.getPropertyValue("margin-left");
      mirrorStyle.borderLeftWidth = inputStyle.getPropertyValue("border-left-width");
      mirrorStyle.borderLeftStyle = inputStyle.getPropertyValue("border-left-style");
      mirrorStyle.fontSize        = inputStyle.getPropertyValue("font-size");
      mirrorStyle.fontFamily      = inputStyle.getPropertyValue("font-family");
    },


    /**
     * Get the context, which is the input
     * field's value bounded to the
     * cursor position.
     */
    context : function () {
      return this._split()[0];
    },


    // Get the right position
    _rightPos : function () {
      const box = this._mirror.firstChild.getBoundingClientRect();
      return box.right - box.left;
    },


    /*
     * Return two substrings,
     * splitted at a given position
     * or at the current cursor position.
     */
    _split : function (start) {
      const s = this._el;
      const value = s.value;

      // Get start from cursor position
      if (arguments.length === 0)
        start = s.selectionStart;

      return new Array(
        value.substring(0, start),
        value.substring(start, value.length)
      );
    },

    /**
     * Return the cursor character offsets
     */
    selectionRange : function () {
      return [this._el.selectionStart, this._el.selectionEnd];
    }
  }
});
