RowMap = require 'row-map'

describe "RowMap", ->
  map = null

  beforeEach ->
    map = new RowMap

  describe "when no mappings have been recorded", ->
    it "maps buffer rows to screen rows 1:1", ->
      expect(map.screenRowRangeForBufferRow(0)).toEqual [0, 1]
      expect(map.screenRowRangeForBufferRow(100)).toEqual [100, 101]

  describe ".mapBufferRowRange(startBufferRow, endBufferRow, screenRows)", ->
    describe "when mapping to a single screen row (like a visible fold)", ->
      beforeEach ->
        map.mapBufferRowRange(5, 10, 1)
        map.mapBufferRowRange(15, 20, 1)
        map.mapBufferRowRange(25, 30, 1)

      it "accounts for the mapping when translating buffer rows to screen row ranges", ->
        expect(map.screenRowRangeForBufferRow(0)).toEqual [0, 1]

        expect(map.screenRowRangeForBufferRow(4)).toEqual [4, 5]
        expect(map.screenRowRangeForBufferRow(5)).toEqual [5, 6]
        expect(map.screenRowRangeForBufferRow(9)).toEqual [5, 6]
        expect(map.screenRowRangeForBufferRow(10)).toEqual [6, 7]

        expect(map.screenRowRangeForBufferRow(14)).toEqual [10, 11]
        expect(map.screenRowRangeForBufferRow(15)).toEqual [11, 12]
        expect(map.screenRowRangeForBufferRow(19)).toEqual [11, 12]
        expect(map.screenRowRangeForBufferRow(20)).toEqual [12, 13]

        expect(map.screenRowRangeForBufferRow(24)).toEqual [16, 17]
        expect(map.screenRowRangeForBufferRow(25)).toEqual [17, 18]
        expect(map.screenRowRangeForBufferRow(29)).toEqual [17, 18]
        expect(map.screenRowRangeForBufferRow(30)).toEqual [18, 19]

      it "accounts for the mapping when translating screen rows to buffer row ranges", ->
        expect(map.bufferRowRangeForScreenRow(0)).toEqual [0, 1]

        expect(map.bufferRowRangeForScreenRow(4)).toEqual [4, 5]
        expect(map.bufferRowRangeForScreenRow(5)).toEqual [5, 10]
        expect(map.bufferRowRangeForScreenRow(6)).toEqual [10, 11]

        expect(map.bufferRowRangeForScreenRow(10)).toEqual [14, 15]
        expect(map.bufferRowRangeForScreenRow(11)).toEqual [15, 20]
        expect(map.bufferRowRangeForScreenRow(12)).toEqual [20, 21]

        expect(map.bufferRowRangeForScreenRow(16)).toEqual [24, 25]
        expect(map.bufferRowRangeForScreenRow(17)).toEqual [25, 30]
        expect(map.bufferRowRangeForScreenRow(18)).toEqual [30, 31]

    describe "when mapping to zero screen rows (like an invisible fold)", ->
      beforeEach ->
        map.mapBufferRowRange(5, 10, 0)
        map.mapBufferRowRange(15, 20, 0)
        map.mapBufferRowRange(25, 30, 0)

      it "accounts for the mapping when translating buffer rows to screen row ranges", ->
        expect(map.screenRowRangeForBufferRow(0)).toEqual [0, 1]

        expect(map.screenRowRangeForBufferRow(4)).toEqual [4, 5]
        expect(map.screenRowRangeForBufferRow(5)).toEqual [5, 5]
        expect(map.screenRowRangeForBufferRow(9)).toEqual [5, 5]
        expect(map.screenRowRangeForBufferRow(10)).toEqual [5, 6]

        expect(map.screenRowRangeForBufferRow(14)).toEqual [9, 10]
        expect(map.screenRowRangeForBufferRow(15)).toEqual [10, 10]
        expect(map.screenRowRangeForBufferRow(19)).toEqual [10, 10]
        expect(map.screenRowRangeForBufferRow(20)).toEqual [10, 11]

        expect(map.screenRowRangeForBufferRow(24)).toEqual [14, 15]
        expect(map.screenRowRangeForBufferRow(25)).toEqual [15, 15]
        expect(map.screenRowRangeForBufferRow(29)).toEqual [15, 15]
        expect(map.screenRowRangeForBufferRow(30)).toEqual [15, 16]

      it "accounts for the mapping when translating screen rows to buffer row ranges", ->
        expect(map.bufferRowRangeForScreenRow(0)).toEqual [0, 1]

        expect(map.bufferRowRangeForScreenRow(4)).toEqual [4, 5]
        expect(map.bufferRowRangeForScreenRow(5)).toEqual [10, 11]

        expect(map.bufferRowRangeForScreenRow(9)).toEqual [14, 15]
        expect(map.bufferRowRangeForScreenRow(10)).toEqual [20, 21]

        expect(map.bufferRowRangeForScreenRow(14)).toEqual [24, 25]
        expect(map.bufferRowRangeForScreenRow(15)).toEqual [30, 31]

    describe "when mapping a single buffer row to multiple screen rows (like a wrapped line)", ->
      beforeEach ->
        map.mapBufferRowRange(5, 6, 3)
        map.mapBufferRowRange(10, 11, 2)
        map.mapBufferRowRange(20, 21, 5)

      it "accounts for the mapping when translating buffer rows to screen row ranges", ->
        expect(map.screenRowRangeForBufferRow(0)).toEqual [0, 1]

        expect(map.screenRowRangeForBufferRow(4)).toEqual [4, 5]
        expect(map.screenRowRangeForBufferRow(5)).toEqual [5, 8]
        expect(map.screenRowRangeForBufferRow(6)).toEqual [8, 9]

        expect(map.screenRowRangeForBufferRow(9)).toEqual [11, 12]
        expect(map.screenRowRangeForBufferRow(10)).toEqual [12, 14]
        expect(map.screenRowRangeForBufferRow(11)).toEqual [14, 15]

        expect(map.screenRowRangeForBufferRow(19)).toEqual [22, 23]
        expect(map.screenRowRangeForBufferRow(20)).toEqual [23, 28]
        expect(map.screenRowRangeForBufferRow(21)).toEqual [28, 29]

      it "accounts for the mapping when translating screen rows to buffer row ranges", ->
        expect(map.bufferRowRangeForScreenRow(0)).toEqual [0, 1]

        expect(map.bufferRowRangeForScreenRow(4)).toEqual [4, 5]
        expect(map.bufferRowRangeForScreenRow(5)).toEqual [5, 6]
        expect(map.bufferRowRangeForScreenRow(7)).toEqual [5, 6]
        expect(map.bufferRowRangeForScreenRow(8)).toEqual [6, 7]

        expect(map.bufferRowRangeForScreenRow(11)).toEqual [9, 10]
        expect(map.bufferRowRangeForScreenRow(12)).toEqual [10, 11]
        expect(map.bufferRowRangeForScreenRow(13)).toEqual [10, 11]
        expect(map.bufferRowRangeForScreenRow(14)).toEqual [11, 12]

        expect(map.bufferRowRangeForScreenRow(22)).toEqual [19, 20]
        expect(map.bufferRowRangeForScreenRow(23)).toEqual [20, 21]
        expect(map.bufferRowRangeForScreenRow(27)).toEqual [20, 21]
        expect(map.bufferRowRangeForScreenRow(28)).toEqual [21, 22]

      describe "after re-mapping a row range to a new number of screen rows", ->
        beforeEach ->
          map.applyScreenDelta(12, 2) # simulate adding 2 more soft wraps
          map.mapBufferRowRange(10, 11, 4)

        it "updates translation accordingly", ->
          expect(map.screenRowRangeForBufferRow(4)).toEqual [4, 5]
          expect(map.screenRowRangeForBufferRow(5)).toEqual [5, 8]
          expect(map.screenRowRangeForBufferRow(6)).toEqual [8, 9]

          expect(map.screenRowRangeForBufferRow(9)).toEqual [11, 12]
          expect(map.screenRowRangeForBufferRow(10)).toEqual [12, 16]
          expect(map.screenRowRangeForBufferRow(11)).toEqual [16, 17]

          expect(map.screenRowRangeForBufferRow(19)).toEqual [24, 25]
          expect(map.screenRowRangeForBufferRow(20)).toEqual [25, 30]
          expect(map.screenRowRangeForBufferRow(21)).toEqual [30, 31]

    describe "when the row range is inside an existing 1:1 region", ->
      it "preserves the starting screen row of subsequent 1:N regions", ->
        map.mapBufferRowRange(5, 10, 1)
        map.mapBufferRowRange(25, 30, 1)

        expect(map.bufferRowRangeForScreenRow(5)).toEqual [5, 10]
        expect(map.bufferRowRangeForScreenRow(21)).toEqual [25, 30]

        map.mapBufferRowRange(15, 20, 1)

        expect(map.bufferRowRangeForScreenRow(11)).toEqual [15, 20]
        expect(map.bufferRowRangeForScreenRow(5)).toEqual [5, 10]
        expect(map.bufferRowRangeForScreenRow(21)).toEqual [25, 30]

    describe "when the row range surrounds existing regions", ->
      it "replaces the regions inside the given buffer row range with a single region", ->
        map.mapBufferRowRange(5, 10, 1)  # inner fold 1
        map.mapBufferRowRange(11, 13, 1)  # inner fold 2
        map.mapBufferRowRange(15, 20, 1) # inner fold 3
        map.mapBufferRowRange(22, 27, 1) # following fold

        map.mapBufferRowRange(5, 20, 1)

        expect(map.bufferRowRangeForScreenRow(5)).toEqual [5, 20]
        expect(map.bufferRowRangeForScreenRow(6)).toEqual [20, 21]
        expect(map.bufferRowRangeForScreenRow(7)).toEqual [21, 22]
        expect(map.bufferRowRangeForScreenRow(8)).toEqual [22, 27]

    describe "when the row range straddles existing regions", ->
      it "splits the straddled regions and places the new region between them", ->
                                         # filler region 0
        map.mapBufferRowRange(4, 7, 1)   # region 1
                                         # filler region 2
        map.mapBufferRowRange(13, 15, 1) # region 3

        # create region straddling region 0 and region 2
        map.mapBufferRowRange(2, 10, 1)

        expect(map.regions[0]).toEqual(bufferRows: 2, screenRows: 2)
        expect(map.regions[1]).toEqual(bufferRows: 8, screenRows: 1)
        expect(map.regions[2]).toEqual(bufferRows: 3, screenRows: 8)
        expect(map.regions[3]).toEqual(bufferRows: 2, screenRows: 1)

    it "merges adjacent isomorphic mappings", ->
      map.mapBufferRowRange(2, 4, 1)
      map.mapBufferRowRange(4, 5, 2)

      map.mapBufferRowRange(1, 4, 3)
      expect(map.regions).toEqual [{bufferRows: 5, screenRows: 5}]

  describe ".applyBufferDelta(startBufferRow, delta)", ->
    describe "when applying a positive delta", ->
      it "expands the region containing the given start row by the given delta", ->
        map.mapBufferRowRange(4, 8, 1)

        map.applyBufferDelta(5, 4)

        expect(map.regions[0]).toEqual(bufferRows: 4, screenRows: 4)
        expect(map.regions[1]).toEqual(bufferRows: 8, screenRows: 1)

    describe "when applying a negative delta", ->
      it "shrinks regions starting at the start row until the entire delta is consumed", ->
        map.mapBufferRowRange(4, 8, 1)
        map.mapBufferRowRange(10, 14, 1)

        map.applyBufferDelta(3, -6)

        expect(map.regions[0]).toEqual(bufferRows: 3, screenRows: 4)
        expect(map.regions[1]).toEqual(bufferRows: 0, screenRows: 1)
        expect(map.regions[2]).toEqual(bufferRows: 1, screenRows: 2)
        expect(map.regions[3]).toEqual(bufferRows: 4, screenRows: 1)

  describe ".applyScreenDelta(startScreenRow, delta)", ->
    describe "when applying a positive delta", ->
      it "can enlarge the screen side of existing regions", ->
        map.mapBufferRowRange(5, 6, 3) # wrapped line
        map.applyScreenDelta(5, 2) # wrap it twice more
        expect(map.screenRowRangeForBufferRow(5)).toEqual [5, 10]

    describe "when applying a negative delta", ->
      it "can collapse the screen side of multiple regions to 0 until the entire delta has been applied", ->
        map.mapBufferRowRange(5, 10, 1)  # inner fold 1
        map.mapBufferRowRange(11, 13, 1)  # inner fold 2
        map.mapBufferRowRange(15, 20, 1) # inner fold 3
        map.mapBufferRowRange(22, 27, 1) # following fold

        map.applyScreenDelta(6, -5)

        expect(map.screenRowRangeForBufferRow(5)).toEqual [5, 6]
        expect(map.screenRowRangeForBufferRow(9)).toEqual [5, 6]
        expect(map.screenRowRangeForBufferRow(10)).toEqual [6, 6]
        expect(map.screenRowRangeForBufferRow(19)).toEqual [6, 6]
        expect(map.screenRowRangeForBufferRow(22)).toEqual [8, 9]
        expect(map.screenRowRangeForBufferRow(26)).toEqual [8, 9]

      it "starts collapsing the first region at the start row, not before", ->
        map.mapBufferRowRange(5, 6, 4)
        map.mapBufferRowRange(11, 13, 1)

        map.applyScreenDelta(7, -5)

        expect(map.regions[0]).toEqual(bufferRows: 5, screenRows: 5)
        expect(map.regions[1]).toEqual(bufferRows: 1, screenRows: 2)
        expect(map.regions[2]).toEqual(bufferRows: 5, screenRows: 2)

    it "does not throw an exception when applying a delta beyond the last region", ->
      map.mapBufferRowRange(5, 10, 1)  # inner fold 1
      map.applyScreenDelta(15, 10)
