// retoor <retoor@molodetz.nl>
export default class Balloon {
  constructor(targetEl) {
    this._targetEl = targetEl;
    this._hidden = true;
    this._hiding = null;
    this.WORD_SPEAK_TIME = 200;
    this.CLOSE_BALLOON_DELAY = 5000;
    this._BALLOON_MARGIN = 15;
    this._setup();
  }

  _setup() {
    this._balloon = document.createElement("div");
    this._balloon.className = "md-clippy-balloon";

    this._tip = document.createElement("div");
    this._tip.className = "md-clippy-balloon-tip";

    this._content = document.createElement("div");
    this._content.className = "md-clippy-balloon-content";

    this._balloon.appendChild(this._tip);
    this._balloon.appendChild(this._content);

    document.body.appendChild(this._balloon);
  }

  reposition() {
    const sides = ["top-left", "top-right", "bottom-left", "bottom-right"];

    for (let i = 0; i < sides.length; i++) {
      const side = sides[i];
      this._position(side);
      if (!this._isOut()) break;
    }
  }

  _position(side) {
    const o = this._targetEl.getBoundingClientRect();
    const h = this._targetEl.offsetHeight;
    const w = this._targetEl.offsetWidth;

    const bH = this._balloon.offsetHeight;
    const bW = this._balloon.offsetWidth;

    let left, top;
    switch (side) {
      case "top-left":
        left = o.left + w - bW;
        top = o.top - bH - this._BALLOON_MARGIN;
        break;
      case "top-right":
        left = o.left;
        top = o.top - bH - this._BALLOON_MARGIN;
        break;
      case "bottom-right":
        left = o.left;
        top = o.top + h + this._BALLOON_MARGIN;
        break;
      case "bottom-left":
        left = o.left + w - bW;
        top = o.top + h + this._BALLOON_MARGIN;
        break;
    }

    this._balloon.style.top = top + "px";
    this._balloon.style.left = left + "px";
    this._positionTip(side);
  }

  _positionTip(side) {
    const s = this._tip.style;
    s.top = "";
    s.left = "";
    s.marginTop = "";
    s.marginLeft = "";
    s.backgroundPosition = "";

    switch (side) {
      case "top-left":
        s.top = "100%";
        s.marginTop = "0px";
        s.left = "100%";
        s.marginLeft = "-50px";
        break;
      case "top-right":
        s.top = "100%";
        s.marginTop = "0px";
        s.left = "0";
        s.marginLeft = "50px";
        s.backgroundPosition = "-10px 0";
        break;
      case "bottom-right":
        s.top = "0";
        s.marginTop = "-16px";
        s.left = "0";
        s.marginLeft = "50px";
        s.backgroundPosition = "-10px -16px";
        break;
      case "bottom-left":
        s.top = "0";
        s.marginTop = "-16px";
        s.left = "100%";
        s.marginLeft = "-50px";
        s.backgroundPosition = "0px -16px";
        break;
    }
  }

  _isOut() {
    const o = this._balloon.getBoundingClientRect();
    const bH = this._balloon.offsetHeight;
    const bW = this._balloon.offsetWidth;

    const wW = window.innerWidth;
    const wH = window.innerHeight;

    const top = o.top;
    const left = o.left;
    const m = 5;
    if (top - m < 0 || left - m < 0) return true;
    return top + bH + m > wH || left + bW + m > wW;
  }

  speak(complete, text, hold) {
    this._hidden = false;
    this.show();
    const c = this._content;

    c.style.height = "auto";
    c.style.width = "auto";
    c.textContent = text;
    const w = c.offsetWidth;
    c.style.width = w + "px";
    c.style.height = c.offsetHeight + "px";
    c.textContent = "";
    this.reposition();

    this._complete = complete;
    this._sayWords(text, hold, complete);
  }

  show() {
    if (this._hidden) return;
    this._balloon.style.display = "block";
  }

  hide(fast) {
    if (fast) {
      this._balloon.style.display = "none";
      return;
    }

    this._hiding = window.setTimeout(this._finishHideBalloon.bind(this), this.CLOSE_BALLOON_DELAY);
  }

  _finishHideBalloon() {
    if (this._active) return;
    this._balloon.style.display = "none";
    this._hidden = true;
    this._hiding = null;
  }

  _sayWords(text, hold, complete) {
    this._active = true;
    this._hold = hold;
    const words = text.split(/( +|\n)/);
    const time = this.WORD_SPEAK_TIME;
    const el = this._content;
    let idx = 1;

    this._addWord = () => {
      if (!this._active) return;
      if (idx > words.length) {
        delete this._addWord;
        this._active = false;
        if (!this._hold) {
          complete();
          this.hide();
        }
      } else {
        el.textContent = words.slice(0, idx).join("");
        idx += 2;
        this._loop = window.setTimeout(this._addWord, time);
      }
    };

    this._addWord();
  }

  speakStream(complete) {
    this._hidden = false;
    this._active = true;
    this._hold = true;
    this._complete = complete;
    this.show();

    const c = this._content;
    c.style.height = "auto";
    c.style.width = "auto";
    c.textContent = "";
    this.reposition();

    let text = "";

    return {
      push: (chunk) => {
        text += chunk;
        c.textContent = text;
        c.style.height = "auto";
        c.style.width = "auto";
        const w = c.offsetWidth;
        c.style.width = w + "px";
        c.style.height = c.offsetHeight + "px";
        this.reposition();
      },
      done: () => {
        this._active = false;
        this._hold = false;
        complete();
        this.hide();
      },
    };
  }

  close() {
    if (this._active) {
      this._hold = false;
    } else if (this._hold) {
      this._complete();
    }
  }

  pause() {
    window.clearTimeout(this._loop);
    if (this._hiding) {
      window.clearTimeout(this._hiding);
      this._hiding = null;
    }
  }

  resume() {
    if (this._addWord) {
      this._addWord();
    } else if (!this._hold && !this._hidden) {
      this._hiding = window.setTimeout(
        this._finishHideBalloon.bind(this),
        this.CLOSE_BALLOON_DELAY,
      );
    }
  }

  dispose() {
    window.clearTimeout(this._loop);
    if (this._hiding) {
      window.clearTimeout(this._hiding);
      this._hiding = null;
    }
    this._active = false;
    this._addWord = undefined;
    this._balloon.remove();
  }
}
