import {
  ElementRef,
  AfterViewInit,
  Directive,
  Host,
  Optional,
  Renderer2,
  Self,
  ViewContainerRef,
  Input,
  HostListener,
  AfterContentChecked,
  OnChanges
} from "@angular/core";
import { MatPaginator } from "@angular/material/paginator";
import { MatButton } from "@angular/material/button";

interface PageObject {
  length: number;
  pageIndex: number;
  pageSize: number;
  previousPageIndex: number;
}


@Directive({
  selector: '[CustomMatPagination]'
})
export class CustomMatPaginationDirective implements OnChanges {
  private _pageGapTxt = "...";
  private _rangeStart!: number;
  private _rangeEnd!: number;
  private _buttons = [];
  private _curPageObj: PageObject = {
    length: 0,
    pageIndex: 0,
    pageSize: 0,
    previousPageIndex: 0
  };
  private numberOfRecord!: number;
  @Input()
  length!: number;
  private paginationToShow!: number;
  @Input()
  pageSize!: number;
  private showPages!: boolean;
  @Input() showPageNumbers !: boolean;
  @Input()
  get showTotalPages(): number {
    return this._showTotalPages;
  }
  set showTotalPages(value: number) {
    this._showTotalPages = value % 2 == 0 ? value + 1 : value;
  }
  private _showTotalPages = 2;
  private _noOfRec: any;
  get inc(): number {
    return this._showTotalPages % 2 == 0
      ? this.showTotalPages / 2
      : (this.showTotalPages - 1) / 2;
  }

  get numOfPages(): number {
    return this.matPag.getNumberOfPages();
  }

  get lastPageIndex(): number {
    return this.matPag.getNumberOfPages() - 1;
  }

  ngOnChanges() {
    if (this.numberOfRecord !== this.length) {

      this.numberOfRecord = this.length;
      this.paginationToShow = this.pageSize;

      const paginationNotToDisplay = this.vr.element.nativeElement.querySelector(
        "div.mat-paginator-outer-container"
      );
      
      if (this.numberOfRecord <= this.paginationToShow) {
        paginationNotToDisplay.childNodes[0].style.visibility = "hidden";
        this.vr.element.nativeElement.style.borderTop="none"

      } else {
        paginationNotToDisplay.childNodes[0].style.visibility = "visible";
        this.vr.element.nativeElement.style.borderTop=""

      }
      this.buildPageNumbers();
    }
  }


  constructor(
    @Host() @Self() @Optional() private readonly matPag: MatPaginator,
    private vr: ViewContainerRef,
    private ren: Renderer2
  ) {

    //to rerender buttons on items per page change and first, last, next and prior buttons
    this.matPag.page.subscribe((e: PageObject) => {
      if (this._curPageObj.pageSize != e.pageSize &&
        this._curPageObj.pageIndex != 0
      ) {
        e.pageIndex = 0;
        this._rangeStart = 0;
        this._rangeEnd = this._showTotalPages - 1;
      }
      this._curPageObj = e;

      this.initPageRange();
    });
  }


  private buildPageNumbers() {
    const actionContainer = this.vr.element.nativeElement.querySelector(
      "div.mat-paginator-range-actions"
    );
    const nextPageNode = this.vr.element.nativeElement.querySelector(
      "button.mat-paginator-navigation-next"
    );
    const prevButtonCount = this._buttons.length;

    // remove buttons before creating new ones
    if (this._buttons.length > 0) {
      this._buttons.forEach(button => {
        this.ren.removeChild(actionContainer, button);
      });
      //Empty state array
      this._buttons.length = 0;
    }

    //initialize next page and last page buttons
    if (this._buttons.length == 0 && this.vr.element.nativeElement.childNodes[0].childNodes[0]
      .childNodes[2]) {
      let nodeArray = this.vr.element.nativeElement.childNodes[0].childNodes[0]
        .childNodes[2].childNodes;
      setTimeout(() => {
        for (let i = 0; i < nodeArray.length; i++) {
          if (nodeArray[i].nodeName === "BUTTON") {
            if (nodeArray[i].innerHTML.length > 100 && nodeArray[i].disabled) {
              // this.ren.setStyle( nodeArray[i], "background-color", "rgb(248 248 248)" );
              // this.ren.setStyle(nodeArray[i], "color", "rgb(76 79 83)");
              // this.ren.setStyle(nodeArray[i], "margin", ".5% 2px");
            } else if (
              nodeArray[i].innerHTML.length > 100 &&
              !nodeArray[i].disabled
            ) {
              // this.ren.setStyle( nodeArray[i], "color", "rgb(0 0 0)" );

              // this.ren.setStyle(nodeArray[i], "color", "black");
              // this.ren.setStyle(nodeArray[i], "margin", ".5% 2px");
              // this.ren.setStyle(nodeArray[i], "box-shadow", "0px 3px 5px -1px rgba(0, 0, 0, 0.2), 0px 6px 10px 0px rgba(0, 0, 0, 0.14), 0px 1px 18px 0px rgba(0, 0, 0, 0.12)");


            } else if (nodeArray[i].disabled) {
              // this.ren.setStyle(nodeArray[i], "background-color", "rgb(50, 51, 84)");
              // this.ren.setStyle(nodeArray[i], "color", "white");
            }
          }
        }
      });
    }
    // setTimeout(() => {

      this.showPages  = this.showPageNumbers;

  if(typeof this.showPageNumbers === 'undefined'){
    this.showPages = true;
  }
      if(this.showPages){
    for (let i = 0; i < this.numOfPages; i++) {
      if (i >= this._rangeStart && i <= this._rangeEnd) {
        this.ren.insertBefore(
          actionContainer,
          this.createButton(i, this.matPag.pageIndex),
          nextPageNode
        );
      }
      // this code is to show three ... button at end of next Button
      if (i == this._rangeEnd) {
          this.ren.insertBefore(
              actionContainer,
              this.createButton(this._pageGapTxt, this._rangeEnd),
              nextPageNode
          );
      }
    }
  }
    // }, 200)

  }

