#########################################################################
#
# Date: Aug. 2003  Author: Daniel Stoffler
#
#       stoffler@scripps.edu
#
# Copyright: Daniel Stoffler and TSRI
#
#########################################################################

# NOTE: these are nodes to be used in Tests. This allows to be independent
#       from ViPEr

from NetworkEditor.simpleNE import NetworkNode

##########################
## Nodes
##########################


class DialNode(NetworkNode):
    def __init__(self, name='Dial', **kw):
        kw['name'] = name
        apply( NetworkNode.__init__, (self,), kw)
        self.inNodeWidgetsVisibleByDefault = True

        self.widgetDescr['dial'] = {
            'class':'NEDial', 'master':'node', 'size':50,
            'oneTurn':1, 'type':'float',
            'initialValue':0.0,
            'labelCfg':{'text':''}}

        self.inputPortsDescr.append({'datatype': 'float', 'name': 'dial'})
        self.outputPortsDescr.append({'datatype': 'float', 'name': 'value'})
        code = """def doit(self, dial):
    if dial is not None:
        # set the output port data type if different from value type
        port = self.outputPorts[0]
        dataTypeStr = dial.__class__.__name__
        portTypeStr = port.datatypeObject['name']
        if dataTypeStr != portTypeStr:
            port.setDataType(dataTypeStr)
        self.outputData(value=dial)\n"""
        self.setFunction(code)


class PassNode(NetworkNode):
    def __init__(self, name='Pass', **kw):
        kw['name'] = name
        apply( NetworkNode.__init__, (self,), kw)
	code = """def doit(self, in1):
    if in1 is not None:
        self.outputData(out1=in1)\n"""
        self.setFunction(code)
        # connecting to a pass node sets the output port datatype
        afterConnectCode = """def afterConnect(self, conn):
    self.node.outputPorts[0].configure(datatype=conn.port2.datatypeObject['name'])
"""
        # disconnecting to a pass node resets the output port datatype to None
        afterDisconnectCode = """def afterDisconnect(self, port1, port2):
   self.node.outputPorts[0].configure(datatype='None')
"""
	self.inputPortsDescr.append({'name':'in1', 'datatype':'None',
                                     'afterConnect':afterConnectCode,
                                     'afterDisconnect':afterDisconnectCode})
	self.outputPortsDescr.append({'name':'out1', 'datatype':'None'})



class CounterNode(NetworkNode):
    def __init__(self, name='Counter', **kw):
        kw['name'] = name
        self.counter = 0 # increments every time the node runs
        apply( NetworkNode.__init__, (self,), kw)
	code = """def doit(self, in1, reset):
    if in1 or reset:
        self.counter = self.counter + 1
        self.outputData(out1=in1, counts=self.counter)\n"""
        self.setFunction(code)
        self.widgetDescr['reset'] = {
            'class': 'NEButton', 'master':'node',
            'command':self.reset,
            'labelCfg':{'text':'Reset'}
            }
	self.inputPortsDescr.append({'name':'in1', 'datatype':'None'})
	self.inputPortsDescr.append({'name':'reset', 'datatype':'int',
                                     'required':False})
	self.outputPortsDescr.append({'name':'out1', 'datatype':'None'})
	self.outputPortsDescr.append({'name':'counts', 'datatype':'int'})
    def reset(self):
        # reset counter
        self.counter = -1 # node will run once, thus counter will be 0
        # run to output 0 as new counter
        self.inputPorts[1].widget.scheduleNode()


class EvalNode(NetworkNode):
    def __init__(self, name='eval', **kw):
        kw['name'] = name
        apply( NetworkNode.__init__, (self,), kw )

	code = """def doit(self, command):
    if len(command) == 0:
        return
    else:
        result = eval(command)
        self.outputData(result=result)\n"""
	self.setFunction(code)
        self.widgetDescr['command'] = {
            'class': 'NEEntry', 'master':'node',
            'labelCfg':{'text':'statement:'}}
	self.inputPortsDescr.append({'datatype':'string','name': 'command'})
	self.outputPortsDescr.append({'name':'result', 'datatype':'None'})


class PrintNode(NetworkNode):
    def __init__(self, name='print', **kw):
        kw['name'] = name
        apply( NetworkNode.__init__, (self,), kw )
	code = """def doit(self, in1):
    print in1\n"""
	self.setFunction(code)
	self.inputPortsDescr.append({'datatype':'None', 'name':'in1'})


