import sys, string
#from mglutil.regression import testplus

from NetworkEditor.simpleNE import NetworkNode
from time import sleep

ed = None
withThreads = 0 # default is: multi-threading off

# allow for additional user input
if len(sys.argv):
    for myArg in sys.argv[1:]:
        if myArg[:11] == 'withThreads':
            withThreads = int(string.strip(myArg)[-1])


def setUp():
    from NetworkEditor.simpleNE import NetworkBuilder
    global ed
    ed = NetworkBuilder("test builder1", withShell=0,
                        visibleWidth=400, visibleHeight=300)
    ed.master.update()
    ed.configure(withThreads=withThreads)


from NetworkEditor.items import NetworkNode

class NodeWithParamWidget(NetworkNode):
    def __init__(self, name='ParamPanelNode', **kw):
        kw['name'] = name
        apply( NetworkNode.__init__, (self,), kw )
        self.widgetDescr['dial'] = {'class':'NEDial','name':None,
                                    'size':50, 'value':0, 'showLabel':1,
                                    'oneTurn':1, 'type':float }
        self.inputPortsDescr.append({'name':'dial', 'datatype':'None'})
        self.outputPortsDescr.append( {'name':'value', 'datatype':'float'} )
        code = """def doit(self, dial):
        if dial:
            self.outputData(value=dial)\n"""
        self.setFunction(code)

        
def tearDown():
    ed.master.after(1000, ed.exit_cb )


def pause(sleepTime=0.4):
    ed.master.update()
    sleep(sleepTime)

# helper function to add a node with NO widgets at all
def addPrintNode(x, y):
    from ViPEr.StandardNodes import Print
    node = Print()
    ed.currentNetwork.addNode(node, x, y)
    port = node.inputPorts[0]
    return node, port


# helper function to add a node with a widget in node
def addDialNode(x, y):
    from ViPEr.StandardNodes import DialNE
    node = DialNE()
    ed.currentNetwork.addNode(node, x, y)
    port = node.inputPorts[0]
    return node, port

# helper function to add a node with a widgets in parampanel
def addParamWidgetNode(x, y):
    node = NodeWithParamWidget()
    ed.currentNetwork.addNode(node, x, y)
    port = node.inputPorts[0]
    return node, port

# helper function to add any node
def addAnyNode(klass, x, y):
    eval("from ViPEr.StandardNodes import klass", locals(),
         globals())
    node = klass()
    ed.currentNetwork.addNode(node, x, y)
    return node


###################BEGIN TESTS###################################

def test_deleteWidget():
    node1, port1 = addDialNode(50, 50)
    port = node1.inputPorts[0]
    wdescr = port.deleteWidget()
    assert port.widget is None


def test_SpecialPorts():
    node1, port1 = addDialNode(100, 100)
    assert hasattr(node1, 'specialInputPorts')
    assert hasattr(node1, 'specialOutputPorts')

    # show and hide specialPorts
    node1.showSpecialPorts()
    pause()
    node1.hideSpecialPorts()
    pause()

    ed.currentNetwork.deleteNodes([node1])


def test_ConnectSpecialPorts():
    node1, port1 = addDialNode(100, 100)
    node1.showSpecialPorts()
    pause()

    node2, port2 = addDialNode(200, 200)
    node2.showSpecialPorts()
    pause()

    ed.currentNetwork.specialConnectNodes(node1, node2, 0, 0)
    pause()
    
    ed.currentNetwork.deleteNodes([node1, node2])
    

