export class SideNav {
  panning = false;
  menuOut = false;
  options: { width?: number, edge?: string, closeOnClick?: boolean };
  $nav: JQuery;
  $button: JQuery;
  $dragTarget: JQuery;

  private onResizeListener: EventListener;

  private removeMenu(restoreNav: boolean) {
    this.panning = false;
    this.menuOut = false;

    // Reenable scrolling
    $("body").css({
      overflow: "",
      width: ""
    });

    $("#sidenav-overlay").velocity({ opacity: 0 }, {
      duration: 200,
      queue: false, easing: "easeOutQuad",
      complete: function () {
        $(this).remove();
      }
    });

    if (this.options.edge === "left") {
      // Reset phantom div
      this.$dragTarget.css({ width: "", right: "", left: "0" });
      this.$nav.velocity(
        { "translateX": "-100%" },
        {
          duration: 200,
          queue: false,
          easing: "easeOutCubic",
          complete: () => {
            if (restoreNav === true) {
              // Restore Fixed sidenav
              this.$nav.removeAttr("style");
              this.$nav.css("width", this.options.width);
            }
          }

        });
    } else {
      // Reset phantom div
      this.$dragTarget.css({ width: "", right: "0", left: "" });
      this.$nav.velocity(
        { "translateX": "100%" },
        {
          duration: 200,
          queue: false,
          easing: "easeOutCubic",
          complete: () => {
            if (restoreNav === true) {
              // Restore Fixed sidenav
              this.$nav.removeAttr("style");
              this.$nav.css({
                "position": "absolute",
                "left": "calc(100% - " + this.options.width + "px)",
                "right": 0, "width": this.options.width
              });
            }
          }
        });
    }
  }

  private onPan(e: any) {
    if (e.gesture.pointerType === "touch") {

      let direction = e.gesture.direction;
      let x = e.gesture.center.x;
      let y = e.gesture.center.y;
      let velocityX = e.gesture.velocityX;

      // Disable Scrolling
      let $body = $("body");
      let oldWidth = $body.innerWidth();
      $body.css("overflow", "hidden");
      $body.width(oldWidth);

      // If overlay does not exist, create one and if it is clicked, close menu
      if ($("#sidenav-overlay").length === 0) {
        let overlay = $(`<div id="sidenav-overlay"></div>`);
        overlay.css("opacity", 0).click(function () {
          this.removeMenu(false);
        });
        $("body").append(overlay);
      }

      // Keep within boundaries
      if (this.options.edge === "left") {
        if (x > this.options.width) { x = this.options.width; }
        else if (x < 0) { x = 0; }
      }

      if (this.options.edge === "left") {
        // Left Direction
        if (x < (this.options.width / 2)) { this.menuOut = false; }
        // Right Direction
        else if (x >= (this.options.width / 2)) { this.menuOut = true; }
        this.$nav.css("transform", "translateX(" + (x - this.options.width) + "px)");
      }
      else {
        // Left Direction
        if (x < (window.innerWidth - this.options.width / 2)) {
          this.menuOut = true;
        }
        // Right Direction
        else if (x >= (window.innerWidth - this.options.width / 2)) {
          this.menuOut = false;
        }
        let rightPos = (x - this.options.width / 2);
        if (rightPos < 0) {
          rightPos = 0;
        }

        this.$nav.css("transform", "translateX(" + rightPos + "px)");
      }


      // Percentage overlay
      let overlayPerc: number;
      if (this.options.edge === "left") {
        overlayPerc = x / this.options.width;
        $("#sidenav-overlay").velocity({ opacity: overlayPerc }, { duration: 10, queue: false, easing: "easeOutQuad" });
      }
      else {
        overlayPerc = Math.abs((x - window.innerWidth) / this.options.width);
        $("#sidenav-overlay").velocity({ opacity: overlayPerc }, { duration: 10, queue: false, easing: "easeOutQuad" });
      }
    }
  }

  private onPanend(e: any) {
    if (e.gesture.pointerType === "touch") {
      let velocityX = e.gesture.velocityX;
      let x = e.gesture.center.x;
      let leftPos = x - this.options.width;
      let rightPos = x - this.options.width / 2;
      if (leftPos > 0) {
        leftPos = 0;
      }
      if (rightPos < 0) {
        rightPos = 0;
      }
      this.panning = false;

      if (this.options.edge === "left") {
        // If velocityX <= 0.3 then the user is flinging the menu closed so ignore this.menuOut
        if ((this.menuOut && velocityX <= 0.3) || velocityX < -0.5) {
          if (leftPos !== 0) {
            this.$nav.velocity({ "translateX": [0, leftPos] }, { duration: 300, queue: false, easing: "easeOutQuad" });
          }

          // this.$nav.css({"translateX": 0});
          $("#sidenav-overlay").velocity({ opacity: 1 }, { duration: 50, queue: false, easing: "easeOutQuad" });
          this.$dragTarget.css({ width: "50%", right: 0, left: "" });
        }
        else if (!this.menuOut || velocityX > 0.3) {
          // Enable Scrolling
          $("body").css({
            overflow: "",
            width: ""
          });
          // Slide menu closed
          this.$nav.velocity({ "translateX": [-1 * this.options.width - 10, leftPos] }, { duration: 200, queue: false, easing: "easeOutQuad" });
          $("#sidenav-overlay").velocity({ opacity: 0 }, {
            duration: 200, queue: false, easing: "easeOutQuad",
            complete: function () {
              $(this).remove();
            }
          });
          this.$dragTarget.css({ width: "10px", right: "", left: 0 });
        }
      }
      else {
        if ((this.menuOut && velocityX >= -0.3) || velocityX > 0.5) {
          this.$nav.velocity({ "translateX": [0, rightPos] }, { duration: 300, queue: false, easing: "easeOutQuad" });
          $("#sidenav-overlay").velocity({ opacity: 1 }, { duration: 50, queue: false, easing: "easeOutQuad" });
          this.$dragTarget.css({ width: "50%", right: "", left: 0 });
        }
        else if (!this.menuOut || velocityX < -0.3) {
          // Enable Scrolling
          $("body").css({
            overflow: "",
            width: ""
          });

          // Slide menu closed
          this.$nav.velocity({ "translateX": [this.options.width + 10, rightPos] }, { duration: 200, queue: false, easing: "easeOutQuad" });
          $("#sidenav-overlay").velocity({ opacity: 0 }, {
            duration: 200, queue: false, easing: "easeOutQuad",
            complete: function () {
              $(this).remove();
            }
          });
          this.$dragTarget.css({ width: "10px", right: 0, left: "" });
        }
      }

    }
  }

