import { Component, OnInit, Input, SimpleChanges, OnChanges, Output, EventEmitter } from '@angular/core';
import { NestedTreeControl } from '@angular/cdk/tree';
import { MatTreeNestedDataSource } from '@angular/material/tree';
import { FileSystemService, SerializedNode } from '../services/file-system/file-system.service';
import { PrettyPrintTorrentDataService } from '../services/pretty-print-torrent-data.service';
import DirectoryNode from '../services/file-system/FileSystemNodes/DirectoryNode';
import { ApplicationConfigService } from '../services/app/application-config.service';

@Component({
  selector: 'app-file-system-tree-explorer',
  templateUrl: './file-system-tree-explorer.component.html',
  styleUrls: ['./file-system-tree-explorer.component.scss']
})
export class FileSystemTreeExplorerComponent implements OnChanges {
  @Input() directories: SerializedNode[];
  @Input() showProgress: boolean = false;
  @Input() allowSetPriority: boolean = false;

  /** When user changes the priority */
  @Output() onPriorityChange = new EventEmitter<SerializedNode>();

  /** When user toggles the drop-down menu in order to choose a priority */
  @Output() onPriorityChangeToggled = new EventEmitter();

  public isLoading = true;

  /** Controls for tree components */
  public treeControl = new NestedTreeControl<SerializedNode>(node => node.children);
  public dataSource = new MatTreeNestedDataSource<SerializedNode>();

  public file_priorities = ApplicationConfigService.FILE_PRIORITY_OPTS;
  public file_priorities_mapping = ApplicationConfigService.FILE_PRIORITY_OPTS_MAPPING;

  private root: DirectoryNode;                           /** File System to keep track of the files in a torrent */

  private expanded_nodes: Set<string> = new Set<string>();
  private nodes_to_render: Set<string> = new Set<string>();

  /** Keep track of which branches to render, for performance reasons
   * Only the immediate children of nodes with this ID will render!
   */
  private parentsToRender: Set<string> = new Set<string>();

  constructor(private appConfig: ApplicationConfigService, private pp: PrettyPrintTorrentDataService) { }

  ngOnInit(): void {
    this._updateData();
  }

  ngOnChanges(changes: SimpleChanges) {
    if(changes.directories) {
      this.directories = changes.directories.currentValue;
      this._updateData();
    }

    if(changes.showProgress) { this.showProgress = changes.showProgress.currentValue; }
    if(changes.allowSetPriority) { this.allowSetPriority = changes.allowSetPriority.currentValue }
  }

  handleCheckboxClick(node: SerializedNode) {
    let o = node.priority;
    node.priority = o === 0 ? 1 : 0

    this.onPriorityChange.emit(node);
  }
  handleFilePriorityChange(node: SerializedNode) { this.onPriorityChange.emit(node); }
  handleFilePriorityToggled() { this.onPriorityChangeToggled.emit(''); }

  /** Refresh all filesystem data. This could potentially be an
   *  expensive operation.
   */
  private async _updateData(): Promise<void> {
    this.dataSource = new MatTreeNestedDataSource<SerializedNode>();
    this.dataSource.data = this.directories;

    /** Should render the root node, which basically shows the top-level torrents */
    this.nodes_to_render.add('');
  }

  public hasChild(_: number, node: SerializedNode) {
    return !!node.children && node.children.length > 0
  }

  toggleNode(node: SerializedNode): void {

    if(this.isExpanded(node)) {
      this.collapseNode(node);
    } else {
      this.expandNode(node);
    }
  }

  expandNode(node: SerializedNode): void {
    this.expanded_nodes.add(node.path);
    this.nodes_to_render.add(node.path);
  }

  collapseNode(node: SerializedNode): void {
    this.expanded_nodes.delete(node.path);
    /** Do not remove node from nodes_to_render! Want to keep it rendered
     * to avoid having to re-create it from subsequent collapse/expand
     */
  }

  collapseAllNodes(): void {
    this.expanded_nodes.clear();
  }

  isExpanded(node: SerializedNode): boolean {
    return this.expanded_nodes.has(node.path);
  }

  isParentRendered(node: SerializedNode): boolean {
    return this.nodes_to_render.has(node.parentPath);
  }

  getNodeSize(node: SerializedNode): string {
    return this.pp.pretty_print_file_size(node.size);
  }

  getNodeProgress(node: SerializedNode): string {
    return (node.progress * 100).toFixed(2);
  }

}