import { printBytecodeOutput, printParserOutput, runCode } from '#editor/runCode'
import { EditorState } from '@codemirror/state'
import { keymap } from '@codemirror/view'

let multilineMode = false

const customKeymap = keymap.of([
  {
    key: 'Enter',
    run: (view) => {
      if (multilineMode) return false

      const input = view.state.doc.toString()
      history.push(input)
      runCode(input)
      view.dispatch({
        changes: { from: 0, to: view.state.doc.length, insert: '' },
        selection: { anchor: 0 },
      })
      return true
    },
  },

  {
    key: 'Shift-Enter',
    run: (view) => {
      if (multilineMode) {
        const input = view.state.doc.toString()
        runCode(input)
        return true
      }

      multilineMode = true
      view.dispatch({
        changes: { from: view.state.doc.length, insert: '\n' },
        selection: { anchor: view.state.doc.length + 1 },
      })

      return true
    },
  },

  {
    key: 'Tab',
    preventDefault: true,
    run: (view) => {
      return true
    },
  },

  {
    key: 'ArrowUp',
    run: (view) => {
      const command = history.previous()
      if (command === undefined) return false
      view.dispatch({
        changes: { from: 0, to: view.state.doc.length, insert: command },
        selection: { anchor: command.length },
      })
      return true
    },
  },

  {
    key: 'ArrowDown',
    run: (view) => {
      const command = history.next()
      if (command === undefined) return false
      view.dispatch({
        changes: { from: 0, to: view.state.doc.length, insert: command },
        selection: { anchor: command.length },
      })
      return true
    },
  },

  {
    key: 'Mod-k 1',
    preventDefault: true,
    run: (view) => {
      const input = view.state.doc.toString()
      printParserOutput(input)

      return true
    },
  },

  {
    key: 'Mod-k 2',
    preventDefault: true,
    run: (view) => {
      const input = view.state.doc.toString()
      printBytecodeOutput(input)

      return true
    },
  },
])

let firstTime = true
const singleLineFilter = EditorState.transactionFilter.of((transaction) => {
  if (multilineMode) return transaction // Allow everything in multiline mode

  if (firstTime) {
    firstTime = false
    if (transaction.newDoc.toString().includes('\n')) {
      multilineMode = true
      return transaction
    }
  }

  transaction.changes.iterChanges((fromA, toA, fromB, toB, inserted) => {
    if (inserted.toString().includes('\n')) {
      multilineMode = true
      return
    }
  })

  return transaction
})

export const shrimpKeymap = [customKeymap, singleLineFilter]

class History {
  private commands: string[] = []
  private index: number | undefined

  push(command: string) {
    this.commands.push(command)
    this.index = undefined
  }

  previous(): string | undefined {
    if (this.commands.length === 0) return

    if (this.index === undefined) {
      this.index = this.commands.length - 1
    } else if (this.index > 0) {
      this.index -= 1
    }

    return this.commands[this.index]
  }

  next(): string | undefined {
    if (this.commands.length === 0 || this.index === undefined) return

    if (this.index < this.commands.length - 1) {
      this.index += 1
      return this.commands[this.index]
    } else {
      this.index = undefined
      return ''
    }
  }
}

const history = new History()
