import { Terminal } from '@xterm/xterm'
import { FitAddon } from '@xterm/addon-fit'
import { Mutex } from './Mutex.js'

/**
 * xterm.js based terminal output for the execution dialog.
 *
 * the xterm.js methods for write(), reset() and clear() appear to be async,
 * but they do not return a Promise and instead use a callback. When calling
 * these methods in quick succession, the output can get garbled due to race
 * conditions.
 *
 * To avoid this, this class uses Mutex around those methods to ensure that
 * only one write OR reset is executed at a time, is completed, and the calls
 * occour in sequential order.
 */
export class OutputTerminal {
  constructor (executionTrackingId) {
    this.executionTrackingId = executionTrackingId
    this.writeMutex = new Mutex()
    this.terminal = new Terminal({
      convertEol: true
    })

    const fitAddon = new FitAddon()
    this.terminal.loadAddon(fitAddon)
    this.terminal.fit = fitAddon
  }

  async write (out, then) {
    const unlock = await this.writeMutex.lock()

    try {
      await new Promise(resolve => {
        this.terminal.write(out, () => {
          resolve()
        })
      })
    } finally {
      unlock()

      if (then != null && then !== undefined) {
        then()
      }
    }
  }

  async reset () {
    const unlock = await this.writeMutex.lock()

    try {
      await new Promise(resolve => {
        this.terminal.clear()
        this.terminal.reset()
        resolve()
      })
    } finally {
      unlock()
    }
  }

  fit () {
    this.terminal.fit.fit()
  }

  open (el) {
    this.terminal.open(el)
  }

  close () {
    this.terminal.dispose()
  }

  resize (cols, rows) {
    this.terminal.resize(cols, rows)
  }
}
