class Form {
  static autoInit() {
    CreateContributionForm.autoInit();
    CreateCorpusFileForm.autoInit();
    CreateJobForm.autoInit();
  }

  constructor(formElement) {
    this.formElement = formElement;
    this.eventListeners = {
      'requestLoad': []
    };
    this.afterRequestListeners = [];

    for (let selectElement of this.formElement.querySelectorAll('select')) {
      selectElement.removeAttribute('required');
    }

    this.formElement.addEventListener('submit', (event) => {
      event.preventDefault();
      this.submit(event);
    });
  }

  addEventListener(eventType, listener) {
    if (eventType in this.eventListeners) {
      this.eventListeners[eventType].push(listener);
    } else {
      throw `Unknown event type ${eventType}`;
    }
  }

  submit(event) {
    let request = new XMLHttpRequest();
    let modalElement = Utils.HTMLToElement(
      `
        <div class="modal">
          <div class="modal-content">
            <h4><i class="material-icons left">file_upload</i>Submitting...</h4>
            <div class="progress">
              <div class="determinate" style="width: 0%"></div>
            </div>
          </div>
          <div class="modal-footer">
            <a class="action-button btn red waves-effect waves-light modal-close" data-action="cancel">Cancel</a>
          </div>
        </div>
      `
    );
    document.querySelector('#modals').appendChild(modalElement);
    let modal = M.Modal.init(
      modalElement,
      {
        dismissible: false,
        onCloseEnd: () => {
          modal.destroy();
          modalElement.remove();
        }
      }
    );
    modal.open();

    // Remove all previous helper text elements that indicate errors
    let errorHelperTextElements = this.formElement
      .querySelectorAll('.helper-text[data-helper-text-type="error"]');
    for (let errorHelperTextElement of errorHelperTextElements) {
      errorHelperTextElement.remove();
    }

    // Check if select elements are filled out properly
    for (let selectElement of this.formElement.querySelectorAll('select')) {
      if (selectElement.value === '') {
        let inputFieldElement = selectElement.closest('.input-field');
        let errorHelperTextElement = Utils.HTMLToElement(
          '<span class="helper-text error-color-text" data-helper-text-type="error">Please select an option.</span>'
        );
        inputFieldElement.appendChild(errorHelperTextElement);
        inputFieldElement.querySelector('.select-dropdown').classList.add('invalid');
        modal.close();
        return;
      }
    }

    // Setup abort handling
    let cancelElement = modalElement.querySelector('.action-button[data-action="cancel"]');
    cancelElement.addEventListener('click', (event) => {request.abort();});

    // Setup load handling (after the request completed)
    request.addEventListener('load', (event) => {
      for (let listener of this.eventListeners['requestLoad']) {
        listener(event);
      }
      if (request.status === 400) {
        let responseJson = JSON.parse(request.responseText);
        for (let [inputName, inputErrors] of Object.entries(responseJson.errors)) {
          let inputFieldElement = this.formElement
            .querySelector(`input[name$="${inputName}"], select[name$="${inputName}"]`)
            .closest('.input-field');
          for (let inputError of inputErrors) {
            let errorHelperTextElement = Utils.HTMLToElement(
              `<span class="helper-text error-color-text" data-helper-type="error">${inputError}</span>`
            );
            inputFieldElement.appendChild(errorHelperTextElement);
          }
        }
      }
      if (request.status === 500) {
        app.flash('Internal Server Error', 'error');
      }
      modal.close();
    });

    // Setup progress handling
    let progressBarElement = modalElement.querySelector('.progress > .determinate');
    request.upload.addEventListener('progress', (event) => {
      let progress = Math.floor(100 * event.loaded / event.total);
      progressBarElement.style.width = `${progress}%`;
    });

    request.open(this.formElement.method, this.formElement.action);
    request.setRequestHeader('Accept', 'application/json');
    let formData = new FormData(this.formElement);
    switch (this.formElement.enctype) {
      case 'application/x-www-form-urlencoded': {
        let urlSearchParams = new URLSearchParams(formData);
        request.send(urlSearchParams);
        break;
      }
      case 'multipart/form-data': {
        request.send(formData);
        break;
      }
      case 'text/plain': {
        throw 'enctype "text/plain" is not supported';
        break;
      }
      default: {
        break;
      }
    }
  }
}