  private onResize(e: any) {
    if (window.innerWidth > 992) {
      // Close menu if window is resized bigger than 992 and user has fixed sidenav
      if ($("#sidenav-overlay").length !== 0 && this.menuOut) {
        this.removeMenu(true);
      }
      else {
        this.$nav.css("transform", "translateX(0%)");
      }
    }
    else if (this.menuOut === false) {
      if (this.options.edge === "left") {
        this.$nav.css("transform", "translateX(-100%)");
      } else {
        this.$nav.css("transform", "translateX(100%)");
      }

    }
    this.refreshCollapseButton();
  }

  public init(nav_id: string, options: { width?: number, edge?: string, closeOnClick?: boolean }) {
    let defaults = {
      width: 240,
      edge: "left",
      closeOnClick: false
    };
    this.options = $.extend(defaults, options);

    this.$nav = $("#" + nav_id);

    // Add Touch Area
    this.$dragTarget = $(`<div class="drag-target"></div>`);
    $("body").append(this.$dragTarget);

    if (options.edge === "left") {
      this.$nav.css("transform", "translateX(-100%)");
      this.$dragTarget.css({ "left": 0 }); // Add Touch Area
    } else {
      this.$nav.css({
        "transform": "translateX(100%)", "position": "absolute",
        "left": "calc(100% - " + this.options.width + "px)",
        "right": 0
      });
      this.$dragTarget.css({ "right": 0 }); // Add Touch Area
    }

    // If fixed sidenav, bring menu out
    if (this.$nav.hasClass("fixed")) {
      if (window.innerWidth > 992) {
        this.$nav.css("transform", "translateX(0)");
      }
    }
    this.refreshCollapseButton();
    // Window resize to reset on large screens fixed
    if (this.$nav.hasClass("fixed")) {
      this.onResizeListener = e => this.onResize(e);
      $(window).on("resize", this.onResizeListener);
    }

    // if closeOnClick, then add close event for all a tags in side sideNav
    if (options.closeOnClick === true) {
      this.$nav.on("click.itemclick", "a:not(.collapsible-header)", () => this.removeMenu(false));
    }

    this.$dragTarget.on("click", () => this.removeMenu(false));

    this.$dragTarget.hammer({
      prevent_default: false
    }).bind("pan", (e) => this.onPan(e)).bind("panend", (e) => this.onPanend(e));
  }

  public show() {
    if (this.menuOut === true) {
      this.menuOut = false;
      this.panning = false;
      this.removeMenu(false);
    } else {
      // Disable Scrolling
      let $body = $("body");
      let oldWidth = $body.innerWidth();
      $body.css("overflow", "hidden");
      $body.width(oldWidth);

      // Push current drag target on top of DOM tree
      $("body").append(this.$dragTarget);

      if (this.options.edge === "left") {
        this.$dragTarget.css({ width: "50%", right: 0, left: "" });
        this.$nav.velocity({ "translateX": [0, -1 * this.options.width] }, { duration: 300, queue: false, easing: "easeOutQuad" });
      }
      else {
        this.$dragTarget.css({ width: "50%", right: "", left: 0 });
        this.$nav.velocity({ "translateX": [0, this.options.width] }, { duration: 300, queue: false, easing: "easeOutQuad" });
      }

      let overlay = $(`<div id="sidenav-overlay"></div>`);
      overlay.css("opacity", 0);
      overlay.click(() => {
        this.menuOut = false;
        this.panning = false;
        this.removeMenu(false);
        overlay.velocity({ opacity: 0 }, {
          duration: 300, queue: false, easing: "easeOutQuad",
          complete: () => {
            overlay.remove();
            overlay = null;
          }
        });
      });
      $("body").append(overlay);
      overlay.velocity({ opacity: 1 }, {
        duration: 300, queue: false, easing: "easeOutQuad",
        complete: () => {
          this.menuOut = true;
          this.panning = false;
        }
      });
    }
  }

  public hide() {
    $("#sidenav-overlay").trigger("click");
  }

  public bind($button: JQuery) {
    this.$button = $button;
    this.$button.click(() => {
      this.show();
      return false;
    });
    this.refreshCollapseButton();
  }

  private refreshCollapseButton() {
    if (this.$button) {
      if (window.innerWidth > 992) {
        this.$button.css({ "visibility": "hidden", "opacity": "0" });
      } else {
        this.$button.css({ "visibility": "visible", "opacity": "1" });
      }
    }
  }

  public dispose() {
    if (this.onResizeListener) {
      $(window).off("resize", this.onResizeListener);
    }

    if (this.$dragTarget) {
      this.$dragTarget.remove();
      if (this.$dragTarget.data("hammer")) {
        this.$dragTarget.data("hammer").destroy();
      }
    }

    $("#sidenav-overlay").remove();
  }
}