def test_inputPortHasNoDataAfterConnectionDeletion():
    # after deleting a connection, the inputport of the child node shall
    # no longer have data
    
    node1, port1 = addDialNode(50, 50)
    port1.widget.set(20.6, run=1)
    node1.network.waitForCompletion()
    assert node1.outputPorts[0].data==20.6

    node2, port2 = addPrintNode(100, 100)
    ip = node2.inputPorts[0].number
    op = node1.outputPorts[0].number
    ed.currentNetwork.connectNodes(node1, node2, op, ip)
    # upon connection, the print node inputport should have data
    node2.network.waitForCompletion()
    print '+++++++++++++++',node2.inputPorts[0].data
    assert node2.inputPorts[0].data[0] == 20.6
    pause()

    # delete connection
    c = port2.connections
    ed.currentNetwork.deleteConnections(c)
    # now port2 should no longer have data
    assert port2.data is None
    # port1 should still have data
    assert port1.data == 20.6
    
    ed.currentNetwork.deleteNodes([node1, node2])
    pause()

##
## THE FOLLOWING TESTS ARE MEANT TO EMULATE THE WAY THE OPERATIONS WOULD
## BE PERFORMED BY A USER USING THE GUI
##
def test_showHideWidgetInNode():
    node1, port1 = addDialNode(50, 50)
    port = node1.inputPorts[0]
    oldwidget = port.widget
    
    # show the widget in node
    node1.toggleNodeExpand_cb()
    pause()
    assert port.node.expandedIcon is True
    assert len(port.node.nodeWidgetsID)==1

    # hide the widget in node
    node1.toggleNodeExpand_cb()
    pause()
    assert port.widget.visibleInNode == 0
    assert port.widget == oldwidget
    
    # delete node
    ed.currentNetwork.deleteNodes([node1])

    # repeat test with node that has NO widget in node
    node1, port = addPrintNode(100, 100)
    node1.toggleNodeExpand_cb()
    # delete node
    ed.currentNetwork.deleteNodes([node1])
    pause()
    

def test_deleteNodeWithOpenEditors():
    node1, port = addDialNode(50, 50)

    # start node editor
    node1.edit()

    # start port editor
    port.portEditCB.invoke()
    portEd = node1.inputPorts[0].objEditor

    # start the code editor
    node1.objEditor.editFunction()
    pause()

    # delete node
    ed.currentNetwork.deleteNodes([node1])
    pause()


    ## repeat test with node that has NO widget in node
    node1, port = addPrintNode(100, 100)

    # start node editor
    node1.edit()

    # start port editor
    port.portEditCB.invoke()
    portEd = node1.inputPorts[0].objEditor

    # start the code editor
    node1.nodeEditor.editFunction()
    pause()

    # delete node
    ed.currentNetwork.deleteNodes([node1])
    pause()



def test_bindWidgetToParamPanel():
    node1, port = addPrintNode(50, 50)

    # start node editor
    node1.edit()

    # start port editor
    port.portEditCB.invoke()
    portEd = node1.inputPorts[0].objEditor
    
    # bind widget
    portEd.widgetType.set('NEEntry')
    portEd.masterTk.selectitem('ParamPanel')

    portEd.Apply() #'press' the Apply button
    pause()
    widgetsInNode = node1.getWidgetsForMaster('Node')
    widgetsInPanel = node1.getWidgetsForMaster('ParamPanel')
    assert len(widgetsInNode)==0
    assert len(widgetsInPanel)==1 # this must be 1
    assert len(node1.inputPorts)==1
    assert len(node1.widgetDescr) == 1

    # delete node
    ed.currentNetwork.deleteNodes([node1])
    pause()


def test_bindWidgetToNode():
    node1, port = addPrintNode(50, 50)

    # start node editor
    node1.edit()

    # start port editor
    port.portEditCB.invoke()
    portEd = node1.inputPorts[0].objEditor
    
    # bind widget
    portEd.widgetType.set('NEEntry')
    portEd.masterTk.selectitem('Node')

    portEd.Apply() #'press' the Apply button
    pause()
    widgetsInNode = node1.getWidgetsForMaster('Node')
    widgetsInPanel = node1.getWidgetsForMaster('ParamPanel')
    assert len(widgetsInNode)==1 # this must be 1
    assert len(widgetsInPanel)==0
    assert len(node1.inputPorts)==1
    assert len(node1.widgetDescr) == 1

    # delete node
    ed.currentNetwork.deleteNodes([node1])
    pause()

    
