'use strict'

const Panel = require('../src/panel')
const PanelContainer = require('../src/panel-container')

describe('PanelContainerElement', () => {
  let jasmineContent, element, container

  class TestPanelContainerItem {
  }

  class TestPanelContainerItemElement_ extends HTMLElement {
    createdCallback () {
      this.classList.add('test-root')
    }
    initialize (model) {
      this.model = model
      return this
    }
    focus() {}
  }

  const TestPanelContainerItemElement = document.registerElement(
    'atom-test-container-item-element',
    {prototype: TestPanelContainerItemElement_.prototype}
  )

  beforeEach(() => {
    jasmineContent = document.body.querySelector('#jasmine-content')

    atom.views.addViewProvider(
      TestPanelContainerItem,
      model => new TestPanelContainerItemElement().initialize(model)
    )

    container = new PanelContainer({viewRegistry: atom.views, location: 'left'})
    element = container.getElement()
    jasmineContent.appendChild(element)
  })

  it('has a location class with value from the model', () => {
    expect(element).toHaveClass('left')
  })

  it('removes the element when the container is destroyed', () => {
    expect(element.parentNode).toBe(jasmineContent)
    container.destroy()
    expect(element.parentNode).not.toBe(jasmineContent)
  })

  describe('adding and removing panels', () => {
    it('allows panels to be inserted at any position', () => {
      const panel1 = new Panel({item: new TestPanelContainerItem(), priority: 10}, atom.views)
      const panel2 = new Panel({item: new TestPanelContainerItem(), priority: 5}, atom.views)
      const panel3 = new Panel({item: new TestPanelContainerItem(), priority: 8}, atom.views)

      container.addPanel(panel1)
      container.addPanel(panel2)
      container.addPanel(panel3)

      expect(element.childNodes[2]).toBe(panel1.getElement())
      expect(element.childNodes[1]).toBe(panel3.getElement())
      expect(element.childNodes[0]).toBe(panel2.getElement())
    })

    describe('when the container is at the left location', () =>
      it('adds atom-panel elements when a new panel is added to the container; removes them when the panels are destroyed', () => {
        expect(element.childNodes.length).toBe(0)

        const panel1 = new Panel({item: new TestPanelContainerItem()}, atom.views)
        container.addPanel(panel1)
        expect(element.childNodes.length).toBe(1)
        expect(element.childNodes[0]).toHaveClass('left')
        expect(element.childNodes[0]).toHaveClass('tool-panel') // legacy selector support
        expect(element.childNodes[0]).toHaveClass('panel-left') // legacy selector support

        expect(element.childNodes[0].tagName).toBe('ATOM-PANEL')

        const panel2 = new Panel({item: new TestPanelContainerItem()}, atom.views)
        container.addPanel(panel2)
        expect(element.childNodes.length).toBe(2)

        expect(panel1.getElement().style.display).not.toBe('none')
        expect(panel2.getElement().style.display).not.toBe('none')

        panel1.destroy()
        expect(element.childNodes.length).toBe(1)

        panel2.destroy()
        expect(element.childNodes.length).toBe(0)
      })
    )

    describe('when the container is at the bottom location', () => {
      beforeEach(() => {
        container = new PanelContainer({viewRegistry: atom.views, location: 'bottom'})
        element = container.getElement()
        jasmineContent.appendChild(element)
      })

      it('adds atom-panel elements when a new panel is added to the container; removes them when the panels are destroyed', () => {
        expect(element.childNodes.length).toBe(0)

        const panel1 = new Panel({item: new TestPanelContainerItem(), className: 'one'}, atom.views)
        container.addPanel(panel1)
        expect(element.childNodes.length).toBe(1)
        expect(element.childNodes[0]).toHaveClass('bottom')
        expect(element.childNodes[0]).toHaveClass('tool-panel') // legacy selector support
        expect(element.childNodes[0]).toHaveClass('panel-bottom') // legacy selector support
        expect(element.childNodes[0].tagName).toBe('ATOM-PANEL')
        expect(panel1.getElement()).toHaveClass('one')

        const panel2 = new Panel({item: new TestPanelContainerItem(), className: 'two'}, atom.views)
        container.addPanel(panel2)
        expect(element.childNodes.length).toBe(2)
        expect(panel2.getElement()).toHaveClass('two')

        panel1.destroy()
        expect(element.childNodes.length).toBe(1)

        panel2.destroy()
        expect(element.childNodes.length).toBe(0)
      })
    })
  })

  describe('when the container is modal', () => {
    beforeEach(() => {
      container = new PanelContainer({viewRegistry: atom.views, location: 'modal'})
      element = container.getElement()
      jasmineContent.appendChild(element)
    })

    it('allows only one panel to be visible at a time', () => {
      const panel1 = new Panel({item: new TestPanelContainerItem()}, atom.views)
      container.addPanel(panel1)

      expect(panel1.getElement().style.display).not.toBe('none')

      const panel2 = new Panel({item: new TestPanelContainerItem()}, atom.views)
      container.addPanel(panel2)

      expect(panel1.getElement().style.display).toBe('none')
      expect(panel2.getElement().style.display).not.toBe('none')

      panel1.show()

      expect(panel1.getElement().style.display).not.toBe('none')
      expect(panel2.getElement().style.display).toBe('none')
    })

    it("adds the 'modal' class to panels", () => {
      const panel1 = new Panel({item: new TestPanelContainerItem()}, atom.views)
      container.addPanel(panel1)

      expect(panel1.getElement()).toHaveClass('modal')

      // legacy selector support
      expect(panel1.getElement()).not.toHaveClass('tool-panel')
      expect(panel1.getElement()).toHaveClass('overlay')
      expect(panel1.getElement()).toHaveClass('from-top')
    })

    describe("autoFocus", () => {
      function createPanel() {
        const panel = new Panel(
          {
            item: new TestPanelContainerItem(),
            autoFocus: true,
            visible: false
          },
          atom.views
        )

        container.addPanel(panel)
        return panel
      }

      it("focuses the first tabbable item if available", () => {
        const panel = createPanel()
        const panelEl = panel.getElement()
        const inputEl = document.createElement('input')

        panelEl.appendChild(inputEl)
        expect(document.activeElement).not.toBe(inputEl)

        panel.show()
        expect(document.activeElement).toBe(inputEl)
      })

      it("focuses the entire panel item when no tabbable item is available and the panel is focusable", () => {
        const panel = createPanel()
        const panelEl = panel.getElement()

        spyOn(panelEl, 'focus')
        panel.show()
        expect(panelEl.focus).toHaveBeenCalled()
      })

      it("returns focus to the original activeElement", () => {
        const panel = createPanel()
        const previousActiveElement = document.activeElement
        const panelEl = panel.getElement()
        panelEl.appendChild(document.createElement('input'))

        panel.show()
        panel.hide()

        waitsFor(() => document.activeElement === previousActiveElement)
        runs(() => {
          expect(document.activeElement).toBe(previousActiveElement)
        })
      })
    })
  })
})
