const { Emitter, CompositeDisposable } = require('event-kit');

// Extended: History manager for remembering which projects have been opened.
//
// An instance of this class is always available as the `atom.history` global.
//
// The project history is used to enable the 'Reopen Project' menu.
class HistoryManager {
  constructor({ project, commands, stateStore }) {
    this.stateStore = stateStore;
    this.emitter = new Emitter();
    this.projects = [];
    this.disposables = new CompositeDisposable();
    this.disposables.add(
      commands.add(
        'atom-workspace',
        { 'application:clear-project-history': this.clearProjects.bind(this) },
        false
      )
    );
    this.disposables.add(
      project.onDidChangePaths(projectPaths => this.addProject(projectPaths))
    );
  }

  destroy() {
    this.disposables.dispose();
  }

  // Public: Obtain a list of previously opened projects.
  //
  // Returns an {Array} of {HistoryProject} objects, most recent first.
  getProjects() {
    return this.projects.map(p => new HistoryProject(p.paths, p.lastOpened));
  }

  // Public: Clear all projects from the history.
  //
  // Note: This is not a privacy function - other traces will still exist,
  // e.g. window state.
  //
  // Return a {Promise} that resolves when the history has been successfully
  // cleared.
  async clearProjects() {
    this.projects = [];
    await this.saveState();
    this.didChangeProjects();
  }

  // Public: Invoke the given callback when the list of projects changes.
  //
  // * `callback` {Function}
  //
  // Returns a {Disposable} on which `.dispose()` can be called to unsubscribe.
  onDidChangeProjects(callback) {
    return this.emitter.on('did-change-projects', callback);
  }

  didChangeProjects(args = { reloaded: false }) {
    this.emitter.emit('did-change-projects', args);
  }

  async addProject(paths, lastOpened) {
    if (paths.length === 0) return;

    let project = this.getProject(paths);
    if (!project) {
      project = new HistoryProject(paths);
      this.projects.push(project);
    }
    project.lastOpened = lastOpened || new Date();
    this.projects.sort((a, b) => b.lastOpened - a.lastOpened);

    await this.saveState();
    this.didChangeProjects();
  }

  async removeProject(paths) {
    if (paths.length === 0) return;

    let project = this.getProject(paths);
    if (!project) return;

    let index = this.projects.indexOf(project);
    this.projects.splice(index, 1);

    await this.saveState();
    this.didChangeProjects();
  }

  getProject(paths) {
    for (let i = 0; i < this.projects.length; i++) {
      if (arrayEquivalent(paths, this.projects[i].paths)) {
        return this.projects[i];
      }
    }

    return null;
  }

  async loadState() {
    const history = await this.stateStore.load('history-manager');
    if (history && history.projects) {
      this.projects = history.projects
        .filter(p => Array.isArray(p.paths) && p.paths.length > 0)
        .map(p => new HistoryProject(p.paths, new Date(p.lastOpened)));
      this.didChangeProjects({ reloaded: true });
    } else {
      this.projects = [];
    }
  }

  async saveState() {
    const projects = this.projects.map(p => ({
      paths: p.paths,
      lastOpened: p.lastOpened
    }));
    await this.stateStore.save('history-manager', { projects });
  }
}

function arrayEquivalent(a, b) {
  if (a.length !== b.length) return false;
  for (let i = 0; i < a.length; i++) {
    if (a[i] !== b[i]) return false;
  }
  return true;
}

class HistoryProject {
  constructor(paths, lastOpened) {
    this.paths = paths;
    this.lastOpened = lastOpened || new Date();
  }

  set paths(paths) {
    this._paths = paths;
  }
  get paths() {
    return this._paths;
  }

  set lastOpened(lastOpened) {
    this._lastOpened = lastOpened;
  }
  get lastOpened() {
    return this._lastOpened;
  }
}

module.exports = { HistoryManager, HistoryProject };