def test_unbindWhileHidden():
    node1, port = addDialNode(50, 50)

    # start node editor
    node1.edit()

    # start port editor
    port.portEditCB.invoke()
    portEd = node1.inputPorts[0].objEditor
    oldwidget = port.widget
    
    # unbind widget
    portEd.widgetType.set('Unbind')
    portEd.Apply() # 'press' the Apply button of the Editor
    
    widgetsInNode = node1.getWidgetsForMaster('Node')
    widgetsInPanel = node1.getWidgetsForMaster('ParamPanel')
    assert len(widgetsInNode)==0
    assert len(widgetsInPanel)==0
    assert len(node1.inputPorts)==1
    assert port.widget is None
    assert port.previousWidget is oldwidget
    
    # add a parent dial and check that values is output
    node2, port2 = addDialNode(100, 100)
    ip = node1.inputPorts[0].number
    op = node2.outputPorts[0].number
    ed.currentNetwork.connectNodes(node2, node1, op, ip)
    port2.widget.set(20.7, run=1)
    node2.network.waitForCompletion()
    assert node2.outputPorts[0].data==20.7

    # delete node
    ed.currentNetwork.deleteNodes([node1, node2])
    pause()

    ##
    ## repeat test with node with widget in ParamPanel
    node1, port = addParamWidgetNode(100, 50)

    # start node editor
    node1.edit()

    # start port editor
    port.portEditCB.invoke()
    portEd = node1.inputPorts[0].objEditor

    # unbind widget
    portEd.widgetType.set('Unbind')
    portEd.Apply() # 'press' the Apply button
    widgetsInNode = node1.getWidgetsForMaster('Node')
    widgetsInPanel = node1.getWidgetsForMaster('ParamPanel')
    assert len(widgetsInNode)==0
    assert len(widgetsInPanel)==0
    assert len(node1.inputPorts)==1

    # add a parent dial and check that values is output
    node2, port2 = addDialNode(100, 100)
    ip = node1.inputPorts[0].number
    op = node2.outputPorts[0].number
    ed.currentNetwork.connectNodes(node2, node1, op, ip)
    port2.widget.set(20.8, run=1)
    node2.network.waitForCompletion()
    assert node2.outputPorts[0].data==20.8

    # delete node
    ed.currentNetwork.deleteNodes([node1, node2])
    pause()


def test_unbindWhileVisible():
    node1, port = addDialNode(50, 50)

    # start node editor
    node1.edit()

    # start port editor
    port.portEditCB.invoke()
    portEd = node1.inputPorts[0].objEditor
    
    # show the widget in param panel
    node1.paramPanel.show()
    pause()
    assert node1.paramPanel.visible == 1

    # unbind widget
    portEd.widgetType.set('Unbind')
    portEd.Apply() #'press' the Apply button
    widgetsInNode = node1.getWidgetsForMaster('Node')
    widgetsInPanel = node1.getWidgetsForMaster('ParamPanel')
    assert len(widgetsInNode)==0
    assert len(widgetsInPanel)==0
    assert len(node1.inputPorts)==1

    # add a parent dial and check that values is output
    node2, port2 = addDialNode(100, 100)
    ip = node1.inputPorts[0].number
    op = node2.outputPorts[0].number
    ed.currentNetwork.connectNodes(node2, node1, op, ip)
    port2.widget.set(20.7, run=1)
    node2.network.waitForCompletion()
    assert node2.outputPorts[0].data==20.7

    # delete node
    ed.currentNetwork.deleteNodes([node1, node2])
    pause()

    ## repeat with node with widgets in Panel
    node1, port = addParamWidgetNode(100, 100)

    # start node editor
    node1.edit()

    # start port editor
    port.portEditCB.invoke()
    portEd = node1.inputPorts[0].objEditor
    
    # show the widget in param panel
    node1.paramPanel.show()
    pause()
    assert node1.paramPanel.visible == 1

    # unbind widget
    portEd.widgetType.set('Unbind')
    portEd.Apply() #'press' the Apply button
    widgetsInNode = node1.getWidgetsForMaster('Node')
    widgetsInPanel = node1.getWidgetsForMaster('ParamPanel')
    assert len(widgetsInNode)==0
    assert len(widgetsInPanel)==0
    assert len(node1.inputPorts)==1

    # add a parent dial and check that values is output
    node2, port2 = addDialNode(100, 100)
    ip = node1.inputPorts[0].number
    op = node2.outputPorts[0].number
    ed.currentNetwork.connectNodes(node2, node1, op, ip)
    port2.widget.set(20.8, run=1)
    node2.network.waitForCompletion()
    assert node2.outputPorts[0].data==20.8

    # delete node
    ed.currentNetwork.deleteNodes([node1, node2])
    pause()


