import pytest

import pyqtgraph as pg
import pyqtgraph.dockarea as da

pg.mkQApp()


def test_dockarea():
    a = da.DockArea()
    d1 = da.Dock("dock 1")
    a.addDock(d1, 'left')

    assert a.topContainer is d1.container()
    assert d1.container().container() is a
    assert d1.area is a
    assert a.topContainer.widget(0) is d1

    d2 = da.Dock("dock 2")
    a.addDock(d2, 'right')

    assert a.topContainer is d1.container()
    assert a.topContainer is d2.container()
    assert d1.container().container() is a
    assert d2.container().container() is a
    assert d2.area is a
    assert a.topContainer.widget(0) is d1
    assert a.topContainer.widget(1) is d2

    d3 = da.Dock("dock 3")
    a.addDock(d3, 'bottom')

    assert a.topContainer is d3.container()
    assert d2.container().container() is d3.container()
    assert d1.container().container() is d3.container()
    assert d1.container().container().container() is a
    assert d2.container().container().container() is a
    assert d3.container().container() is a
    assert d3.area is a
    assert d2.area is a
    assert a.topContainer.widget(0) is d1.container()
    assert a.topContainer.widget(1) is d3

    d4 = da.Dock("dock 4")
    a.addDock(d4, 'below', d3)

    assert d4.container().type() == 'tab'
    assert d4.container() is d3.container()
    assert d3.container().container() is d2.container().container()
    assert d4.area is a
    a.printState()

    # layout now looks like:
    #    vcontainer
    #        hcontainer
    #            dock 1
    #            dock 2
    #        tcontainer
    #            dock 3
    #            dock 4

    # test save/restore state
    state = a.saveState()
    a2 = da.DockArea()
    # default behavior is to raise exception if docks are missing
    with pytest.raises(Exception):
        a2.restoreState(state)

    # test restore with ignore missing
    a2.restoreState(state, missing='ignore')
    assert a2.topContainer is None

    # test restore with auto-create
    a2.restoreState(state, missing='create')
    assert a2.saveState() == state
    a2.printState()

    # double-check that state actually matches the output of saveState()
    c1 = a2.topContainer
    assert c1.type() == 'vertical'
    c2 = c1.widget(0)
    c3 = c1.widget(1)
    assert c2.type() == 'horizontal'
    assert c2.widget(0).name() == 'dock 1'
    assert c2.widget(1).name() == 'dock 2'
    assert c3.type() == 'tab'
    assert c3.widget(0).name() == 'dock 3'
    assert c3.widget(1).name() == 'dock 4'

    # test restore with docks already present
    a3 = da.DockArea()
    a3docks = []
    for i in range(1, 5):
        dock = da.Dock('dock %d' % i)
        a3docks.append(dock)
        a3.addDock(dock, 'right')
    a3.restoreState(state)
    assert a3.saveState() == state

    # test restore with extra docks present    
    a3 = da.DockArea()
    a3docks = []
    for i in [1, 2, 5, 4, 3]:
        dock = da.Dock('dock %d' % i)
        a3docks.append(dock)
        a3.addDock(dock, 'left')
    a3.restoreState(state)
    a3.printState()


    # test a more complex restore
    a4 = da.DockArea()
    state1 = {'float': [], 'main': 
        ('horizontal', [
            ('vertical', [
                ('horizontal', [
                    ('tab', [
                        ('dock', 'dock1', {}), 
                        ('dock', 'dock2', {}), 
                        ('dock', 'dock3', {}), 
                        ('dock', 'dock4', {})
                        ], {'index': 1}), 
                    ('vertical', [
                        ('dock', 'dock5', {}), 
                        ('horizontal', [
                            ('dock', 'dock6', {}), 
                            ('dock', 'dock7', {})
                            ], {'sizes': [184, 363]})
                        ], {'sizes': [355, 120]})
                    ], {'sizes': [9, 552]})
                ], {'sizes': [480]}), 
            ('dock', 'dock8', {})
            ], {'sizes': [566, 69]})
        }

    state2 = {'float': [], 'main': 
        ('horizontal', [
            ('vertical', [
                ('horizontal', [
                    ('dock', 'dock2', {}), 
                    ('vertical', [
                        ('dock', 'dock5', {}), 
                        ('horizontal', [
                            ('dock', 'dock6', {}), 
                            ('dock', 'dock7', {})
                            ], {'sizes': [492, 485]})
                        ], {'sizes': [936, 0]})
                    ], {'sizes': [172, 982]})
                ], {'sizes': [941]}), 
            ('vertical', [
                ('dock', 'dock8', {}), 
                ('dock', 'dock4', {}), 
                ('dock', 'dock1', {})
                ], {'sizes': [681, 225, 25]})
            ], {'sizes': [1159, 116]})}

    a4.restoreState(state1, missing='create')
    # dock3 not mentioned in restored state; stays in dockarea by default
    c, d = a4.findAll()
    assert d['dock3'].area is a4
    
    a4.restoreState(state2, missing='ignore', extra='float')
    a4.printState()

    c, d = a4.findAll()
    # dock3 not mentioned in restored state; goes to float due to `extra` argument
    assert d['dock3'].area is not a4
    assert d['dock1'].container() is d['dock4'].container() is d['dock8'].container()
    assert d['dock6'].container() is d['dock7'].container()
    assert a4 is d['dock2'].area is d['dock2'].container().container().container()
    assert a4 is d['dock5'].area is d['dock5'].container().container().container().container()

    # States should be the same with two exceptions:
    #   dock3 is in a float because it does not appear in state2
    #   a superfluous vertical splitter in state2 has been removed
    state4 = a4.saveState()
    state4['main'][1][0] = state4['main'][1][0][1][0]

    with pytest.raises(AssertionError):
        # this test doesn't work, likely due to clean_state not working as intended
        assert clean_state(state4['main']) == clean_state(state2['main'])


def clean_state(state):
    # return state dict with sizes removed
    ch = [clean_state(x) for x in state[1]] if isinstance(state[1], list) else state[1]
    state = (state[0], ch, {})
    return state
