#########################################################################
#
# Date: Jul 2004  Author: Daniel Stoffler
#
#       stoffler@scripps.edu
#
#       The Scripps Research Institute (TSRI)
#       Molecular Graphics Lab
#       La Jolla, CA 92037, USA
#
# Copyright: Daniel Stoffler, and TSRI
#
#########################################################################

import sys, os
from time import sleep

from NetworkEditor.net import Network
from NetworkEditor.simpleNE import NetworkNode, NetworkBuilder
from NetworkEditor.Tests.nodes import DialNode, PrintNode, PassNode, \
     Operator3Node
from NetworkEditor.Tests import helper

ed = None

###############################
## implement setUp and tearDown
###############################

def setUp():
    global ed
    ed = NetworkBuilder("test save and load networks", withShell=0,
                        visibleWidth=400, visibleHeight=300)
    ed.master.update()
    ed.configure(withThreads=0)


def tearDown():
    ed.exit_cb()
    import gc
    gc.collect()

##########################
## Helper methods
##########################

def pause(sleepTime=None):
    if sleepTime is None:
        from NetworkEditor.Tests import pauseLength as sleepTime
    ed.master.update()
    sleep(sleepTime)


##########################
## Tests
##########################


############################################################################
###################### TESTS FOR SAVING A NETWORK ##########################
############################################################################
    
def test_01_saveLoadDialUnchanged():
    # Test if we can save and restore a dial node, with a value set to 42
    # and that the node (and the value) are restored properly
    net = ed.currentNetwork
    node1 = helper.makeAndAddNode(net, nType='Dial', name='dial', posx=18, posy=20)
    w = node1.inputPorts[0].widget
    w.set(42.0)
    assert w.get() == 42.0,"Expected 42.0, got %s"%w.get()
    # make sure there is no such file
    os.system("rm -f tmpDial_net.py*")
    # save the file
    ed.saveNetwork('tmpDial_net.py')
    # and delete the network
    ed.deleteNetwork(ed.currentNetwork)
    pause()
    # load the file
    ed.loadNetwork('tmpDial_net.py')
    pause()
    assert len(ed.currentNetwork.nodes) == 1,\
           "Expected 1, got %s"%len(ed.currentNetwork.nodes)
    w = ed.currentNetwork.nodes[0].inputPorts[0].widget
    assert w.get() == 42.0,"Expected 42.0, got %s"%w.get()
    # we clean up after us...
    os.system("rm -f tmpDial_net.py*")


def test_02_saveLoadDialUnbindWidget():
    # Test if we can save and restore a dial node with the dial widget set to
    # 42 and then unbind the widget
    net = ed.currentNetwork
    node1 = helper.makeAndAddNode(net, nType='Dial', name='dial', posx=18, posy=20)
    w = node1.inputPorts[0].widget
    w.set(42.0)
    node1.inputPorts[0].unbindWidget()
    # make sure there is no such file
    os.system("rm -f tmpDial_net.py*")
    # save the file
    ed.saveNetwork('tmpDial_net.py')
    # and delete the network
    ed.deleteNetwork(ed.currentNetwork)
    pause()
    # load the file
    ed.loadNetwork('tmpDial_net.py')
    pause()
    node1 =  ed.currentNetwork.nodes[0]
    assert node1.inputPorts[0].widget is None,\
           "Expected None, got %s"%node1.inputPorts[0].widget
    assert node1.inputPorts[0]._previousWidgetDescr is not None,\
          "_previousWidgetDescr is %s"%node1.inputPorts[0]._previousWidgetDescr
    # we clean up after us...
    os.system("rm -f tmpDial_net.py*")


def test_03_saveLoadDialUnbindRebindWidget():
    # Test if we can save and restore a dial node with the dial widget set to
    # 42 and then unbound, and rebind the widget after restoring and the value
    # set back properly
    net = ed.currentNetwork
    node1 = helper.makeAndAddNode(net, nType='Dial', name='dial', posx=18,
                                  posy=20)
    w = node1.inputPorts[0].widget
    w.set(42.0)
    node1.inputPorts[0].unbindWidget()
    # make sure there is no such file
    os.system("rm -f tmpDial_net.py*")
    # save the file
    ed.saveNetwork('tmpDial_net.py')
    # and delete the network
    ed.deleteNetwork(ed.currentNetwork)
    pause()
    # load the file
    ed.loadNetwork('tmpDial_net.py')
    pause()
    assert len(ed.currentNetwork.nodes) == 1,\
           "Expected 1, got %s"%len(ed.currentNetwork.nodes)
    node1 =  ed.currentNetwork.nodes[0]
    assert node1.inputPorts[0].widget is None,\
           "Expected None, got %s"%node1.inputPorts[0].widget
    assert node1.inputPorts[0]._previousWidgetDescr is not None,\
         "_previousWidgetDescr is %s"%node1.inputPorts[0]._previousWidgetDescr
    node1.inputPorts[0].rebindWidget()
    assert node1.inputPorts[0].widget is not None,\
           "widget is %s"%node1.inputPorts[0].widget
    w = node1.inputPorts[0].widget
    assert w.get() == 42.0,"Expected 42.0, got %s"%w.get()
    # we clean up after us...
    os.system("rm -f tmpDial_net.py*")