  private createButton(i: any, pageIndex: number): any {
    const linkBtn: MatButton = this.ren.createElement("button");
    
    this.ren.addClass(linkBtn, "c-button_pagenation");

    // this.ren.setStyle(linkBtn, "margin", "1%");
    this.ren.setStyle(linkBtn, "background-color", "white");

    const pagingTxt = isNaN(i) ? this._pageGapTxt : +(i + 1);
    const text = this.ren.createText(pagingTxt + "");

    this.ren.addClass(linkBtn, "mat-custom-page");
    switch (i) {
      case pageIndex:
        this.ren.setAttribute(linkBtn, "disabled", "disabled");
        this.ren.setStyle(linkBtn, "background-color", "rgb(50 51 84)");
        this.ren.setStyle(linkBtn, "color", "white");
        break;
      case this._pageGapTxt:
        let newIndex = this._curPageObj.pageIndex + this._showTotalPages;

        if (newIndex >= this.numOfPages) newIndex = this.lastPageIndex;

        if (pageIndex != this.lastPageIndex) {
          this.ren.listen(linkBtn, "click", () => {
            this.switchPage(newIndex);
          });
        }

        if (pageIndex == this.lastPageIndex) {
          this.ren.setAttribute(linkBtn, "disabled", "disabled");
          this.ren.setStyle(linkBtn, "display", "none");
        }
        break;
      default:
        this.ren.listen(linkBtn, "click", () => {
          this.switchPage(i);
        });
        break;
    }

    this.ren.appendChild(linkBtn, text);
    //Add button to private array for state
     //@ts-ignore
    this._buttons.push(linkBtn);
    return linkBtn;
  }
  //calculates the button range based on class input parameters and based on current page index value. Used to render new buttons after event.
  private initPageRange(): void {
    const middleIndex = (this._rangeStart + this._rangeEnd) / 2;

    this._rangeStart = this.calcRangeStart(middleIndex);
    this._rangeEnd = this.calcRangeEnd(middleIndex);

    this.buildPageNumbers();
  }

  //Helper function To calculate start of button range
  private calcRangeStart(middleIndex: number): number {
    switch (true) {
      case this._curPageObj.pageIndex == 0 && this._rangeStart != 0:
        return 0;
      case this._curPageObj.pageIndex > this._rangeEnd:
        return this._curPageObj.pageIndex + this.inc > this.lastPageIndex
          ? this.lastPageIndex - this.inc * 2
          : this._curPageObj.pageIndex - this.inc;
      case this._curPageObj.pageIndex > this._curPageObj.previousPageIndex &&
        this._curPageObj.pageIndex > middleIndex &&
        this._rangeEnd < this.lastPageIndex:
        return this._rangeStart + 1;
      case this._curPageObj.pageIndex < this._curPageObj.previousPageIndex &&
        this._curPageObj.pageIndex < middleIndex &&
        this._rangeStart > 0:
        return this._rangeStart - 1;
      default:
        return this._rangeStart;
    }
  }
  //Helpter function to calculate end of button range
  private calcRangeEnd(middleIndex: number): number {
    switch (true) {
      case this._curPageObj.pageIndex == 0 &&
        this._rangeEnd != this._showTotalPages:
        return this._showTotalPages - 1;
      case this._curPageObj.pageIndex > this._rangeEnd:
        return this._curPageObj.pageIndex + this.inc > this.lastPageIndex
          ? this.lastPageIndex
          : this._curPageObj.pageIndex + 1;
      case this._curPageObj.pageIndex > this._curPageObj.previousPageIndex &&
        this._curPageObj.pageIndex > middleIndex &&
        this._rangeEnd < this.lastPageIndex:
        return this._rangeEnd + 1;
      case this._curPageObj.pageIndex < this._curPageObj.previousPageIndex &&
        this._curPageObj.pageIndex < middleIndex &&
        this._rangeStart >= 0 &&
        this._rangeEnd > this._showTotalPages - 1:
        return this._rangeEnd - 1;
      default:
        return this._rangeEnd;
    }
  }
  //Helper function to switch page on non first, last, next and previous buttons only.
  private switchPage(i: number): void {
    const previousPageIndex = this.matPag.pageIndex;
    this.matPag.pageIndex = i;
    this.matPag["_emitPageEvent"](previousPageIndex);
    this.initPageRange();
  }
  //Initialize default state after view init
  public ngAfterViewInit() {
    this._rangeStart = 0;
    this._rangeEnd = this._showTotalPages - 1;
    this.initPageRange();
  }
}