def test_rebindNoParent():
    node1, port = addDialNode(50, 50)
    # start node editor
    node1.edit()
    # start port editor
    port.portEditCB.invoke()
    portEd = node1.inputPorts[0].objEditor
    # set the widget
    oldwidget = port.widget
    port.widget.set(20.7)
    # unbind widget
    portEd.widgetType.set('Unbind')
    portEd.Apply()
    # rebind
    portEd.widgetType.set('Rebind')
    portEd.Apply()
    assert port.widget == oldwidget
    port.widget.set(15.3, run=1)
    node1.network.waitForCompletion()
    assert node1.outputPorts[0].data==15.3

    # delete node
    ed.currentNetwork.deleteNodes([node1])
    pause()

    ## repeat test with node with widget in ParamPanel
    node1, port = addParamWidgetNode(50, 50)
    # start node editor
    node1.edit()
    # start port editor
    port.portEditCB.invoke()
    portEd = node1.inputPorts[0].objEditor
    # set the widget
    oldwidget = port.widget
    port.widget.set(20.8)
    # unbind widget
    portEd.widgetType.set('Unbind')
    portEd.Apply()
    # rebind
    portEd.widgetType.set('Rebind')
    portEd.Apply()
    #assert port.widget == oldwidget # FIXME: This is no longer true?
    assert port.widget.get() == 20.8 # make sure the widget has the old value
    port.widget.set(15.4, run=1)
    node1.network.waitForCompletion()
    assert node1.outputPorts[0].data==15.4

    # delete node
    ed.currentNetwork.deleteNodes([node1])
    pause()


def test_rebindWithParent():
    node1, port = addDialNode(50, 50)
    # start node editor
    node1.edit()
    # start port editor
    port.portEditCB.invoke()
    portEd = node1.inputPorts[0].objEditor
    # set the widget
    oldwidget = port.widget
    port.widget.set(20.6)
    # unbind widget
    portEd.widgetType.set('Unbind')
    portEd.Apply()
    # add a parent dial and check that values is output
    node2, port2 = addDialNode(100, 100)
    ip = node1.inputPorts[0].number
    op = node2.outputPorts[0].number
    ed.currentNetwork.connectNodes(node2, node1, op, ip)
    port2.widget.set(20.7, run=1)
    node2.network.waitForCompletion()
    assert node2.outputPorts[0].data==20.7

    # rebind
    portEd.widgetType.set('Rebind')
    portEd.Apply()
    assert port.widget == oldwidget
    port.widget.set(15.3, run=1)
    node1.network.waitForCompletion()
    assert node1.outputPorts[0].data==15.3

    # delete node
    ed.currentNetwork.deleteNodes([node1, node2])
    pause()

    ## repeat test with node with widget in ParamPanel
    node1, port = addParamWidgetNode(50, 50)
    # start node editor
    node1.edit()
    # start port editor
    port.portEditCB.invoke()
    portEd = node1.inputPorts[0].objEditor
    # set the widget
    oldwidget = port.widget
    port.widget.set(20.4)
    # unbind widget
    portEd.widgetType.set('Unbind')
    portEd.Apply()

    # add a parent dial and check that values is output
    node2, port2 = addDialNode(100, 100)
    ip = node1.inputPorts[0].number
    op = node2.outputPorts[0].number
    ed.currentNetwork.connectNodes(node2, node1, op, ip)
    port2.widget.set(20.8, run=1)
    node1.network.waitForCompletion()
    assert node2.outputPorts[0].data==20.8

    # rebind
    portEd.widgetType.set('Rebind')
    portEd.Apply()
    #assert port.widget == oldwidget # FIXME: this is no longer true?

    # test that widget has old value
    assert port.widget.get() == 20.4
    port.widget.set(15.4, run=1)
    node1.network.waitForCompletion()
    assert node1.outputPorts[0].data==15.4

    # delete node
    ed.currentNetwork.deleteNodes([node1, node2])
    pause()