class IterateNode(NetworkNode):
    def __init__(self, name='iterate', **kw):
        kw['name'] = name
        kw['progbar'] = 1 # add a progress bar to the node
        apply( NetworkNode.__init__, (self,), kw )
        self.isSchedulingNode = True # this node is not scheduled in the net.py/
                                  # method widthFirstChildren()
        self.readOnly = 1
	code = """def doit(self, listToLoopOver):
    iter = 0
    length = len(listToLoopOver)

    net = self.network
    # get the list of nodes to run
    allNodes = net.getAllNodes(self.children)

    if length>0:
        t = type(listToLoopOver[0])
        if t in [int, float, str]:
            if t==str:
               t = 'string'
            else:
               t = t.__name__
        self.outputPorts[0].setDataType(t)
    
    for item in listToLoopOver:
        stop = self.network.checkExecStatus()
        #print 'Iterate', item, stop
        if stop:
            break
        self.outputData(oneItem=item, iter=iter, maxIter=length)
        self.setProgressBar(float(iter)/length)
        self.forceExecution = 1
        net.runNodes(allNodes)
        iter = iter + 1
    self.setProgressBar(1.0)\n"""
        if code: self.setFunction(code)
        self.inputPortsDescr.append({'datatype':'vector',
                                     'name':'listToLoopOver'})
        op = self.outputPortsDescr
	op.append({'name':'oneItem', 'datatype':'None'})
        op.append({'name':'iter', 'datatype':'int',
                   'balloon':'0-based iteration index'})
	op.append({'name':'maxIter', 'datatype':'int',
                   'balloon':'number of iteration to be done'})



class Operator3Node(NetworkNode):
    """apply ternary operator to incomming values.

Input Ports
    data1: any python object or list of python objects
    data2: any python object or list of python objects
    data3: any python object or list of python objects
    operation: unary operator available in operator module
    applyToElements: default=False. Should only be true if data is a sequence.
               When true the operator is applied to each element rather than
               the sequence

Output Ports
    result: result of applying operator to data
"""

    def __init__(self, name='TernaryOp', **kw):
        kw['name'] = name
        apply( NetworkNode.__init__, (self,), kw)

        code = """def doit(self, data, operation, from_, to_, applyToElements):
    if not operation:
        return
    if applyToElements is None:
        applyToElements = False
    import operator
    op = getattr(operator, operation)
    if not applyToElements:
        result = apply( op, (data, from_ , to_) )
    else:
        result = []
        for d in data:
           result.append( op( d, from_, to_ ))
    if result:
        self.outputData(result=result)
"""
        if code: self.setFunction(code)

        self.widgetDescr['operation'] = {
            'class':'NEComboBox', 'master':'node',
            'entryfield_entry_width':8,
            'labelpos':None,
            'choices':['delslice', 'getslice', 'setitem'],
            'fixedChoices':True,
            'initialValue':'setitem',
            'labelGridCfg':{'sticky':'w'},
            'widgetGridCfg':{'sticky':'w', 'columnspan':2, 'pady':2},
            'labelCfg':{'text':'operator'},
            'selectioncommand':self.rename,
            }

        self.widgetDescr['from_'] = {
            'class':'NEThumbWheel', 'master':'node',
            'width':80, 'height':20, 'type':'int', 'wheelPad':1,
            'initialValue':0.0,
            'labelGridCfg':{'sticky':'w'},
            'widgetGridCfg':{'sticky':'w', 'columnspan':2},
            'labelCfg':{'text':'from:'},
            }

        self.widgetDescr['to_'] = {
            'class':'NEThumbWheel', 'master':'node',
            'width':80, 'height':20, 'type':'int', 'wheelPad':1,
            'initialValue':0.0,
            'labelGridCfg':{'sticky':'w'},
            'widgetGridCfg':{'sticky':'w', 'columnspan':2},
            'labelCfg':{'text':'to:'},
            }

	self.widgetDescr['applyToElements'] = {
            'class':'NECheckButton', 'master':'node',
            'labelGridCfg':{'sticky':'w', 'columnspan':2},
            'widgetGridCfg':{'sticky':'w'},
            'labelCfg':{'text':'apply to elements'},
            }
                                               
        ip = self.inputPortsDescr
        ip.append({'name':'data', 'datatype':'None',
                   'balloon':'Data to be operated on'})
        ip.append({'name':'operation', 'datatype':'None',
                   'balloon':'operation to be applied'})
        ip.append({'name':'from_', 'datatype':'None',
                   'balloon':'Data to be operated on'})
        ip.append({'name':'to_', 'datatype':'None',
                   'balloon':'Data to be operated on'})
        ip.append({'name':'applyToElements', 'datatype':'int'})
        
        self.outputPortsDescr.append(
            {'name':'result', 'datatype':'None'})

