/* eslint-disable
    camelcase
 */
import EditorShareJsCodec from '../../../EditorShareJsCodec'
import 'ace/ace'
const { Range } = ace.require('ace/range')
class TrackChangesAdapter {
  constructor(editor) {
    this.editor = editor
    this.changeIdToMarkerIdMap = {}
  }

  tearDown() {
    this.changeIdToMarkerIdMap = {}
  }

  clearAnnotations() {
    const session = this.editor.getSession()
    for (const change_id in this.changeIdToMarkerIdMap) {
      const markers = this.changeIdToMarkerIdMap[change_id]
      for (const marker_name in markers) {
        const marker_id = markers[marker_name]
        session.removeMarker(marker_id)
      }
    }
    this.changeIdToMarkerIdMap = {}
  }

  onInsertAdded(change) {
    const start = this.shareJsOffsetToRowColumn(change.op.p)
    const end = this.shareJsOffsetToRowColumn(change.op.p + change.op.i.length)

    const session = this.editor.getSession()
    const background_range = new Range(
      start.row,
      start.column,
      end.row,
      end.column
    )
    const background_marker_id = session.addMarker(
      background_range,
      'track-changes-marker track-changes-added-marker',
      'text'
    )
    const callout_marker_id = this.createCalloutMarker(
      start,
      'track-changes-added-marker-callout'
    )

    this.changeIdToMarkerIdMap[change.id] = {
      background_marker_id,
      callout_marker_id,
    }
  }

  onDeleteAdded(change) {
    const position = this.shareJsOffsetToRowColumn(change.op.p)
    const session = this.editor.getSession()

    const markerLayer = this.editor.renderer.$markerBack
    const klass = 'track-changes-marker track-changes-deleted-marker'
    const background_range = this.makeZeroWidthRange(position)
    const background_marker_id = session.addMarker(
      background_range,
      klass,
      (html, range, left, top, config) =>
        markerLayer.drawSingleLineMarker(
          html,
          range,
          `${klass} ace_start`,
          config,
          0,
          ''
        )
    )

    const callout_marker_id = this.createCalloutMarker(
      position,
      'track-changes-deleted-marker-callout'
    )

    this.changeIdToMarkerIdMap[change.id] = {
      background_marker_id,
      callout_marker_id,
    }
  }

  onInsertRemoved(change) {
    if (this.changeIdToMarkerIdMap[change.id] == null) {
      return
    }
    const { background_marker_id, callout_marker_id } =
      this.changeIdToMarkerIdMap[change.id]
    delete this.changeIdToMarkerIdMap[change.id]
    const session = this.editor.getSession()
    session.removeMarker(background_marker_id)
    session.removeMarker(callout_marker_id)
  }

  onDeleteRemoved(change) {
    if (this.changeIdToMarkerIdMap[change.id] == null) {
      return
    }
    const { background_marker_id, callout_marker_id } =
      this.changeIdToMarkerIdMap[change.id]
    delete this.changeIdToMarkerIdMap[change.id]

    const session = this.editor.getSession()
    session.removeMarker(background_marker_id)
    session.removeMarker(callout_marker_id)
  }

  onChangeMoved(change) {
    let end
    const start = this.shareJsOffsetToRowColumn(change.op.p)
    if (change.op.i != null) {
      end = this.shareJsOffsetToRowColumn(change.op.p + change.op.i.length)
    } else {
      end = start
    }
    this.updateMarker(change.id, start, end)
  }

  onCommentAdded(comment) {
    if (this.changeIdToMarkerIdMap[comment.id] == null) {
      // Only create new markers if they don't already exist
      const start = this.shareJsOffsetToRowColumn(comment.op.p)
      const end = this.shareJsOffsetToRowColumn(
        comment.op.p + comment.op.c.length
      )
      const session = this.editor.getSession()
      const background_range = new Range(
        start.row,
        start.column,
        end.row,
        end.column
      )
      const background_marker_id = session.addMarker(
        background_range,
        'track-changes-marker track-changes-comment-marker',
        'text'
      )
      const callout_marker_id = this.createCalloutMarker(
        start,
        'track-changes-comment-marker-callout'
      )
      this.changeIdToMarkerIdMap[comment.id] = {
        background_marker_id,
        callout_marker_id,
      }
    }
  }

  onCommentMoved(comment) {
    const start = this.shareJsOffsetToRowColumn(comment.op.p)
    const end = this.shareJsOffsetToRowColumn(
      comment.op.p + comment.op.c.length
    )
    this.updateMarker(comment.id, start, end)
  }

  onCommentRemoved(comment) {
    if (this.changeIdToMarkerIdMap[comment.id] != null) {
      // Resolved comments may not have marker ids
      const { background_marker_id, callout_marker_id } =
        this.changeIdToMarkerIdMap[comment.id]
      delete this.changeIdToMarkerIdMap[comment.id]
      const session = this.editor.getSession()
      session.removeMarker(background_marker_id)
      session.removeMarker(callout_marker_id)
    }
  }

  updateMarker(change_id, start, end) {
    if (this.changeIdToMarkerIdMap[change_id] == null) {
      return
    }
    const session = this.editor.getSession()
    const markers = session.getMarkers()
    const { background_marker_id, callout_marker_id } =
      this.changeIdToMarkerIdMap[change_id]
    if (background_marker_id != null && markers[background_marker_id] != null) {
      const background_marker = markers[background_marker_id]
      background_marker.range.start = start
      background_marker.range.end = end
    }
    if (callout_marker_id != null && markers[callout_marker_id] != null) {
      const callout_marker = markers[callout_marker_id]
      callout_marker.range.start = start
      callout_marker.range.end = start
    }
  }

  shareJsOffsetToRowColumn(offset) {
    const lines = this.editor.getSession().getDocument().getAllLines()
    return EditorShareJsCodec.shareJsOffsetToRowColumn(offset, lines)
  }

  createCalloutMarker(position, klass) {
    const session = this.editor.getSession()
    const callout_range = this.makeZeroWidthRange(position)
    const markerLayer = this.editor.renderer.$markerBack
    return session.addMarker(
      callout_range,
      klass,
      (html, range, left, top, config) =>
        markerLayer.drawSingleLineMarker(
          html,
          range,
          `track-changes-marker-callout ${klass} ace_start`,
          config,
          0,
          'width: auto; right: 0;'
        )
    )
  }

  makeZeroWidthRange(position) {
    const ace_range = new Range(
      position.row,
      position.column,
      position.row,
      position.column
    )
    // Our delete marker is zero characters wide, but Ace doesn't draw ranges
    // that are empty. So we monkey patch the range to tell Ace it's not empty
    // We do want to claim to be empty if we're off screen after clipping rows
    //  though. This is the code we need to trick:
    //   var range = marker.range.clipRows(config.firstRow, config.lastRow);
    //   if (range.isEmpty()) continue;
    ace_range.clipRows = function (first_row, last_row) {
      this.isEmpty = function () {
        return first_row > this.end.row || last_row < this.start.row
      }
      return this
    }
    return ace_range
  }
}
export default TrackChangesAdapter
