import Component from '@glimmer/component';
import { tracked } from '@glimmer/tracking';
import { action } from '@ember/object';
import { inject as service } from '@ember/service';

class State {
  constructor(name) {
    this.name = name;
  }
  matches(match) {
    return this.name === match;
  }
}

class Outlets {
  constructor() {
    this.map = new Map();
  }
  sort() {
    this.sorted = [...this.map.keys()];
    this.sorted.sort((a, b) => {
      const al = a.split('.').length;
      const bl = b.split('.').length;
      switch (true) {
        case al > bl:
          return -1;
        case al < bl:
          return 1;
        default:
          return 0;
      }
    });
  }
  set(name, value) {
    this.map.set(name, value);
    this.sort();
  }
  get(name) {
    return this.map.get(name);
  }
  delete(name) {
    this.map.delete(name);
    this.sort();
  }
  keys() {
    return this.sorted;
  }
}
const outlets = new Outlets();

export default class Outlet extends Component {
  @service('router') router;
  @service('dom') dom;

  @tracked route;
  @tracked state;
  @tracked previousState;

  constructor() {
    super(...arguments);
    if (this.args.name === 'application') {
      this.setAppState('loading');
      this.setAppRoute(this.router.currentRouteName);
    }
  }

  setAppRoute(name) {
    const nspace = 'nspace.';
    if (name.startsWith(nspace)) {
      name = name.substr(nspace.length);
    }
    if (name !== 'loading') {
      const doc = this.dom.root();
      if (doc.classList.contains('ember-loading')) {
        doc.classList.remove('ember-loading');
      }
      doc.dataset.route = name;
      this.setAppState('idle');
    }
  }

  setAppState(state) {
    this.dom.root().dataset.state = state;
  }

  setOutletRoutes(route) {
    const keys = [...outlets.keys()];
    const pos = keys.indexOf(this.name);
    const key = pos + 1;
    const parent = outlets.get(keys[key]);
    parent.route = this.args.name;

    this.route = route;
  }

  @action
  startLoad(transition) {
    const keys = [...outlets.keys()];

    const outlet =
      keys.find(item => {
        return transition.to.name.indexOf(item) !== -1;
      }) || 'application';

    if (this.args.name === outlet) {
      this.previousState = this.state;
      this.state = new State('loading');
    }
    if (this.args.name === 'application') {
      this.setAppState('loading');
    }
  }

  @action
  endLoad(transition) {
    if (this.state.matches('loading')) {
      this.setOutletRoutes(transition.to.name);

      this.previousState = this.state;
      this.state = new State('idle');
    }
    if (this.args.name === 'application') {
      this.setAppRoute(this.router.currentRouteName);
    }
  }

  @action
  connect() {
    outlets.set(this.args.name, this);
    this.previousState = this.state = new State('idle');
    this.router.on('routeWillChange', this.startLoad);
    this.router.on('routeDidChange', this.endLoad);
  }

  @action
  disconnect() {
    outlets.delete(this.args.name);
    this.router.off('routeWillChange', this.startLoad);
    this.router.off('routeDidChange', this.endLoad);
  }
}
