import { Component, Input, Output, EventEmitter } from '@angular/core';
import { Keys, selectRows, selectRowsBetween } from '../../utils';
import { SelectionType } from '../../types';

export interface Model {
  type: string;
  event: MouseEvent | KeyboardEvent;
  row: any;
  rowElement: any;
  cellElement: any;
  cellIndex: number;
}

@Component({
  selector: 'datatable-selection',
  template: `
    <ng-content></ng-content>
  `
})
export class DataTableSelectionComponent {

  @Input() rows: any[];
  @Input() selected: any[];
  @Input() selectEnabled: boolean;
  @Input() selectionType: SelectionType;
  @Input() rowIdentity: any;
  @Input() selectCheck: any;

  @Output() activate: EventEmitter<any> = new EventEmitter();
  @Output() select: EventEmitter<any> = new EventEmitter();

  prevIndex: number;

  selectRow(event: KeyboardEvent | MouseEvent, index: number, row: any): void {
    if (!this.selectEnabled) return;

    const chkbox = this.selectionType === SelectionType.checkbox;
    const multi = this.selectionType === SelectionType.multi;
    let selected: any[] = [];

    if (multi || chkbox) {
      if (event.shiftKey) {
        const newSelected = [...this.selected];
        selected = selectRowsBetween(
          newSelected, 
          this.rows, 
          index, 
          this.prevIndex, 
          this.getRowSelectedIdx.bind(this));
      } else if (!event.shiftKey) {
        selected.push(row);
      } else {
        const newSelected = [...this.selected];
        selected = selectRows(newSelected, row, this.getRowSelectedIdx.bind(this));
      }
    } else {
      selected.push(row);
    }

    if(this.selectCheck) {
      selected = selected.filter(this.selectCheck.bind(this));
    }

    this.selected.splice(0, this.selected.length);
    this.selected.push(...selected);
    
    this.prevIndex = index;

    this.select.emit({
      selected
    });
  }

  onActivate(model: Model, index: number): void {
    const { type, event, row } = model;
    const chkbox = this.selectionType === SelectionType.checkbox;
    const select = (!chkbox && (type === 'click' || type === 'dblclick')) || 
      (chkbox && type === 'checkbox');

    if(select) {
      this.selectRow(event, index, row);
    } else if(type === 'keydown') {
      if ((<KeyboardEvent>event).keyCode === Keys.return) {
        this.selectRow(event, index, row);
      } else {
        this.onKeyboardFocus(model);
      }
    }
    this.activate.emit(model);
  }

  onKeyboardFocus(model: Model): void {
    const { keyCode } = <KeyboardEvent>model.event;
    const shouldFocus =
      keyCode === Keys.up ||
      keyCode === Keys.down ||
      keyCode === Keys.right ||
      keyCode === Keys.left;

    if(shouldFocus) {
      const isCellSelection =
        this.selectionType === SelectionType.cell;

      if(!model.cellElement || !isCellSelection) {
        this.focusRow(model.rowElement, keyCode);
      } else if(isCellSelection) {
        this.focusCell(model.cellElement, model.rowElement, keyCode, model.cellIndex);
      }
    }
  }

  focusRow(rowElement: any, keyCode: number): void {
    const nextRowElement = this.getPrevNextRow(rowElement, keyCode);
    if(nextRowElement) nextRowElement.focus();
  }

  getPrevNextRow(rowElement: any, keyCode: number): any {
    const parentElement = rowElement.parentElement;

    if(parentElement) {
      let focusElement: HTMLElement;
      if(keyCode === Keys.up) {
        focusElement = parentElement.previousElementSibling;
      } else if(keyCode === Keys.down) {
        focusElement = parentElement.nextElementSibling;
      }

      if(focusElement && focusElement.children.length) {
        return focusElement.children[0];
      }
    }
  }

  focusCell(cellElement: any, rowElement: any, keyCode: number, cellIndex: number): void {
    let nextCellElement: HTMLElement;

    if(keyCode === Keys.left) {
      nextCellElement = cellElement.previousElementSibling;
    } else if(keyCode === Keys.right) {
      nextCellElement = cellElement.nextElementSibling;
    } else if(keyCode === Keys.up || keyCode === Keys.down) {
      const nextRowElement = this.getPrevNextRow(rowElement, keyCode);
      if(nextRowElement) {
        const children = nextRowElement.getElementsByClassName('datatable-body-cell');
        if(children.length) nextCellElement = children[cellIndex];
      }
    }

    if(nextCellElement) nextCellElement.focus();
  }

  getRowSelected(row: any): boolean {
    return this.getRowSelectedIdx(row, this.selected) > -1;
  }

  getRowSelectedIdx(row: any, selected: any[]): number {
    if(!selected || !selected.length) return -1;

    const rowId = this.rowIdentity(row);
    return selected.findIndex((r) => {
      const id = this.rowIdentity(r);
      return id === rowId;
    });
  }

}