def test_04_saveLoadDialDeleteWidget():
    # Test if we can save and restore a dial node with the dial widget set to
    # 42 and then delete the widget
    net = ed.currentNetwork
    node1 = helper.makeAndAddNode(net, nType='Dial', name='dial', posx=18,
                                  posy=20)
    w = node1.inputPorts[0].widget
    w.set(42.0)
    node1.inputPorts[0].deleteWidget()
    # make sure there is no such file
    os.system("rm -f tmpDial_net.py*")
    # save the file
    ed.saveNetwork('tmpDial_net.py')
    # and delete the network
    ed.deleteNetwork(ed.currentNetwork)
    pause()
    # load the file
    ed.loadNetwork('tmpDial_net.py')
    pause()
    node1 =  ed.currentNetwork.nodes[0]
    assert node1.inputPorts[0].widget is None,\
           "Expected None, got %s"%node1.inputPorts[0].widget
    assert node1.inputPorts[0]._previousWidgetDescr is None,\
           "Expected None, got %s"%node1.inputPorts[0]._previousWidgetDescr
    # we clean up after us...
    os.system("rm -f tmpDial_net.py*")


def test_05_saveLoadCreateNewWidget():
    # Test if we can save and restore a pass node to which we bind a widget
    net = ed.currentNetwork
    node1 = helper.makeAndAddNode(net,nType='Pass', name='Pass', posx=18, posy=20)
    node1.inputPorts[0].createWidget(descr={'initialValue': 0.0, 'master': 'ParamPanel', 'increment': 0.0, 'labelCfg': {'text': 'label'}, 'class': 'NEDial'})
    assert node1.inputPorts[0].widget is not None,\
           "widget is %s"%node1.inputPorts[0].widget
    node1.inputPorts[0].widget.set(42.0,0)
    # make sure there is no such file
    os.system("rm -f tmpDial_net.py*")
    # save the file
    ed.saveNetwork('tmpDial_net.py')
    # and delete the network
    ed.deleteNetwork(ed.currentNetwork)
    pause()
    # load the file
    ed.loadNetwork('tmpDial_net.py')
    pause()
    node1 =  ed.currentNetwork.nodes[0]
    assert node1.inputPorts[0].widget is not None,\
           "widget is %s"%node1.inputPorts[0].widget
    assert node1.inputPorts[0].widget.get() == 42.0,\
           "Expected 42.0, got s"%node1.inputPorts[0].widget.get()
    # we clean up after us...
    os.system("rm -f tmpDial_net.py*")


def test_06_saveLoadNetwork():
    # first, build a network: dial->pass->print
    # test, if we can reload this (with connections
    node1, node2, node3 = helper.buildSmallNetwork(ed.currentNetwork)
    pause()
    # make sure there is no such file
    os.system("rm -f tmpFoo_net.py*")
    # save the file
    ed.saveNetwork('tmpFoo_net.py')
    # and delete the network
    ed.deleteNetwork(ed.currentNetwork)
    pause()
    # load the file
    ed.loadNetwork('tmpFoo_net.py')
    pause()
    assert len(ed.currentNetwork.nodes) == 3,\
           "Expected 3, got %s"%len(ed.currentNetwork.nodes)
    assert ed.currentNetwork.name == 'tmpFoo',\
           "Expected 'tmpFoo', got '%s'"%ed.currentNetwork.name
    # the nice people we are, we clean up after us
    os.system("rm -f tmpFoo_net.py*")


