PaneContainer = require 'pane-container'
Pane = require 'pane'
{View, $$} = require 'space-pen'
_ = require 'underscore'
$ = require 'jquery'

describe "PaneContainer", ->
  [TestView, container, pane1, pane2, pane3] = []

  beforeEach ->
    class TestView extends View
      registerDeserializer(this)
      @deserialize: ({name}) -> new TestView(name)
      @content: -> @div tabindex: -1
      initialize: (@name) -> @text(@name)
      serialize: -> { deserializer: 'TestView', @name }
      getUri: -> "/tmp/#{@name}"
      save: -> @saved = true
      isEqual: (other) -> @name is other.name

    container = new PaneContainer
    pane1 = new Pane(new TestView('1'))
    container.setRoot(pane1)
    pane2 = pane1.splitRight(new TestView('2'))
    pane3 = pane2.splitDown(new TestView('3'))

  afterEach ->
    unregisterDeserializer(TestView)

  describe ".focusNextPane()", ->
    it "focuses the pane following the focused pane or the first pane if no pane has focus", ->
      container.attachToDom()
      container.focusNextPane()
      expect(pane1.activeItem).toMatchSelector ':focus'
      container.focusNextPane()
      expect(pane2.activeItem).toMatchSelector ':focus'
      container.focusNextPane()
      expect(pane3.activeItem).toMatchSelector ':focus'
      container.focusNextPane()
      expect(pane1.activeItem).toMatchSelector ':focus'

  describe ".focusPreviousPane()", ->
    it "focuses the pane preceding the focused pane or the last pane if no pane has focus", ->
      container.attachToDom()
      container.focusPreviousPane()
      expect(pane3.activeItem).toMatchSelector ':focus'
      container.focusPreviousPane()
      expect(pane2.activeItem).toMatchSelector ':focus'
      container.focusPreviousPane()
      expect(pane1.activeItem).toMatchSelector ':focus'
      container.focusPreviousPane()
      expect(pane3.activeItem).toMatchSelector ':focus'

  describe ".getActivePane()", ->
    it "returns the most-recently focused pane", ->
      focusStealer = $$ -> @div tabindex: -1, "focus stealer"
      focusStealer.attachToDom()
      container.attachToDom()

      pane2.focus()
      expect(container.getFocusedPane()).toBe pane2
      expect(container.getActivePane()).toBe pane2

      focusStealer.focus()
      expect(container.getFocusedPane()).toBeUndefined()
      expect(container.getActivePane()).toBe pane2

      pane3.focus()
      expect(container.getFocusedPane()).toBe pane3
      expect(container.getActivePane()).toBe pane3

      # returns the first pane if none have been set to active
      container.find('.pane.active').removeClass('active')
      expect(container.getActivePane()).toBe pane1

  describe ".eachPane(callback)", ->
    it "runs the callback with all current and future panes until the subscription is cancelled", ->
      panes = []
      subscription = container.eachPane (pane) -> panes.push(pane)
      expect(panes).toEqual [pane1, pane2, pane3]

      panes = []
      pane4 = pane3.splitRight()
      expect(panes).toEqual [pane4]

      panes = []
      subscription.cancel()
      pane4.splitDown()
      expect(panes).toEqual []

  describe ".reopenItem()", ->
    describe "when there is an active pane", ->
      it "reconstructs and shows the last-closed pane item", ->
        expect(container.getActivePane()).toBe pane3
        item3 = pane3.activeItem
        item4 = new TestView('4')
        pane3.showItem(item4)

        pane3.destroyItem(item3)
        pane3.destroyItem(item4)
        expect(container.getActivePane()).toBe pane1

        expect(container.reopenItem()).toBeTruthy()
        expect(pane1.activeItem).toEqual item4

        expect(container.reopenItem()).toBeTruthy()
        expect(pane1.activeItem).toEqual item3

        expect(container.reopenItem()).toBeFalsy()
        expect(pane1.activeItem).toEqual item3

    describe "when there is no active pane", ->
      it "attaches a new pane with the reconstructed last pane item and focuses it", ->
        container.attachToDom()
        pane1.remove()
        pane2.remove()
        item3 = pane3.activeItem
        pane3.destroyItem(item3)
        expect(container.getActivePane()).toBeUndefined()

        container.reopenItem()

        expect(container.getActivePane().activeItem).toEqual item3
        expect(container.getActivePane().activeView).toMatchSelector ':focus'

    it "does not reopen an item that is already open", ->
      item3 = pane3.activeItem
      item4 = new TestView('4')
      pane3.showItem(item4)
      pane3.destroyItem(item3)
      pane3.destroyItem(item4)

      expect(container.getActivePane()).toBe pane1
      pane1.showItem(new TestView('4'))

      expect(container.reopenItem()).toBeTruthy()
      expect(_.pluck(pane1.getItems(), 'name')).toEqual ['1', '4', '3']
      expect(pane1.activeItem).toEqual item3

      expect(container.reopenItem()).toBeFalsy()
      expect(pane1.activeItem).toEqual item3

  describe ".saveAll()", ->
    it "saves all open pane items", ->
      pane1.showItem(new TestView('4'))

      container.saveAll()

      for pane in container.getPanes()
        for item in pane.getItems()
          expect(item.saved).toBeTruthy()

  describe ".confirmClose()", ->
    it "returns true after modified files are saved", ->
      pane1.itemAtIndex(0).isModified = -> true
      pane2.itemAtIndex(0).isModified = -> true
      spyOn(atom, "confirmSync").andReturn(0)

      saved = container.confirmClose()

      runs ->
        expect(saved).toBeTruthy()
        expect(atom.confirmSync).toHaveBeenCalled()

    it "returns false if the user cancels saving", ->
      pane1.itemAtIndex(0).isModified = -> true
      pane2.itemAtIndex(0).isModified = -> true
      spyOn(atom, "confirmSync").andReturn(1)

      saved = container.confirmClose()

      runs ->
        expect(saved).toBeFalsy()
        expect(atom.confirmSync).toHaveBeenCalled()

  describe "serialization", ->
    it "can be serialized and deserialized, and correctly adjusts dimensions of deserialized panes after attach", ->
      newContainer = deserialize(container.serialize())
      expect(newContainer.find('.row > :contains(1)')).toExist()
      expect(newContainer.find('.row > .column > :contains(2)')).toExist()
      expect(newContainer.find('.row > .column > :contains(3)')).toExist()

      newContainer.height(200).width(300).attachToDom()
      expect(newContainer.find('.row > :contains(1)').width()).toBe 150
      expect(newContainer.find('.row > .column > :contains(2)').height()).toBe 100

    xit "removes empty panes on deserialization", ->
      # only deserialize pane 1's view successfully
      TestView.deserialize = ({name}) -> new TestView(name) if name is '1'
      newContainer = deserialize(container.serialize())
      expect(newContainer.find('.row, .column')).not.toExist()
      expect(newContainer.find('> :contains(1)')).toExist()
