/** @babel */

const Grim = require('grim')

import {it, fit, ffit, fffit, beforeEach, afterEach} from './async-spec-helpers'
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'})
      }
      const itemDeserializer = 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)
    })
  })
})