def test_07_saveLoadNetworkWithWidgetValue():
    net = ed.currentNetwork
    # first, build a network: dial->pass->print
    node1, node2, node3 = helper.buildSmallNetwork(net)
    pause()
    # now change the widget value of the dial node
    node1.inputPorts[0].widget.set(42.0)
    # make sure there is no such file
    os.system("rm -f tmpFoo_net.py*")
    # save the file
    ed.saveNetwork('tmpFoo_net.py')
    # and delete the network
    ed.deleteNetwork(ed.currentNetwork)
    pause()
    # load the file
    ed.loadNetwork('tmpFoo_net.py')
    pause()
    # run the network
    ed.currentNetwork.run()
    # check for the value 42.0, find the dial node:
    n = None
    for n in ed.currentNetwork.nodes:
        if n.name == 'dial':
            break
    # did we find the node?
    assert n is not None,"n is: %s"%n
    assert n.outputPorts[0].data == 42.0,\
           "Expected 42.0, got %s"%n.outputPorts[0].data
    # the nice people we are, we clean up after us
    os.system("rm -f tmpFoo_net.py*")


def test_08_saveRestoreSpecialPorts():
    # this tests if we can save and restore a network with nodes that
    # have special ports visible, and the special ports are connected
    # add 2 nodes, show special ports, connect them
    net = ed.currentNetwork
    node1 = helper.makeAndAddNode(net, nType='Dial', name='dial', posx=21, posy=40)
    node2 = helper.makeAndAddNode(net, nType='Pass', name='pass', posx=153, posy=149)
    node1.showSpecialPorts()
    node2.showSpecialPorts()
    node1.network.specialConnectNodes(node1, node2, "trigger", "runNode")
    # remove previous tmp file (if present)
    os.system("rm -f tmpSpecialConnections_net.py*")
    # save network
    ed.saveNetwork('tmpSpecialConnections_net.py')
    pause()
    # delete network
    ed.deleteNetwork(ed.currentNetwork)
    # load saved network
    ed.loadNetwork('tmpSpecialConnections_net.py')
    # check if we could load it
    assert ed.currentNetwork.name == 'tmpSpecialConnections',\
           "Expected 'tmpSpecialConnections', got '%s'"%ed.currentNetwork.name
    assert len(ed.currentNetwork.nodes) == 2,\
           "Expected 2, got %s"%len(ed.currentNetwork.nodes)
    # remove tmp file
    os.system("rm -f tmpSpecialConnections_net.py*")


def test_09_saveLoadDialDeleteInputPort():
    # Test if we can save and restore a dial node with a deleted input port
    net = ed.currentNetwork
    node1 = helper.makeAndAddNode(net, nType='Dial', name='dial', posx=18, posy=20)
    node1.deletePort(node1.inputPortByName['dial'])
    code = """def doit(self):
    pass
"""
    node1.setFunction(code)
    # make sure there is no such file
    os.system("rm -f tmpDial_net.py*")
    # save the file
    ed.saveNetwork('tmpDial_net.py')
    # and delete the network
    ed.deleteNetwork(ed.currentNetwork)
    pause()
    # load the file
    ed.loadNetwork('tmpDial_net.py')
    pause()
    node1 =  ed.currentNetwork.nodes[0]
    assert len(node1.inputPorts) == 0,\
           "Expected 0, got %s"%len(node1.inputPorts)
    # we clean up after us...
    os.system("rm -f tmpDial_net.py*")


def test_10_saveLoadDialDeleteOutputPort():
    # Test if we can save and restore a dial node with a deleted output port
    net = ed.currentNetwork
    node1 = helper.makeAndAddNode(net, nType='Dial', name='dial', posx=18, posy=20)
    node1.deletePort(node1.outputPortByName['value'])
    code = """def doit(self, dial):
    pass
"""
    node1.setFunction(code)
    # make sure there is no such file
    os.system("rm -f tmpDial_net.py*")
    # save the file
    ed.saveNetwork('tmpDial_net.py')
    # and delete the network
    ed.deleteNetwork(ed.currentNetwork)
    pause()
    # load the file
    ed.loadNetwork('tmpDial_net.py')
    pause()
    node1 =  ed.currentNetwork.nodes[0]
    assert len(node1.outputPorts) == 0,\
           "Expected 0, got %s"%len(node1.outputPorts)
    # we clean up after us...
    os.system("rm -f tmpDial_net.py*")


