_ = require 'underscore-plus'
CustomGutterComponent = require './custom-gutter-component'
LineNumberGutterComponent = require './line-number-gutter-component'

# The GutterContainerComponent manages the GutterComponents of a particular
# TextEditorComponent.

module.exports =
class GutterContainerComponent
  constructor: ({@onLineNumberGutterMouseDown, @editor, @domElementPool, @views}) ->
    # An array of objects of the form: {name: {String}, component: {Object}}
    @gutterComponents = []
    @gutterComponentsByGutterName = {}
    @lineNumberGutterComponent = null

    @domNode = document.createElement('div')
    @domNode.classList.add('gutter-container')
    @domNode.style.display = 'flex'

  destroy: ->
    for {name, component} in @gutterComponents
      component.destroy?()
    return

  getDomNode: ->
    @domNode

  getLineNumberGutterComponent: ->
    @lineNumberGutterComponent

  updateSync: (state) ->
    # The GutterContainerComponent expects the gutters to be sorted in the order
    # they should appear.
    newState = state.gutters

    newGutterComponents = []
    newGutterComponentsByGutterName = {}
    for {gutter, visible, styles, content} in newState
      gutterComponent = @gutterComponentsByGutterName[gutter.name]
      if not gutterComponent
        if gutter.name is 'line-number'
          gutterComponent = new LineNumberGutterComponent({onMouseDown: @onLineNumberGutterMouseDown, @editor, gutter, @domElementPool, @views})
          @lineNumberGutterComponent = gutterComponent
        else
          gutterComponent = new CustomGutterComponent({gutter, @views})

      if visible then gutterComponent.showNode() else gutterComponent.hideNode()
      # Pass the gutter only the state that it needs.
      if gutter.name is 'line-number'
        # For ease of use in the line number gutter component, set the shared
        # 'styles' as a field under the 'content'.
        gutterSubstate = _.clone(content)
        gutterSubstate.styles = styles
      else
        # Custom gutter 'content' is keyed on gutter name, so we cannot set
        # 'styles' as a subfield directly under it.
        gutterSubstate = {content, styles}
      gutterComponent.updateSync(gutterSubstate)

      newGutterComponents.push({
        name: gutter.name,
        component: gutterComponent,
      })
      newGutterComponentsByGutterName[gutter.name] = gutterComponent

    @reorderGutters(newGutterComponents, newGutterComponentsByGutterName)

    @gutterComponents = newGutterComponents
    @gutterComponentsByGutterName = newGutterComponentsByGutterName

  ###
  Section: Private Methods
  ###

  reorderGutters: (newGutterComponents, newGutterComponentsByGutterName) ->
    # First, insert new gutters into the DOM.
    indexInOldGutters = 0
    oldGuttersLength = @gutterComponents.length

    for gutterComponentDescription in newGutterComponents
      gutterComponent = gutterComponentDescription.component
      gutterName = gutterComponentDescription.name

      if @gutterComponentsByGutterName[gutterName]
        # If the gutter existed previously, we first try to move the cursor to
        # the point at which it occurs in the previous gutters.
        matchingGutterFound = false
        while indexInOldGutters < oldGuttersLength
          existingGutterComponentDescription = @gutterComponents[indexInOldGutters]
          existingGutterComponent = existingGutterComponentDescription.component
          indexInOldGutters++
          if existingGutterComponent is gutterComponent
            matchingGutterFound = true
            break
        if not matchingGutterFound
          # If we've reached this point, the gutter previously existed, but its
          # position has moved. Remove it from the DOM and re-insert it.
          gutterComponent.getDomNode().remove()
          @domNode.appendChild(gutterComponent.getDomNode())

      else
        if indexInOldGutters is oldGuttersLength
          @domNode.appendChild(gutterComponent.getDomNode())
        else
          @domNode.insertBefore(gutterComponent.getDomNode(), @domNode.children[indexInOldGutters])

    # Remove any gutters that were not present in the new gutters state.
    for gutterComponentDescription in @gutterComponents
      if not newGutterComponentsByGutterName[gutterComponentDescription.name]
        gutterComponent = gutterComponentDescription.component
        gutterComponent.getDomNode().remove()
