#########################################################################
#
# Date: Aug 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
from Vision.VPE import VisualProgramingEnvironment
from Vision.StandardNodes import RegressionTestMacro, DialNE, stdlib

ed = None

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

def setUp():
    global ed
    ed = VisualProgramingEnvironment("test node library macros",
                                      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=0.1):
    ed.master.update()
    sleep(sleepTime)


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


def test_01_saveLoadNodeLibraryMacro():
    # Test if we can save and reload a node library macro
    net = ed.currentNetwork
    filename = "tmpLibMacro1_net.py"
    # Important: since we did not drag-and-drop the node, make sure we pass
    # the node's library
    node1 = RegressionTestMacro()
    net.addNode(node1, 220, 70)
    # make sure there is no such file
    os.system("rm -f %s"%filename)
    # save the file
    ed.saveNetwork(filename)
    # and delete the network
    ed.deleteNetwork(ed.currentNetwork)
    pause()
    # load the file
    ed.loadNetwork(filename)
    # could we load macro node?
    net = ed.currentNetwork
    assert len(net.nodes) == 1,"Expected 1, got %s"%len(net.nodes)
    macro = net.nodes[0]
    assert macro.name == 'NodeLibraryMacro',\
           "Expected 'NodeLibraryMacro', got '%s'"%macro.name
    macro.expand()
    pause()
    # do we have the nodes inside the macro network?
    macroNet = macro.macroNetwork
    assert len(macroNet.nodes) == 5,"Expected 5, got %s"%len(macroNet.nodes)
    assert macroNet.nodes[2].name == 'Pass1',\
           "Expected 'Pass1', got '%s'"%macroNet.nodes[2].name
    assert macroNet.nodes[3].name == 'Pass2',\
           "Expected 'Pass2', got '%s'"%macroNet.nodes[3].name
    assert macroNet.nodes[4].name == 'Dial',\
           "Expected 'Dial', got '%s'"%macroNet.nodes[4].name
    assert len(macroNet.connections) == 4,\
           "Expected 4, got %s"%len(macroNet.connections)
    opNode = macroNet.nodes[1]
    assert len(opNode.inputPorts) == 3,\
           "Expected 3, got %s"%len(opNode.inputPorts) 
    assert opNode.inputPorts[2].singleConnection == True,\
           "Expected True, got %s"%opNode.inputPorts[2].singleConnection
    macro.shrink()
    # we clean up after us...
    os.system("rm -f %s"%filename)


def test_02_saveLoadWidgetValueChanged():
    # Test if we can set the widget value, save and reload a node library
    # macro, and properly restore the widget value
    net = ed.currentNetwork
    filename = "tmpLibMacro2_net.py"
    # Important: since we did not drag-and-drop the node, make sure we pass
    # the node's library
    node1 = RegressionTestMacro()
    net.addNode(node1, 220, 70)
    macro = net.nodes[0]
    dialNode = macro.macroNetwork.nodes[4]
    dialNode.inputPorts[0].widget.set(42.0)
    # make sure there is no such file
    os.system("rm -f %s"%filename)
    # save the file
    ed.saveNetwork(filename)
    # and delete the network
    ed.deleteNetwork(ed.currentNetwork)
    pause()
    # load the file
    ed.loadNetwork(filename)
    net = ed.currentNetwork
    macro = net.nodes[0]
    dialNode = macro.macroNetwork.nodes[4]
    assert dialNode.inputPorts[0].widget.get() == 42.0,\
           "Expected 42.0, got %s"%dialNode.inputPorts[0].widget.get()
    # we clean up after us...
    os.system("rm -f %s"%filename)


def test_03_saveLoadComputeFunctionChanged():
    # Test if we can change the compute function in Pass1, save and reload a
    # node library macro, and properly restore the macro and Pass1 with the
    # modified compute function
    filename = 'tmpLibMacro3_net.py'
    net = ed.currentNetwork
    # Important: since we did not drag-and-drop the node, make sure we pass
    # the node's library
    node1 = RegressionTestMacro()
    net.addNode(node1, 220, 70)
    macro = net.nodes[0]
    pass1Node = macro.macroNetwork.nodes[2]
    code = """def doit(self, in1):
    print '''Hello'''\n"""
    pass1Node.configure(function=code)
    # make sure there is no such file
    os.system("rm -f %s"%filename)
    # save the file
    ed.saveNetwork(filename)
    # and delete the network
    ed.deleteNetwork(ed.currentNetwork)
    pause()
    # load the file
    ed.loadNetwork(filename)
    net = ed.currentNetwork
    macro = net.nodes[0]
    pass1Node = macro.macroNetwork.nodes[2]
    assert pass1Node.sourceCode == code,\
           "code = %s\npass1Node.sourceCode = %s"%(code, pass1Node.sourceCode)
    # we clean up after us...
    os.system("rm -f %s"%filename)