def test_11_saveLoadDeleteMultiplePorts():
    # Test if we can save and restore an operator3 node with the first and
    # and the last port deleted
    net = ed.currentNetwork
    node1 = helper.makeAndAddNode(net, nType='Operator3', name='Operator3', posx=18, posy=20)
    node1.deletePort(node1.inputPortByName['data'])
    node1.deletePort(node1.inputPortByName['applyToElements'])
    code = """def doit(self, operation, from_, to_):
    pass
"""
    node1.setFunction(code)
    # make sure there is no such file
    os.system("rm -f tmpOp3_net.py*")
    # save the file
    ed.saveNetwork('tmpOp3_net.py')
    # and delete the network
    ed.deleteNetwork(ed.currentNetwork)
    pause()
    # load the file
    ed.loadNetwork('tmpOp3_net.py')
    pause()
    node1 =  ed.currentNetwork.nodes[0]
    assert len(node1.inputPorts) == 3,\
           "Expected 3, got %s"%len(node1.inputPorts) == 3
    assert node1.inputPorts[0].name == 'operation',\
           "Expected 'operation', got '%s'"%node1.inputPorts[0].name
    assert node1.inputPorts[1].name == 'from_',\
           "Expected 'from_', got '%s'"%node1.inputPorts[1].name
    assert node1.inputPorts[2].name == 'to_',\
           "Expected 'to_', got '%s'"%node1.inputPorts[2].name 
    # we clean up after us...
    os.system("rm -f tmpOp3_net.py*")


def test_12_saveLoadAddInputPort():
    # Test if we can save and restore a dial node with a new input port
    net = ed.currentNetwork
    node1 = helper.makeAndAddNode(net, nType='Dial', name='dial',
                                   posx=18, posy=20)
    # 1 port
    assert len(node1.inputPorts) == 1,\
           "Expected 1, got %s"%len(node1.inputPorts)
    p = node1.addInputPort(name='NewTestPort')
    # 2 ports
    assert len(node1.inputPorts) == 2,\
           "Expected 2, gpt %s"%len(node1.inputPorts)
    # make sure there is no such file
    os.system("rm -f tmpDial_net.py*")
    # save the file
    ed.saveNetwork('tmpDial_net.py')
    # and delete the network
    ed.deleteNetwork(ed.currentNetwork)
    pause()
    # load the file
    ed.loadNetwork('tmpDial_net.py')
    pause()
    # do we have 2 input ports?
    node1 = ed.currentNetwork.nodes[0]
    assert len(node1.inputPorts) == 2,\
           "Expected 2, got %s"%len(node1.inputPorts)
    # is the code updated?
    assert "NewTestPort" in node1.sourceCode,\
           "node1.sourceCode is: %s"%node1.sourceCode
    # we clean up after us...
    os.system("rm -f tmpDial_net.py*")   


def test_13_saveLoadAddOutputPort():
    # Test if we can save and restore a dial node with a new output port
    net = ed.currentNetwork
    node1 = helper.makeAndAddNode(net, nType='Dial', name='dial',
                                   posx=18, posy=20)
    oldcode = node1.sourceCode
    # 1 port
    assert len(node1.outputPorts) == 1,\
           "Expected 1, got %s"%len(node1.outputPorts)
    p = node1.addOutputPort(name='NewTestPort')
    # 2 ports
    assert len(node1.outputPorts) == 2,\
           "Expected 2, got %s"%len(node1.outputPorts)
    # make sure there is no such file
    os.system("rm -f tmpDial_net.py*")
    # save the file
    ed.saveNetwork('tmpDial_net.py')
    # and delete the network
    ed.deleteNetwork(ed.currentNetwork)
    pause()
    # load the file
    ed.loadNetwork('tmpDial_net.py')
    pause()
    # do we have 2 input ports?
    node1 = ed.currentNetwork.nodes[0]
    newcode = node1.sourceCode
    assert len(node1.outputPorts) == 2,\
           "Expected 2, got %s"%len(node1.outputPorts)
    # we clean up after us...
    os.system("rm -f tmpDial_net.py*") 


def test_14_saveLoadMacroNoConnections():
    """this tests if we can save and restore a macro node with no nodes in the
macro network, and no connections."""
    net = Network('save macro')
    ed.addNetwork(net)
    # add macro node
    from NetworkEditor.macros import MacroNode
    node0 = MacroNode(name='test macro')
    net.addNode(node0, 200, 200)
    node0.shrink()
    # make sure there is no such file
    os.system("rm -f tmpMacro_net.py*")
    # save the file
    ed.saveNetwork('tmpMacro_net.py')
    # and delete the network
    ed.deleteNetwork(net)
    pause()
    # load the file
    ed.loadNetwork('tmpMacro_net.py')
    pause()
    # we clean up after us...
    os.system("rm -f tmpMacro_net.py*") 
    # is macro node here?
    assert len(ed.currentNetwork.nodes) == 1
