/** @babel */

const Grim = require('grim')

import etch from 'etch'

const getNextUpdatePromise = () => etch.getScheduler().nextUpdatePromise

describe('Dock', () => {
  describe('when a dock is activated', () => {
    it('opens the dock and activates its active pane', () => {
      jasmine.attachToDOM(atom.workspace.getElement())
      const dock = atom.workspace.getLeftDock()
      const didChangeVisibleSpy = jasmine.createSpy()
      dock.onDidChangeVisible(didChangeVisibleSpy)

      expect(dock.isVisible()).toBe(false)
      expect(document.activeElement).toBe(
        atom.workspace
          .getCenter()
          .getActivePane()
          .getElement()
      )
      dock.activate()
      expect(dock.isVisible()).toBe(true)
      expect(document.activeElement).toBe(dock.getActivePane().getElement())
      expect(didChangeVisibleSpy).toHaveBeenCalledWith(true)
    })
  })

  describe('when a dock is hidden', () => {
    it('transfers focus back to the active center pane if the dock had focus', () => {
      jasmine.attachToDOM(atom.workspace.getElement())
      const dock = atom.workspace.getLeftDock()
      const didChangeVisibleSpy = jasmine.createSpy()
      dock.onDidChangeVisible(didChangeVisibleSpy)

      dock.activate()
      expect(document.activeElement).toBe(dock.getActivePane().getElement())
      expect(didChangeVisibleSpy.mostRecentCall.args[0]).toBe(true)

      dock.hide()
      expect(document.activeElement).toBe(
        atom.workspace
          .getCenter()
          .getActivePane()
          .getElement()
      )
      expect(didChangeVisibleSpy.mostRecentCall.args[0]).toBe(false)

      dock.activate()
      expect(document.activeElement).toBe(dock.getActivePane().getElement())
      expect(didChangeVisibleSpy.mostRecentCall.args[0]).toBe(true)

      dock.toggle()
      expect(document.activeElement).toBe(
        atom.workspace
          .getCenter()
          .getActivePane()
          .getElement()
      )
      expect(didChangeVisibleSpy.mostRecentCall.args[0]).toBe(false)

      // Don't change focus if the dock was not focused in the first place
      const modalElement = document.createElement('div')
      modalElement.setAttribute('tabindex', -1)
      atom.workspace.addModalPanel({ item: modalElement })
      modalElement.focus()
      expect(document.activeElement).toBe(modalElement)

      dock.show()
      expect(document.activeElement).toBe(modalElement)
      expect(didChangeVisibleSpy.mostRecentCall.args[0]).toBe(true)

      dock.hide()
      expect(document.activeElement).toBe(modalElement)
      expect(didChangeVisibleSpy.mostRecentCall.args[0]).toBe(false)
    })
  })

  describe('when a pane in a dock is activated', () => {
    it('opens the dock', async () => {
      const item = {
        element: document.createElement('div'),
        getDefaultLocation () {
          return 'left'
        }
      }

      await atom.workspace.open(item, { activatePane: false })
      expect(atom.workspace.getLeftDock().isVisible()).toBe(false)

      atom.workspace
        .getLeftDock()
        .getPanes()[0]
        .activate()
      expect(atom.workspace.getLeftDock().isVisible()).toBe(true)
    })
  })

  describe('activating the next pane', () => {
    describe('when the dock has more than one pane', () => {
      it('activates the next pane', () => {
        const dock = atom.workspace.getLeftDock()
        const pane1 = dock.getPanes()[0]
        const pane2 = pane1.splitRight()
        const pane3 = pane2.splitRight()
        pane2.activate()
        expect(pane1.isActive()).toBe(false)
        expect(pane2.isActive()).toBe(true)
        expect(pane3.isActive()).toBe(false)

        dock.activateNextPane()
        expect(pane1.isActive()).toBe(false)
        expect(pane2.isActive()).toBe(false)
        expect(pane3.isActive()).toBe(true)
      })
    })

    describe('when the dock has only one pane', () => {
      it('leaves the current pane active', () => {
        const dock = atom.workspace.getLeftDock()

        expect(dock.getPanes().length).toBe(1)
        const pane = dock.getPanes()[0]
        expect(pane.isActive()).toBe(true)
        dock.activateNextPane()
        expect(pane.isActive()).toBe(true)
      })
    })
  })

  describe('activating the previous pane', () => {
    describe('when the dock has more than one pane', () => {
      it('activates the previous pane', () => {
        const dock = atom.workspace.getLeftDock()
        const pane1 = dock.getPanes()[0]
        const pane2 = pane1.splitRight()
        const pane3 = pane2.splitRight()
        pane2.activate()
        expect(pane1.isActive()).toBe(false)
        expect(pane2.isActive()).toBe(true)
        expect(pane3.isActive()).toBe(false)

        dock.activatePreviousPane()
        expect(pane1.isActive()).toBe(true)
        expect(pane2.isActive()).toBe(false)
        expect(pane3.isActive()).toBe(false)
      })
    })

    describe('when the dock has only one pane', () => {
      it('leaves the current pane active', () => {
        const dock = atom.workspace.getLeftDock()

        expect(dock.getPanes().length).toBe(1)
        const pane = dock.getPanes()[0]
        expect(pane.isActive()).toBe(true)
        dock.activatePreviousPane()
        expect(pane.isActive()).toBe(true)
      })
    })
  })

  describe('when the dock resize handle is double-clicked', () => {
    describe('when the dock is open', () => {
      it("resizes a vertically-oriented dock to the current item's preferred width", async () => {
        jasmine.attachToDOM(atom.workspace.getElement())

        const item = {
          element: document.createElement('div'),
          getDefaultLocation () {
            return 'left'
          },
          getPreferredWidth () {
            return 142
          },
          getPreferredHeight () {
            return 122
          }
        }

        await atom.workspace.open(item)
        const dock = atom.workspace.getLeftDock()
        const dockElement = dock.getElement()

        dock.setState({ size: 300 })
        await getNextUpdatePromise()
        expect(dockElement.offsetWidth).toBe(300)
        dockElement
          .querySelector('.atom-dock-resize-handle')
          .dispatchEvent(new MouseEvent('mousedown', { detail: 2 }))
        await getNextUpdatePromise()

        expect(dockElement.offsetWidth).toBe(item.getPreferredWidth())
      })

      it("resizes a horizontally-oriented dock to the current item's preferred width", async () => {
        jasmine.attachToDOM(atom.workspace.getElement())

        const item = {
          element: document.createElement('div'),
          getDefaultLocation () {
            return 'bottom'
          },
          getPreferredWidth () {
            return 122
          },
          getPreferredHeight () {
            return 142
          }
        }

        await atom.workspace.open(item)
        const dock = atom.workspace.getBottomDock()
        const dockElement = dock.getElement()

        dock.setState({ size: 300 })
        await getNextUpdatePromise()
        expect(dockElement.offsetHeight).toBe(300)
        dockElement
          .querySelector('.atom-dock-resize-handle')
          .dispatchEvent(new MouseEvent('mousedown', { detail: 2 }))
        await getNextUpdatePromise()

        expect(dockElement.offsetHeight).toBe(item.getPreferredHeight())
      })
    })

    describe('when the dock is closed', () => {
      it('does nothing', async () => {
        jasmine.attachToDOM(atom.workspace.getElement())

        const item = {
          element: document.createElement('div'),
          getDefaultLocation () {
            return 'bottom'
          },
          getPreferredWidth () {
            return 122
          },
          getPreferredHeight () {
            return 142
          }
        }

        await atom.workspace.open(item, { activatePane: false })

        const dockElement = atom.workspace.getBottomDock().getElement()
        dockElement
          .querySelector('.atom-dock-resize-handle')
          .dispatchEvent(new MouseEvent('mousedown', { detail: 2 }))
        expect(dockElement.offsetHeight).toBe(0)
        expect(dockElement.querySelector('.atom-dock-inner').offsetHeight).toBe(
          0
        )
        // The content should be masked away.
        expect(dockElement.querySelector('.atom-dock-mask').offsetHeight).toBe(
          0
        )
      })
    })
  })

  describe('when you add an item to an empty dock', () => {
    describe('when the item has a preferred size', () => {
      it('is takes the preferred size of the item', async () => {
        jasmine.attachToDOM(atom.workspace.getElement())

        const createItem = preferredWidth => ({
          element: document.createElement('div'),
          getDefaultLocation () {
            return 'left'
          },
          getPreferredWidth () {
            return preferredWidth
          }
        })

        const dock = atom.workspace.getLeftDock()
        const dockElement = dock.getElement()
        expect(dock.getPaneItems()).toHaveLength(0)

        const item1 = createItem(111)
        await atom.workspace.open(item1)

        // It should update the width every time we go from 0 -> 1 items, not just the first.
        expect(dock.isVisible()).toBe(true)
        expect(dockElement.offsetWidth).toBe(111)
        dock.destroyActivePane()
        expect(dock.getPaneItems()).toHaveLength(0)
        expect(dock.isVisible()).toBe(false)
        const item2 = createItem(222)
        await atom.workspace.open(item2)
        expect(dock.isVisible()).toBe(true)
        expect(dockElement.offsetWidth).toBe(222)

        // Adding a second shouldn't change the size.
        const item3 = createItem(333)
        await atom.workspace.open(item3)
        expect(dockElement.offsetWidth).toBe(222)
      })
    })

    describe('when the item has no preferred size', () => {
      it('is still has an explicit size', async () => {
        jasmine.attachToDOM(atom.workspace.getElement())

        const item = {
          element: document.createElement('div'),
          getDefaultLocation () {
            return 'left'
          }
        }
        const dock = atom.workspace.getLeftDock()
        expect(dock.getPaneItems()).toHaveLength(0)

        expect(dock.state.size).toBe(null)
        await atom.workspace.open(item)
        expect(dock.state.size).not.toBe(null)
      })
    })
  })

  describe('a deserialized dock', () => {
    it('restores the serialized size', async () => {
      jasmine.attachToDOM(atom.workspace.getElement())

      const item = {
        element: document.createElement('div'),
        getDefaultLocation () {
          return 'left'
        },
        getPreferredWidth () {
          return 122
        },
        serialize: () => ({ deserializer: 'DockTestItem' })
      }
      atom.deserializers.add({
        name: 'DockTestItem',
        deserialize: () => item
      })
      const dock = atom.workspace.getLeftDock()
      const dockElement = dock.getElement()

      await atom.workspace.open(item)
      dock.setState({ size: 150 })
      expect(dockElement.offsetWidth).toBe(150)
      const serialized = dock.serialize()
      dock.setState({ size: 122 })
      expect(dockElement.offsetWidth).toBe(122)
      dock.destroyActivePane()
      dock.deserialize(serialized, atom.deserializers)
      expect(dockElement.offsetWidth).toBe(150)
    })

    it("isn't visible if it has no items", async () => {
      jasmine.attachToDOM(atom.workspace.getElement())

      const item = {
        element: document.createElement('div'),
        getDefaultLocation () {
          return 'left'
        },
        getPreferredWidth () {
          return 122
        }
      }
      const dock = atom.workspace.getLeftDock()

      await atom.workspace.open(item)
      expect(dock.isVisible()).toBe(true)
      const serialized = dock.serialize()
      dock.deserialize(serialized, atom.deserializers)
      expect(dock.getPaneItems()).toHaveLength(0)
      expect(dock.isVisible()).toBe(false)
    })
  })

  describe('drag handling', () => {
    it('expands docks to match the preferred size of the dragged item', async () => {
      jasmine.attachToDOM(atom.workspace.getElement())

      const element = document.createElement('div')
      element.setAttribute('is', 'tabs-tab')
      element.item = {
        element,
        getDefaultLocation () {
          return 'left'
        },
        getPreferredWidth () {
          return 144
        }
      }

      const dragEvent = new DragEvent('dragstart')
      Object.defineProperty(dragEvent, 'target', { value: element })

      atom.workspace.getElement().handleDragStart(dragEvent)
      await getNextUpdatePromise()
      expect(atom.workspace.getLeftDock().refs.wrapperElement.offsetWidth).toBe(
        144
      )
    })

    it('does nothing when text nodes are dragged', () => {
      jasmine.attachToDOM(atom.workspace.getElement())

      const textNode = document.createTextNode('hello')

      const dragEvent = new DragEvent('dragstart')
      Object.defineProperty(dragEvent, 'target', { value: textNode })

      expect(() =>
        atom.workspace.getElement().handleDragStart(dragEvent)
      ).not.toThrow()
    })
  })

  describe('::getActiveTextEditor()', () => {
    it('is deprecated', () => {
      spyOn(Grim, 'deprecate')

      atom.workspace.getLeftDock().getActiveTextEditor()
      expect(Grim.deprecate.callCount).toBe(1)
    })
  })
})