def test_04_saveLoadDeleted1Node():
    # Test if we can delete one node, and restore with this node missing
    # Deleting the node automatically deletes the connection which
    # automatically deletes the port on MacroOutputNode and MacroNode
    filename = 'tmpLibMacro4_net.py'
    net = ed.currentNetwork
    # Important: since we did not drag-and-drop the node, make sure we pass
    # the node's library
    node1 = RegressionTestMacro()
    net.addNode(node1, 220, 70)
    macro = net.nodes[0]
    # this node has 2 output ports
    assert len(macro.outputPorts) == 2,\
           "Expected 2, got %s"%len(macro.outputPorts)
    dialNode = macro.macroNetwork.nodes[4]
    macro.macroNetwork.deleteNodes([dialNode])
    # make sure there is no such file
    os.system("rm -f %s"%filename)
    # save the file
    ed.saveNetwork(filename)
    # and delete the network
    ed.deleteNetwork(ed.currentNetwork)
    pause()
    # load the file
    ed.loadNetwork(filename)
    net = ed.currentNetwork
    macro = net.nodes[0]
    macroNet = macro.macroNetwork
    # dial deleted?
    assert len(macroNet.nodes) == 4,\
           "Expected 4, got %s"%len(macroNet.nodes)
    # connection deleted?
    assert len(macroNet.connections) == 3,\
           "Expected 3, got %s"%len(macroNet.connections)
    # port deleted in MacroOutputNode?
    opNode = macroNet.nodes[1]
    assert len(opNode.inputPorts) == 2,\
           "Expected 2, got %s"%len(opNode.inputPorts)
    # port deleted in MacroNode?
    assert len(macro.outputPorts) == 1,\
           "Expected 1, got %s"%len(macro.outputPorts)
    # we clean up after us...
    os.system("rm -f %s"%filename)


def test_05_saveLoadDeleted1Connection():
    # Test if we can delete one connection, and restore with this connection
    # missing. Deleting a connection to a MacroInput- or OutputNode
    # automatically deletes the port. We delete the connection from Pass1
    # to MacroInputNode
    filename = 'tmpLibMacro5_net.py'
    net = ed.currentNetwork
    # Important: since we did not drag-and-drop the node, make sure we pass
    # the node's library
    node1 = RegressionTestMacro()
    net.addNode(node1, 220, 70)
    macro = net.nodes[0]
    # this node has 1 port
    assert len(macro.inputPorts) == 1,\
           "Expected 1, got %s"%len(macro.inputPorts)
    pass1Node = macro.macroNetwork.nodes[2]
    conn = pass1Node.inputPorts[0].connections
    macro.macroNetwork.deleteConnections(conn)
    # make sure there is no such file
    os.system("rm -f %s"%filename)
    # save the file
    ed.saveNetwork(filename)
    # and delete the network
    ed.deleteNetwork(ed.currentNetwork)
    pause()
    # load the file
    ed.loadNetwork(filename)
    net = ed.currentNetwork
    macro = net.nodes[0]
    macroNet = macro.macroNetwork
    # connection deleted?
    assert len(macroNet.connections) == 3,\
           "Expected 3, got %s"%len(macroNet.connections)
    # port deleted in MacroInputNode?
    ipNode = macroNet.nodes[0]
    assert len(ipNode.outputPorts) == 1,\
           "Expected 1, got %s"%len(ipNode.outputPorts)
    # port deleted in MacroNode?
    assert len(macro.inputPorts) == 0,\
           "Expected 0, got %s"%len(macro.inputPorts)
    # we clean up after us...
    os.system("rm -f %s"%filename)


def test_06_saveLoadAddNewNode():
    # Test if we can save/restore a node libary macro with a new Dial node
    # added, which we connect to the MacroOutputNode
    filename = 'tmpLibMacro6_net.py'
    net = ed.currentNetwork
    # Important: since we did not drag-and-drop the node, make sure we pass
    # the node's library
    node1 = RegressionTestMacro()
    net.addNode(node1, 220, 70)
    macro = net.nodes[0]
    newNode = DialNE(library=stdlib)
    macro.macroNetwork.addNode(newNode,250,15)
    opNode = macro.macroNetwork.opNode
    macro.macroNetwork.connectNodes(newNode, opNode, 'value', 'new')
    # make sure there is no such file
    os.system("rm -f %s"%filename)
    # save the file
    ed.saveNetwork(filename)
    # and delete the network
    ed.deleteNetwork(ed.currentNetwork)
    pause()
    # load the file
    ed.loadNetwork(filename)
    net = ed.currentNetwork
    macro = net.nodes[0]
    macroNet = macro.macroNetwork
    # node here?
    assert len(macroNet.nodes) == 6,\
           "Expected 6, got %s"%len(macroNet.nodes)
    # connection and port created?
    opNode = macroNet.opNode
    assert len(opNode.inputPorts) == 4,\
           "Expected 4, got %s"%len(opNode.inputPorts)
    assert len(macro.outputPorts) == 3,\
           "Expected 3, got %s"%len(macro.outputPorts)
    # we clean up after us...
    os.system("rm -f %s"%filename)


