 _ = require 'underscore-plus'
 {CompositeDisposable} = require 'atom'
 {inspect} = require 'util'
+
+# Moving strategy
+# -------------------------
+# Two wise. correspoinding submode of visual-mode is below
+#  - Linewise: linewise, characterwise(in case some selection is multi-row)
+#  - Characterwise: characterwise(in case all selection is single-row), blockwise
+#
+# Four direction
+#   ['up', 'down', 'right', 'left']
+#
+# Movability
+#  up
+#    - Linewise: can't move upward if row is 0
+#    - Characterwise: can't move upward if top selection is at row 0
+#  down
+#    - Linewise: always movable since last row is automatically extended.
+#    - Characterwise: always movable since last row is automatically extended.
+#  right
+#    - Linewise: always movable since EOL is automatically extended.
+#    - Characterwise: always movable since EOL is automatically extended.
+#  left
+#    - Linewise: always true since indent/outdent command can handle it.
+#    - Characterwise: can't move if start of selection is at column 0
+#
+# Moving method
+#  up
+#    - Linewise: rotate linewise
+#    - Characterwise: swap single-row selection with upper block
+#  down
+#    - Linewise: rotate linewise
+#    - Characterwise: swap single-row selection with lower block
+#  right
+#    - Linewise: indent line
+#    - Characterwise: rotate charater in single-row selection.
+#  left
+#    - Linewise: outdent line
+#    - Characterwise: rotate charater in single-row selection.
+
 
 requireFrom = (pack, path) ->
   packPath = atom.packages.resolvePackagePath(pack)

    range = selection.getBufferRange().translate(translation...)
    selection.setBufferRange(range)

    newText = switch @direction
      when 'up', 'down' # rotate row
        text = swrap(selection).lineTextForBufferRows()
        rotated = @rotateText(text, selection, @isOverwrite())[0]
        rotated.join("\n") + "\n"
      when 'right', 'left' # rotate column
        text = selection.getText()
        rotated = @rotateText(text, selection, @isOverwrite())[0]
        rotated.join('')

    range = selection.insertText(newText)
    range = range.translate(translation.reverse()...)

  isOverwrite: ->
    atom.config.get('vim-mode-plus-move-selected-text.overwrite')

  isLinewise: ->
    switch @vimState.submode
      when 'linewise' then true
      when 'characterwise', 'blockwise'
        @editor.getSelections().some (selection) ->
          not swrap(selection).isSingleRow()

  isCharacterwise: ->
    not @isLinewise()

  isMovable: (selection) ->
    {start} = selection.getBufferRange()
    switch @direction
      when 'down', 'right' then true
      when 'up' then start.row isnt 0
      when 'left'
        if @isLinewise() then true else start.column isnt 0

  execute: ->
    @withUndoJoin =>
      selections = @editor.getSelectionsOrderedByBufferPosition()
      return if @isCharacterwise() and (not @isMovable(selections[0]))
      selections.reverse() if @direction is 'down'
      @editor.transact =>
        @countTimes =>
          @mutateSelections(selections)

  mutateSelections: (selections) ->
    for selection in selections when @isMovable(selection)
      switch @direction
        when 'down' # auto insert new linew at last row
          endRow = selection.getBufferRange().end.row
          if endRow >= getVimLastBufferRow(@editor)
            eof = @editor.getEofBufferPosition()
            @editor.setTextInBufferRange([eof, eof], "\n")
        when 'right' # automatically append space at EOL
          if @isCharacterwise()
            eol = selection.getBufferRange().end
            if pointIsAtEndOfLine(@editor, eol)
              @editor.setTextInBufferRange([eol, eol], " ")

      if @isLinewise()
        @moveLinewise(selection)
      else
        @moveCharacterwise(selection)

  getInitialOverwrittenBySelection: ->
    overwrittenBySelection = new Map
    isLinewise = @isLinewise()
    @editor.getSelections().forEach (selection) ->
