import { CUSTOM_PAGING_ATTACHED } from './custom-paging';
import { autoinject, TaskQueue } from 'aurelia-framework';
import { Datacontext } from './../../data/data-context';
import { I18N } from 'aurelia-i18n';
import { AgGridView, CsvExportConfig } from './../../model/common-model';
import { bindable, customElement, observable, BindingEngine } from 'aurelia-framework';
import { Column } from "ag-grid-community";
import { CustomLogger } from '../../modules/custom-logger';
import { Box } from '../../modules/box';
import { Router } from 'aurelia-router';
import { AuthService } from '../../security/auth-service';
import { computedFrom, Disposable } from 'aurelia-binding';
import { Config } from '../../config';
import { UIInternal } from '../../utils/ui-internal';
import { debounce } from 'debounce';
import { GridOptions } from './../../resources/elements/utilities/ag-grid-helpers';
import { autoAdjustColumnsSize } from './../../ag-grid/agUtils';

@autoinject
@customElement('custom-ag-grid')
export class CustomAgGrid {
  @bindable gridOptions: GridOptions;
  @bindable heightStyle: string;
  @bindable title: string;
  @bindable options: BindableGridOptions;
  @bindable addFn: Function;
  @bindable deleteFn: Function;
  @bindable canAdd: boolean = true;
  @bindable canDelete: boolean = true;
  @bindable canBack: boolean = false;
  @bindable canUseView: boolean = true;
  @bindable canUseFilter: boolean = true;
  @bindable canUseParameters: boolean = true;
  @bindable canResetFilters: boolean;
  @bindable canExport: boolean = true;
  @bindable agGridViewList: Array<AgGridView>;
  @bindable afterResetFilters: Function;
  @bindable afterSetAgGridView: (agGridView: IAgGridView) => void;
  @bindable rowCount: number;
  @bindable selectedElemCount: number;
  @bindable canShowAllView: boolean = false;
  @bindable showFilterBox: boolean;
  @bindable showViewBox: boolean;
  @bindable showParametersViewBox: boolean;
  @bindable csvExportConfig: CsvExportConfig;
  @bindable agGridViewModuleView: string;

  public gridHeaderBar: HTMLElement;
  public disposables: Array<Disposable> = [];
  private calculatedHeight: string;

  constructor(public bindingEngine: BindingEngine, private router: Router, private logger: CustomLogger, public i18n: I18N, private box: Box, private authService: AuthService, private datacontext: Datacontext, private config: Config, private element: Element, private taskQueue: TaskQueue) {
    this.showFilterBox = this.config.agGridConfig.showFilterBox;
    this.showViewBox = this.config.agGridConfig.showViewBox;
    this.showParametersViewBox = this.config.agGridConfig.showParametersViewBox;
  }

  @computedFrom("heightStyle")
  public get isHeightStyleProvided(): boolean {
    return this.heightStyle != null;
  }

  @computedFrom("rowCount")
  public get showRowsCount(): boolean {
    return this.rowCount != null;
  }

  @computedFrom("selectedElemCount")
  public get showSelectedElemCount(): boolean {
    return this.selectedElemCount != null;
  }

  public attached() {
    window.addEventListener('resize', debounce(() => {
      if (this.gridOptions.adjustColumns === true && this.gridOptions.api != null) {
        this.gridOptions.api.sizeColumnsToFit();
      }
      this.calculateBodyHeight();
    }));

    UIInternal.subscribe(CUSTOM_PAGING_ATTACHED, () => this.calculateBodyHeight());
    this.calculateBodyHeight();
  }

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

  public calculateBodyHeight(tries: number = 0) {
    // If heightStyle is provided through bind, we must keep it
    if (!this.isHeightStyleProvided) {
      let availableHeight = this.element.parentElement.clientHeight;

      // Ignore division by 0
      if (availableHeight <= 0) {
        this.calculatedHeight = 'height: 100px';
        return;
      }

      // Remove padding top/bottom
      let parentStyle = getComputedStyle(this.element.parentElement);
      availableHeight -= this.getPadding(parentStyle, 'top') + this.getPadding(parentStyle, 'bottom')

      let headerHeight = this.element.getElementsByTagName('ag-grid-aurelia')[0].offsetTop;
      let footerHeight = this.element.getElementsByClassName("grid-footer-bar")[0].offsetHeight;

      if (footerHeight <= 0 && tries < 5) {
        // If height is 0, footer might not be fully attached
        // Can only be the case if all of the following are true:
        // 1. There is only a single page
        // 2. showSelectedElemCount is False
        // 3. showRowsCount is False
        // 4. left-paging / right-paging slots are empty OR not rendered (custom content)
        this.taskQueue.queueTask(() => this.calculateBodyHeight(++tries));
      } else {
        this.calculatedHeight = `height: ${availableHeight - headerHeight - footerHeight}px`;
      }
    } else {
      this.calculatedHeight = this.heightStyle;
    }
  }

  private getPadding(styles: CSSStyleDeclaration, side: 'top' | 'bottom'): number {
    return parseFloat(styles.getPropertyValue(`padding-${side}`).replace(/[^0-9.]/g, ''));
  }

  public gridOptionsChanged(newValue: GridOptions, oldValue) {
    newValue.onDisplayedColumnsChanged = (ev) => {
      var cols = ev.columnApi.getColumns();
      this.options.refreshColumns(cols);
    };
    this.options = new BindableGridOptions(newValue);
  }

  public selectAll() {
    let show = this.options.columns.every(column => column.show);
    this.options.columns.forEach(column => {
      column.show = !show;
    });
    autoAdjustColumnsSize(this.gridOptions);
  }
}

class GridColumn {
  headerName: string;
  @observable
  show: boolean;
  hide: boolean;

  field: string;
  constructor(private gridOptions: GridOptions) { }
  showChanged(newValue, oldValue) {
    if (newValue != oldValue && oldValue != null) {
      this.gridOptions.columnApi.setColumnVisible(this.field, newValue);
      window.dispatchEvent(new Event('resize'));
    }
  }
}

export class BindableGridOptions {
  columnsSave: Array<GridColumn> = [];
  columns: Array<GridColumn> = [];

  constructor(private gridOptions: GridOptions) {
  }
  private initializeGridColumn(col: Column, gridOptions: GridOptions) {
    let colDef = col.getColDef();
    if (colDef.headerName != null && colDef.headerName.length > 0) {
      let column: GridColumn = new GridColumn(gridOptions);

      column.headerName = colDef.headerName;
      column.show = col.isVisible();
      column.hide = false;
      if (this.columnsSave && this.columnsSave.length > 0) {
        var columnSave = this.columnsSave.find(x => x.headerName == column.headerName);
        if (columnSave) {
          column.hide = columnSave.hide;
        }
      }

      column.field = col.getColId();
      this.columns.push(column);
    }
  }

  public refreshColumns(updatedColumns: Column[]) {
    this.columnsSave = this.columns;
    this.columns = [];
    this.gridOptions.columnApi.getColumnState().forEach(colState => {
      let column = this.gridOptions.columnApi.getColumn(colState.colId);

      this.initializeGridColumn(column, this.gridOptions);
    });
  }
}

export interface IAgGridView {
  agGridView: AgGridView
}