import Emitter, { IEmitter } from '../Emitter';

export interface IGridScrollbars extends IEmitter {
  SCROLL: string;
  SCROLL_X: string;
  SCROLL_Y: string;
  scrollOffset: number;
  fit: string;
  pageEl: HTMLDivElement;
  mainEl: HTMLDivElement;
  scrollTrackX: HTMLDivElement;
  scrollTrackY: HTMLDivElement;
  update(fit?: string, scale?: number): void;
  destroy(): void;
}

export class GridScrollbars extends Emitter {
  public get SCROLL(): string {
    return 'scroll';
  }
  public get SCROLL_X(): string {
    return 'scroll-x';
  }
  public get SCROLL_Y(): string {
    return 'scroll-y';
  }
  public scrollOffset: number = 20;
  public scrollTrackX: HTMLDivElement;
  private scrollHandleX: HTMLDivElement;
  public scrollTrackY: HTMLDivElement;
  private scrollHandleY: HTMLDivElement;
  public fit: string = 'scroll';
  public scale: number = 1;

  private scrolling: boolean = false;
  private axis: string = '';
  private offset: number = 0;
  private start: number = 0;

  constructor(public pageEl: HTMLDivElement, public mainEl: HTMLDivElement) {
    super();
    this.mainEl.style.setProperty('overflow-x', 'hidden');
    this.mainEl.style.setProperty('overflow-y', 'hidden');

    this.scrollTrackX = document.createElement('div');
    this.scrollTrackX.classList.add('grid-scrollbar', 'grid-scrollbar-x');
    this.scrollTrackX.addEventListener('mousedown', this.onMouseDownTrack);
    this.scrollHandleX = document.createElement('div');
    this.scrollHandleX.classList.add('grid-scrollbar-handle', 'grid-scrollbar-handle-x');
    this.scrollTrackX.appendChild(this.scrollHandleX);
    this.scrollHandleX.addEventListener('mousedown', this.onMouseDown);

    this.scrollTrackY = document.createElement('div');
    this.scrollTrackY.classList.add('grid-scrollbar', 'grid-scrollbar-y');
    this.scrollTrackY.addEventListener('mousedown', this.onMouseDownTrack);
    this.scrollHandleY = document.createElement('div');
    this.scrollHandleY.classList.add('grid-scrollbar-handle', 'grid-scrollbar-handle-y');
    this.scrollTrackY.appendChild(this.scrollHandleY);
    this.scrollHandleY.addEventListener('mousedown', this.onMouseDown);

    this.mainEl.addEventListener('scroll', this.onScroll);
    // window.addEventListener('resize', this.onResize);
    this.pageEl.addEventListener('wheel', this.onMouseWheel);
    window.addEventListener('mousemove', this.onMouseMove);
    window.addEventListener('mouseup', this.onMouseUp);

    // this.update();
  }
  public update(fit?: string, scale: number = 1): void {
    if (fit) this.fit = fit;
    this.scale = scale;
    if (this.mainEl.scrollWidth > this.mainEl.clientWidth && this.fit !== 'width' && this.fit !== 'contain') {
      this.scrollTrackX.classList.remove('disabled');
      let size: number = (this.pageEl.clientWidth * this.mainEl.clientWidth) / this.mainEl.scrollWidth;
      // console.log(this.pageEl.clientWidth, this.mainEl.clientWidth / this.mainEl.scrollWidth);
      this.scrollHandleX.style.setProperty('width', `${size}px`);
    } else {
      this.scrollTrackX.classList.add('disabled');
    }
    if (this.mainEl.scrollHeight > this.mainEl.clientHeight && this.fit !== 'height' && this.fit !== 'contain') {
      this.scrollTrackY.classList.remove('disabled');
      let size: number = (this.pageEl.clientHeight * this.mainEl.clientHeight) / this.mainEl.scrollHeight;
      // let size: number = (this.pageEl.clientHeight / this.mainEl.scrollHeight) * this.pageEl.clientHeight;
      this.scrollHandleY.style.setProperty('height', `${size}px`);
    } else {
      this.scrollTrackY.classList.add('disabled');
    }
  }
  public destroy(): void {
    // window.removeEventListener('resize', this.onResize);
    this.scrollTrackX.removeEventListener('mousedown', this.onMouseDownTrack);
    this.scrollHandleX.removeEventListener('mousedown', this.onMouseDown);
    this.scrollTrackY.removeEventListener('mousedown', this.onMouseDownTrack);
    this.scrollHandleY.removeEventListener('mousedown', this.onMouseDown);

    this.pageEl.removeEventListener('wheel', this.onMouseWheel);
    window.removeEventListener('mousemove', this.onMouseMove);
    window.removeEventListener('mouseup', this.onMouseUp);
  }
  private onScroll = (evt: any) => {
    this.updateHandles();
    this.emit(this.SCROLL_X);
    this.emit(this.SCROLL_Y);
  };
  private onResize = (evt: any) => {
    // this.update();
  };
  private updateHandles(): void {
    let posX: number = (this.pageEl.clientWidth * this.mainEl.scrollLeft) / this.mainEl.scrollWidth;
    this.scrollHandleX.style.setProperty('left', `${posX}px`);

    let posY: number = (this.pageEl.clientHeight * this.mainEl.scrollTop) / this.mainEl.scrollHeight;
    this.scrollHandleY.style.setProperty('top', `${posY}px`);
  }
  private onMouseWheel = (evt: WheelEvent) => {
    if (this.fit === 'contain') return;
    let offsetX = evt.deltaX ? (Math.abs(evt.deltaX) >= 100 ? Math.round(Math.abs(evt.deltaX) / 100) : 1) : 0;
    let offsetY = evt.deltaY ? (Math.abs(evt.deltaY) >= 100 ? Math.round(Math.abs(evt.deltaY) / 100) : 1) : 0;
    let directionX = evt.deltaX > 0 ? 1 : -1;
    let directionY = evt.deltaY > 0 ? 1 : -1;
    // console.log(offsetX, offsetY);
    if (this.scrollTrackY.classList.contains('disabled')) {
      if (this.fit !== 'width') this.mainEl.scrollLeft += 10 * directionY;
    } else {
      if (this.fit !== 'width') this.mainEl.scrollLeft += offsetX * this.scrollOffset * directionX;
      if (this.fit !== 'height') this.mainEl.scrollTop += offsetY * this.scrollOffset * directionY * (1 / this.scale);
    }
    if (
      (this.mainEl.scrollLeft > 0 && this.mainEl.scrollWidth - this.mainEl.scrollLeft > this.mainEl.clientWidth) ||
      (this.mainEl.scrollTop > 0 && this.mainEl.scrollHeight - this.mainEl.scrollTop > this.mainEl.clientHeight)
    ) {
      evt.preventDefault();
    }
  };
  private onMouseDownTrack = (evt: MouseEvent) => {
    // console.log('ow');
    let target: HTMLDivElement = evt.target as HTMLDivElement;
    let axis = target.classList.contains('grid-scrollbar-x') ? 'x' : 'y';
    let handle: HTMLDivElement = axis === 'x' ? this.scrollHandleX : this.scrollHandleY;
    let rect: ClientRect = handle.getBoundingClientRect();
    if (axis === 'x') {
      let scale = this.mainEl.scrollWidth / this.mainEl.clientWidth;
      this.mainEl.scrollLeft += handle.clientWidth * scale * (evt.x < rect.left ? -1 : 1);
    } else {
      let scale = this.mainEl.scrollHeight / this.mainEl.clientHeight;
      this.mainEl.scrollTop += (handle.clientHeight * scale * (evt.y < rect.top ? -1 : 1) * 1) / this.scale;
    }
  };
  private onMouseDown = (evt: MouseEvent) => {
    if (!evt.target) return;
    let target: HTMLDivElement = evt.target as HTMLDivElement;
    this.scrolling = true;
    this.axis = target.classList.contains('grid-scrollbar-handle-x') ? 'x' : 'y';
    this.start = this.axis === 'x' ? evt.x : evt.y;
    this.offset = this.axis === 'x' ? this.mainEl.scrollLeft : this.mainEl.scrollTop;
    this.pageEl.classList.add(`scrolling`);
    this.pageEl.classList.add(`scrolling-${this.axis}`);
    evt.stopPropagation();
  };
  private onMouseMove = (evt: MouseEvent) => {
    if (!this.scrolling) return;
    if (this.axis === 'x') {
      let offset = ((evt.x - this.start) * (this.mainEl.scrollWidth / this.pageEl.clientWidth)) ;
      // let offset = ((((evt.x - this.start) * this.mainEl.scrollWidth) / this.mainEl.clientWidth) * 1) / this.scale;
      // offset *= this.mainEl.scrollWidth / this.mainEl.clientWidth;
      this.mainEl.scrollLeft += offset;
      this.emit(this.SCROLL_X);
      this.start = evt.x;
    }
    if (this.axis === 'y') {
      let offset = ((evt.y - this.start) * (this.mainEl.scrollHeight / this.pageEl.clientHeight)) ;
      this.mainEl.scrollTop += offset;
      this.emit(this.SCROLL_Y);
      this.start = evt.y;
    }
  };
  private onMouseUp = (evt: MouseEvent) => {
    this.scrolling = false;
    this.pageEl.classList.remove('scrolling');
    this.pageEl.classList.remove('scrolling-x');
    this.pageEl.classList.remove('scrolling-y');
  };
}