def test_unbindWhileVisible():
    node1, port = addDialNode(50, 50)

    # start node editor
    node1.edit()

    # start port editor
    port.portEditCB.invoke()
    portEd = node1.inputPorts[0].objEditor
    
    # show the widget in param panel
    node1.paramPanel.show()
    pause()
    assert node1.paramPanel.visible == 1

    # unbind widget
    portEd.widgetType.set('Unbind')
    portEd.Apply()

    widgetsInNode = node1.getWidgetsForMaster('Node')
    widgetsInPanel = node1.getWidgetsForMaster('ParamPanel')
    assert len(widgetsInNode)==0
    assert len(widgetsInPanel)==0
    assert len(node1.inputPorts)==1

    # add a parent dial and check that values is output
    node2, port2 = addDialNode(100, 100)
    ip = node1.inputPorts[0].number
    op = node2.outputPorts[0].number
    ed.currentNetwork.connectNodes(node2, node1, op, ip)
    port2.widget.set(20.7, run=1)
    node2.network.waitForCompletion()
    assert node2.outputPorts[0].data==20.7

    # delete node
    ed.currentNetwork.deleteNodes([node1, node2])
    pause()

    ## repeat with node with widgets in Panel
    node1, port = addParamWidgetNode(100, 100)

    # start node editor
    node1.edit()

    # start port editor
    port.portEditCB.invoke()
    portEd = node1.inputPorts[0].objEditor
    
    # show the widget in param panel
    node1.paramPanel.show()
    pause()
    assert node1.paramPanel.visible == 1

    # unbind widget
    portEd.widgetType.set('Unbind')
    portEd.Apply()

    widgetsInNode = node1.getWidgetsForMaster('Node')
    widgetsInPanel = node1.getWidgetsForMaster('ParamPanel')
    assert len(widgetsInNode)==0
    assert len(widgetsInPanel)==0
    assert len(node1.inputPorts)==1

    # add a parent dial and check that values is output
    node2, port2 = addDialNode(100, 100)
    ip = node1.inputPorts[0].number
    op = node2.outputPorts[0].number
    ed.currentNetwork.connectNodes(node2, node1, op, ip)
    port2.widget.set(20.8, run=1)
    node2.network.waitForCompletion()
    assert node2.outputPorts[0].data==20.8

    # delete node
    ed.currentNetwork.deleteNodes([node1, node2])
    pause()



    
##  def test_bindWhileBound():
##      # overwrite a widget
##      from ViPEr.StandardNodes import EntryNE
##      from NetworkEditor.widgets import NEEntry, NEXYZGUI
##      node1 = EntryNE()
##      ed.currentNetwork.addNode(node1, 50, 50)
##      node1.edit()
##      port = node1.inputPorts[0]
##      port.portEditCB.invoke()
##      portEd = node1.inputPorts[0].objEditor

