import { GlobalLoaderService } from '../../../resources/global-loader/global-loader-service';
import { bindingMode, Disposable, observable } from "aurelia-binding";
import { autoinject, bindable, children, customElement, processContent } from "aurelia-framework";
import * as breeze from 'breeze-client';
import { UIInternal } from '../../../utils/ui-internal';
import { ServiceBase } from '../../../data/service-base';

@customElement('ui-table-pagination')
@autoinject
@children({ name: 'columns', selector: ['ui-column', 'ui-table-action'] })
@processContent((compiler, resources, node, instruction) => {
  instruction.inheritBindingContext = true;
  return true;
})
export class UiTablePagination {
  @bindable({ defaultBindingMode: bindingMode.oneTime }) public rowClassComputedFrom: string | string[] = [];
  @bindable() public rowClass: string | ((record: KeyValue, isDesktopScreen: boolean) => string);
  @bindable() public onRowClicked: (obj: { record: KeyValue }) => void;
  @bindable() public service: ServiceBase<breeze.Entity>;
  @bindable() public afterLoad: (datas: KeyValue[]) => Promise<void> = null;
  @bindable() public sortBy: string = null;
  @bindable() public sortByDescending: boolean = false;
  @bindable() public nbRecordPerPage = 10;
  @bindable() public nbPagesToShow = 5;

  @bindable()
  public defaultSortProperty?: string;
  @bindable()
  public defaultSortOrder?: "asc" | "desc";
  @bindable()
  public filter: (record: KeyValue) => boolean = null;

  @bindable()
  protected dragDropFunction: (obj: { record: KeyValue, oldVal: number, newVal: number }) => void | Promise<void> | boolean | Promise<boolean>;
  @bindable()
  protected showCounter: boolean;
  @bindable()
  public checkable: boolean;
  @bindable()
  public selected: KeyValue[] = [];
  @bindable()
  public nbSelectedMax: number;
  @bindable()
  public designWithBorder: boolean = false;
  @bindable()
  public compact: boolean = false;

  @bindable()
  public rowRoute: (obj: { record: KeyValue }) => string;
  @bindable
  public cellAlign: "start" | "center" | "end" = "start";
  @bindable
  public headerAlign: "start" | "center" | "end" = "start";
  @bindable()
  public onRowSelected: (obj: object) => boolean;

  private datas: KeyValue[] = [];
  private dataToShow: KeyValue[] = [];
  @observable private currentPage = 1;
  private totalPages = 1;
  private disposables: Disposable[] = [];
  private columns = [];
  private customColumns = [];
  private pageLoaded: number[] = [];
  private forceReload = false;
  private nbDatas = 0;

  constructor(private globalLoaderService: GlobalLoaderService) { }

  public async bind() {
    if (this.service == null) {
      console.error("Service is mandatory in UiTablePagination");
      return;
    }
    await this.computeTotalPages();
    this.disposables.push(UIInternal.subscribe(UIInternal.EVT_UI_TABLE_PAGINATION_RELOAD, () => this.computeTotalPages()));
  }

  private async computeTotalPages() {
    if (this.isLoading) {
      return;
    }
    this.pageLoaded.splice(0);
    this.nbDatas = await this.service.getCount(this.service.gridDataSource.predicates, this.service.gridDataSource.queryParameters);
    if (this.nbRecordPerPage == 0) {
      this.nbRecordPerPage = 1;
    }
    this.totalPages = Math.ceil(this.nbDatas / this.nbRecordPerPage);
    this.datas = new Array(this.nbDatas);
    await this.setData();
  }

  public unbind() {
    this.disposables.forEach(x => x.dispose());
  }

  private isLoading = false;
  private async loadData() {
    if (!this.isLoading && this.totalPages > 0 && !this.pageLoaded.includes(this.currentPage)) {
      this.isLoading = true;
      this.globalLoaderService.show();
      const nbDataToSkip = (this.currentPage - 1) * this.nbRecordPerPage;

      let dataLoaded = await this.service.getEntities(
        this.service.gridDataSource.predicates,
        this.service.gridDataSource.expands,
        this.service.gridDataSource.queryParameters,
        {
          propertyPaths: this.sortBy,
          isDescending: this.sortByDescending,
        },
        {
          take: this.nbRecordPerPage,
          skip: nbDataToSkip
        },
      );

      if (this.forceReload) {
        this.datas.splice(0);
        this.datas.push(...dataLoaded);
      }
      else {
        this.datas.splice(nbDataToSkip, dataLoaded.length, ...dataLoaded);
      }
      this.pageLoaded.push(this.currentPage);

      if (this.afterLoad != null) {
        await this.afterLoad(dataLoaded);
      }
      this.globalLoaderService.hide();
      this.isLoading = false;
    }
  }

  private async setData() {
    await this.loadData();
    var dataToShowToAdd = this.datas.slice().splice((this.currentPage - 1) * this.nbRecordPerPage, this.nbRecordPerPage);
    if (this.forceReload) {
      this.dataToShow.splice(0);
      this.dataToShow.push(...dataToShowToAdd);
    }
    else {
      this.dataToShow.splice(0, this.dataToShow.length, ...dataToShowToAdd);
    }
  }
  
  public async refresh() {
    this.forceReload = true;
    this.currentPage = 1;
    this.pageLoaded.splice(0);
    await this.setData();
    this.forceReload = false;
  }

  public columnsChanged() {
    this.customColumns = this.columns;
  }

  public async currentPageChanged(newValue, oldValue) {
    if (oldValue != null && newValue != oldValue && !this.forceReload) {
      await this.setData();
    }
  }

  public goToLastPage() {
    this.currentPage = this.totalPages;
  }
}
