import { Disposable, Event, EventEmitter, FileDecoration, FileDecorationProvider, Uri, window } from 'vscode';
import { Repository } from './repository';
import { PijulResourceGroup } from './resource';

/**
 * A decoration provider which tells VS Code how to decorate the files in a Pijul repository
 */
export class PijulDecorationProvider implements FileDecorationProvider {
  private readonly onDidChangeDecorationsEmitter = new EventEmitter<Uri[]>();
  readonly onDidChangeFileDecorations: Event<Uri[]> = this.onDidChangeDecorationsEmitter.event;

  private readonly disposables: Disposable[] = [];
  private decorations = new Map<string, FileDecoration>();

  /**
   * Create a new PijulDecorationProvider instance
   * @param repository The repository decorations will be provided for
   */
  constructor (private readonly repository: Repository) {
    this.disposables.push(
      window.registerFileDecorationProvider(this),
      repository.onDidRefreshStatus(this.onDidRefreshStatus, this)
    );
  }

  /**
   * Event listener that is triggered when the Pijul repo state is refreshed
   */
  private onDidRefreshStatus (): void {
    const newDecorations = new Map<string, FileDecoration>();

    this.collectDecorationData(this.repository.changedGroup, newDecorations);
    this.collectDecorationData(this.repository.untrackedGroup, newDecorations);

    const uris = new Set([...this.decorations.keys()].concat([...newDecorations.keys()]));
    this.decorations = newDecorations;
    this.onDidChangeDecorationsEmitter.fire([...uris.values()].map(value => Uri.parse(value, true)));
  }

  /**
   * Take the decoration data from each item in a resource group and add it to the decorationMap
   * @param group The resource group decoration data will be taken from
   * @param decorationMap The decoration map the decoration data will be added to
   */
  private collectDecorationData (group: PijulResourceGroup, decorationMap: Map<string, FileDecoration>): void {
    for (const resource of group.resourceStates) {
      // TODO: Better handling of moves
      decorationMap.set(resource.resourceUri.toString(), resource.resourceDecoration);
    }
  }

  /**
   * Interface function which provides decorations to VS Code by looking up the file
   * URI in the decoration map.
   * @param uri The URI of the file VS Code is looking up decorations for
   */
  provideFileDecoration (uri: Uri): FileDecoration | undefined {
    return this.decorations.get(uri.toString());
  }

  /**
   * Dispose of all of the disposable resources
   */
  dispose (): void {
    this.disposables.forEach(d => d.dispose());
  }
}