nopaque.corpus_analysis.App = class App {
  constructor(corpusId) {
    this.corpusId = corpusId;

    this.data = {};

    // HTML elements
    this.elements = {
      container: document.querySelector('#corpus-analysis-container'),
      extensionCards: document.querySelector('#corpus-analysis-extension-cards'),
      extensionTabs: document.querySelector('#corpus-analysis-extension-tabs'),
      initModal: document.querySelector('#corpus-analysis-init-modal')
    };
    // Materialize elements
    this.elements.m = {
      extensionTabs: M.Tabs.init(this.elements.extensionTabs),
      initModal: M.Modal.init(this.elements.initModal, {dismissible: false})
    };

    this.extensions = {};

    this.settings = {};
  }

  async init() {
    this.disableActionElements();
    this.elements.m.initModal.open();

    try {
      // Setup CQi over SocketIO connection and gather data from the CQPServer
      const statusTextElement = this.elements.initModal.querySelector('.status-text');
      statusTextElement.innerText = 'Creating CQi over SocketIO client...';
      const cqiClient = new nopaque.corpus_analysis.cqi.Client('/cqi_over_sio');
      statusTextElement.innerText += ' Done';
      statusTextElement.innerHTML = 'Waiting for the CQP server...';
      const response = await cqiClient.api.socket.emitWithAck('init', this.corpusId);
      if (response.code !== 200) {throw new Error();}
      statusTextElement.innerText += ' Done';
      statusTextElement.innerHTML = 'Connecting to the CQP server...';
      await cqiClient.connect('anonymous', '');
      statusTextElement.innerText += ' Done';
      statusTextElement.innerHTML = 'Building and receiving corpus data cache from the server (This may take a while)...';
      const cqiCorpus = await cqiClient.corpora.get(`NOPAQUE-${this.corpusId.toUpperCase()}`);
      statusTextElement.innerText += ' Done';
      // TODO: Don't do this hgere
      await cqiCorpus.updateDb();
      this.data.cqiClient = cqiClient;
      this.data.cqiCorpus = cqiCorpus;
      this.data.corpus = {o: cqiCorpus};  // legacy
      // Initialize extensions
      for (const extension of Object.values(this.extensions)) {
        statusTextElement.innerHTML = `Initializing ${extension.name} extension...`;
        await extension.init();
        statusTextElement.innerText += ' Done'
      }
    } catch (error) {
      let errorString = '';
      if ('code' in error && error.code !== undefined && error.code !== null) {
        errorString += `[${error.code}] `;
      }
      errorString += `${error.constructor.name}`;
      if ('description' in error && error.description !== undefined && error.description !== null) {
        errorString += `: ${error.description}`;
      }
      const errorsElement = this.elements.initModal.querySelector('.errors');
      const progressElement = this.elements.initModal.querySelector('.progress');
      errorsElement.innerText = errorString;
      errorsElement.classList.remove('hide');
      progressElement.classList.add('hide');
      return;
    }

    for (const extensionSelectorElement of this.elements.extensionCards.querySelectorAll('.extension-selector')) {
      extensionSelectorElement.addEventListener('click', () => {
        this.elements.m.extensionTabs.select(extensionSelectorElement.dataset.target);
      });
    }

    this.enableActionElements();
    this.elements.m.initModal.close();
  }

  registerExtension(extension) {
    if (extension.name in this.extensions) {return;}
    this.extensions[extension.name] = extension;
  }

  disableActionElements() {
    const actionElements = this.elements.container.querySelectorAll('.corpus-analysis-action');
    for (const actionElement of actionElements) {
      switch(actionElement.nodeName) {
        case 'INPUT':
          actionElement.disabled = true;
          break;
        case 'SELECT':
          actionElement.parentNode.querySelector('input.select-dropdown').disabled = true;
          break;
        default:
          actionElement.classList.add('disabled');
      }
    }
  }

  enableActionElements() {
    const actionElements = this.elements.container.querySelectorAll('.corpus-analysis-action');
    for (const actionElement of actionElements) {
      switch(actionElement.nodeName) {
        case 'INPUT':
          actionElement.disabled = false;
          break;
        case 'SELECT':
          actionElement.parentNode.querySelector('input.select-dropdown').disabled = false;
          break;
        default:
          actionElement.classList.remove('disabled');
      }
    }
  }
}