##      # rebind widget
##      portEd.widgetType.set('NEXYZGUI')
##      pause()
##      assert portEd.widgetType.get(0) == 'Unbind'
##      assert portEd.widgetType.get(1) == 'Rebind'
##      assert isinstance(port.previousWidget, NEEntry)
##      assert isinstance(port.widget, NEXYZGUI)

##      # delete node
##      ed.currentNetwork.deleteNodes([node1])
##      pause()
    

##  def test_displayPortEditorNoWidget():
##      from ViPEr.StandardNodes import Print
##      node1 = Print()
##      ed.currentNetwork.addNode(node1, 50, 50)

##      # show node editor panel
##      node1.edit()
##      from NetworkEditor.Editor import NodeEditor
##      pause()
##      assert isinstance(node1.objEditor, NodeEditor)

##      # show port editor for first input port
##      port = node1.inputPorts[0]
##      #node1.objEditor.editPort(port)
##      port.portEditCB.invoke()
##      pause()
##      from NetworkEditor.Editor import PortEditor
##      portEd = node1.inputPorts[0].objEditor
##      assert isinstance(portEd, PortEditor)

##      # check that first entry in widgets is NOT Unbind
##      assert portEd.widgetType.get(0) != 'Unbind'
    
##      # hide objEditor
##      node1.inputPorts[0].objEditor.dismiss()
##      assert node1.inputPorts[0].objEditor is None
    
##      # hide node editor panel
##      node1.objEditor.dismiss()
##      assert node1.objEditor is None
##      pause()

##      # delete node
##      ed.currentNetwork.deleteNodes([node1])
##      pause()
    


    
##  def test_displayPortEditorWithWidget():
##      from ViPEr.StandardNodes import EntryNE
##      node1 = EntryNE()
##      ed.currentNetwork.addNode(node1, 50, 50)

##      # show node editor panel
##      node1.edit()
##      from NetworkEditor.Editor import NodeEditor
##      pause()
##      assert isinstance(node1.objEditor, NodeEditor)

##      # show port editor for first input port
##      port = node1.inputPorts[0]
##      #node1.objEditor.editPort(port)
##      port.portEditCB.invoke()
##      pause()
##      from NetworkEditor.Editor import PortEditor
##      portEd = node1.inputPorts[0].objEditor
##      assert isinstance(portEd, PortEditor)

##      # check that first entry in widgets is Unbind
##      assert portEd.widgetType.get(0) == 'Unbind'

##      # check that the widget combobox is initialized
##      assert portEd.widgetType.get() == 'NEEntry'
    
##      # check that the type combobox is initialized
##      assert portEd.dataType.get() == 'None'
    
##      # unbind widget
##      portEd.widgetType.set('Unbind')
##      pause()
##      assert port.widget is None
##      widgetsInNode = node1.getWidgetsForMaster('Node')
##      widgetsInPanel = node1.getWidgetsForMaster('ParamPanel')
    
##      assert len(widgetsInNode) == 0
##      assert portEd.widgetType.get(0) == 'Rebind'
##      from NetworkEditor.widgets import NEEntry
##      assert isinstance(port.previousWidget, NEEntry)

##      # rebind widget
##      portEd.widgetType.set('Rebind')
##      pause()
##      assert isinstance(port.widget, NEEntry)
##      assert len(widgetsInNode) == 1
##      assert portEd.widgetType.get(0) == 'Unbind'
    
##      # hide objEditor
##      node1.inputPorts[0].objEditor.dismiss()
##      assert node1.inputPorts[0].objEditor is None
    
##      # hide node editor panel
##      node1.objEditor.dismiss()
##      assert node1.objEditor is None
##      pause()

##      # delete node
##      ed.currentNetwork.deleteNodes([node1])
##      pause()
    

## harness = testplus.TestHarness( "portsAndWidgets",
##                                 connect = (startEditor, (), {}),
##                                 funs = testplus.testcollect( globals()),
##                                 disconnect = quitEditor
##                                 )

## if __name__ == '__main__':
##     testplus.chdir()
##     print harness
##     sys.exit( len( harness))