def test_07_cutPasteInMainNet():
    # Test if we can cut and paste nodes inside a node library macro into
    # the main network, back into the macro, reconnect, save-and-restore?
    filename = 'tmpLibMacro7_net.py'
    net = ed.currentNetwork
    # Important: since we did not drag-and-drop the node, make sure we pass
    # the node's library
    node1 = RegressionTestMacro()
    net.addNode(node1, 220, 70)
    node1.expand()
    nodes = node1.macroNetwork.nodes
    node1.macroNetwork.selectNodes(nodes)
    pause()
    net.editor.cutNetwork_cb()
    pause()
    # although all nodes were selected, we do not cut the MacroInput- and
    # OutputNode
    assert len(node1.macroNetwork.nodes) == 2,\
           "Expected 2, got %s"%len(node1.macroNetwork.nodes)
    node1.shrink()
    net.editor.pasteNetwork_cb()
    pause()
    # did we paste the nodes?
    assert len(net.nodes) == 4, "Expected 4, got %s"%len(net.nodes)
    # are the pasted nodes still selected?
    assert len(net.selectedNodes) == 3,\
           "Expected 3, got %s"%len(net.selectedNodes)
    # did the connection between Pass1 and Pass2 survive?
    pass1 = net.nodes[1]
    pass2 = net.nodes[2]
    assert len(pass1.outputPorts[0].connections) == 1,\
           "Expected 1, got %s"%len(pass1.outputPorts[0].connections)
    assert len(pass2.inputPorts[0].connections) == 1,\
           "Expected 1, got %s"%len(pass2.inputPorts[0].connections)
    # is pass1 connected to pass2?
    assert pass1.outputPorts[0].connections[0].port2.node == pass2,\
           "Expected %s, got %s"%(pass2, pass1.outputPorts[0].connections[0].port2.node)
    # can we paste them back?
    net.editor.cutNetwork_cb()
    pause()
    # only main macro node should be present
    assert len(net.nodes) == 1,"Expected 1, got %s"%len(net.nodes)
    node1.expand()
    net.editor.pasteNetwork_cb()
    pause()
    # did they arrive?
    assert len(node1.macroNetwork.nodes) == 5,\
           "Expected 5, got %s"%len(node1.macroNetwork.nodes)
    # deselect them
    sel = node1.macroNetwork.selectedNodes
    node1.macroNetwork.deselectNodes(sel)
    pause()
    # did the connection between Pass1 and Pass2 survive?
    pass1 = node1.macroNetwork.nodes[2]
    pass2 = node1.macroNetwork.nodes[3]
    assert len(pass1.outputPorts[0].connections) == 1,\
           "Expected 1, got %s"%len(pass1.outputPorts[0].connections)
    assert len(pass2.inputPorts[0].connections) == 1,\
           "Expected 1, got %s"%len(pass2.inputPorts[0].connections)
    # is pass1 connected to pass2?
    assert pass1.outputPorts[0].connections[0].port2.node == pass2,\
           "Expected %s, got %s"%(pass2, pass1.outputPorts[0].connections[0].port2.node)
    # reconnect to MacroOutput and MacroInputNode
    ip = node1.macroNetwork.ipNode
    op = node1.macroNetwork.opNode
    dial = node1.macroNetwork.nodes[4]
    node1.macroNetwork.connectNodes(ip, pass1, 'new', 'in1')
    node1.macroNetwork.connectNodes(pass2, op, 'out1', 'new')
    node1.macroNetwork.connectNodes(dial, op, 'value', 'new')
    pause()
    node1.shrink()
    # save the network
    # make sure there is no such file
    os.system("rm -f %s"%filename)
    # save the file
    ed.saveNetwork(filename)
    # and delete the network
    ed.deleteNetwork(net)
    pause()
    # load the file
    ed.loadNetwork(filename)
    pause()
    # do we have nodes inside the macro network?
    net = ed.currentNetwork
    macro = net.nodes[0]
    macroNet = macro.macroNetwork
    assert len(macroNet.nodes) == 5,"Expected 5, got %s"%len(macroNet.nodes)
    assert macroNet.nodes[2].name == 'Pass1',\
           "Expected 'Pass1', got '%s'"%macroNet.nodes[2].name
    assert macroNet.nodes[3].name == 'Pass2',\
           "Expected 'Pass2', got '%s'"%macroNet.nodes[3].name
    assert macroNet.nodes[4].name == 'Dial',\
           "Expected 'Dial', got '%s'"%macroNet.nodes[4].name
    assert len(macroNet.connections) == 4,\
           "Expected 4, got s"%len(macroNet.connections)
    opNode = macroNet.nodes[1]
    assert len(opNode.inputPorts) == 3,\
           "Expected 3, got %s"%len(opNode.inputPorts)
    # singleConnection==True cannot be restored because this gets lost in the
    # cut/paste process. Default value is False:
    assert opNode.inputPorts[2].singleConnection == 'auto',\
           "Expected 'auto', got '%s'"%opNode.inputPorts[2].singleConnection
    # we clean up after us...
    os.system("rm -f %s"%filename)

