/*
 * Copyright (C) 2019 Robert Bosch GmbH Copyright (C) 2019 Robert Bosch Manufacturing Solutions GmbH, Germany. All rights reserved.
 */

import { DataSource } from '@angular/cdk/table';
import { CollectionViewer } from '@angular/cdk/collections';
import { Observable, merge, BehaviorSubject, Subject } from 'rxjs';
import { map } from 'rxjs/operators';
import { BciColumnBrowserModel, ColumnBrowserComponent, SelectionChange } from '@bci-web-core/core';
import { FlatModel } from './column-browser-demo-data';

export class FlatDataSource implements DataSource<BciColumnBrowserModel> {
  columnBrowser: ColumnBrowserComponent;
  totalData: FlatModel[];

  selectionChange: Observable<{ level: number; item: FlatModel; hasChildren: boolean }>;

  private dataSubject = new BehaviorSubject<BciColumnBrowserModel[]>([]);

  private selectionChangeSubject = new Subject<{ level: number; item: FlatModel; hasChildren: boolean }>();

  public get data(): BciColumnBrowserModel[] {
    return this.dataSubject.value;
  }

  public set data(value: BciColumnBrowserModel[]) {
    this.dataSubject.next(value);
  }

  constructor() {
    this.selectionChange = this.selectionChangeSubject.asObservable();
  }

  connect(
    collectionViewer: CollectionViewer
  ): Observable<BciColumnBrowserModel[] | ReadonlyArray<BciColumnBrowserModel>> {
    this.columnBrowser.selectionChange.subscribe((selection: SelectionChange) => {
      const items = this.getChildren(selection.item.label, selection.columnIndex);
      this.addChildren(this.data, items, selection.item.label);
      this.selectionChangeSubject.next({
        level: selection.columnIndex,
        item: selection.selected ? selection.item.model : null,
        hasChildren: selection.item.children.length !== 0,
      });
      this.dataSubject.next(this.data);
    });
    return merge(collectionViewer.viewChange, this.dataSubject).pipe(map(() => this.data));
  }

  /* eslint-disable-next-line @typescript-eslint/no-unused-vars */
  disconnect(collectionViewer: CollectionViewer): void {
    this.selectionChangeSubject.next(undefined);
    this.selectionChangeSubject.complete();
  }

  addChildren(list: BciColumnBrowserModel[], items: BciColumnBrowserModel[], parent: string): void {
    list.forEach((item) => {
      if (item.label === parent) {
        item.children.push(...items);
      } else {
        this.addChildren(item.children, items, parent);
      }
    });
  }

  getChildren(parent: string, level: number): BciColumnBrowserModel[] {
    return this.totalData
      .filter((item) => item.parent === parent && item.level === level + 1)
      .map((item) => {
        return {
          label: item.name,
          model: item,
          children: [],
        };
      });
  }
}

export class DynamicDataSource implements DataSource<BciColumnBrowserModel> {
  columnBrowser: ColumnBrowserComponent;
  totalData: BciColumnBrowserModel[];

  public get data(): BciColumnBrowserModel[] {
    return this.dataSubject.value;
  }

  public set data(value: BciColumnBrowserModel[]) {
    this.dataSubject.next(value);
  }

  private dataSubject = new BehaviorSubject<BciColumnBrowserModel[]>([]);

  connect(collectionViewer: CollectionViewer): Observable<BciColumnBrowserModel[]> {
    this.columnBrowser.selectionChange.subscribe((selection: SelectionChange) => {
      this.data.forEach((item) => {
        const node = this.getNode(item, selection.item.model);
        if (node != null) {
          const items = this.getChildren(selection.item.label, selection.selectedPath);
          node.children = items || [];
        }
      });
      // delayed loading here. in a real world application this could be a http call
      setTimeout(() => {
        this.dataSubject.next(this.data);
      }, 800);
    });
    return merge(collectionViewer.viewChange, this.dataSubject.asObservable()).pipe(map(() => this.data));
  }

  /* eslint-disable-next-line @typescript-eslint/no-unused-vars */
  disconnect(collectionViewer: CollectionViewer): void {}

  getNode(node: BciColumnBrowserModel, selectedNode: FlatModel): BciColumnBrowserModel {
    if (node.model === selectedNode) {
      return node;
    }
    let result = null;
    for (let i = 0; result == null && i < node.children.length; i++) {
      result = this.getNode(node.children[i], selectedNode);
    }
    return result;
  }

  getChildren(itemName: string, items: BciColumnBrowserModel[]): BciColumnBrowserModel[] {
    for (let i = 0; i < items.length; i++) {
      if (items[i].label === itemName) {
        return items[i].children.map((item) => {
          return {
            label: item.label,
            children: [],
            model: item,
          };
        });
      }
      this.getChildren(itemName, items[i].children);
    }
  }
}
