# Import Node and Function module
from nipype import Node, Function

# Create a small example function
def add_two(x_input):
    return x_input + 2

# Create Node
addtwo = Node(Function(input_names=["x_input"],
                       output_names=["val_output"],
                       function=add_two),
              name='add_node')

"""
Explanation: Nodes
From the Interface tutorial, you learned that interfaces are the core pieces of Nipype that run the code of your desire. But to streamline your analysis and to execute multiple interfaces in a sensible order, you have to put them in something that we call a Node.
In Nipype, a node is an object that executes a certain function. This function can be anything from a Nipype interface to a user specified function or an external script. Each node consists of a name, an interface category and at least one input field and at least one output field.
Following is a simple node from the utility interface, with the name name_of_node, the input field IN and the output field OUT:

Once you connect multiple nodes to each other, you create a directed graph. In Nipype we call such graphs either workflows or pipelines. Directed connections can only be established from an output field (below node1_out) of a node to an input field (below node2_in) of another node.

This is all there is to Nipype. Connecting specific nodes with certain functions to other specific nodes with other functions. So let us now take a closer look at the different kind of nodes that exist and see when they should be used.
Example of a simple node
First, let us take a look at a simple stand-alone node. In general, a node consists of the following elements:
nodename = Nodetype(interface_function(), name='labelname')


nodename: Variable name of the node in the python environment.
Nodetype: Type of node to be created. This can be a Node, MapNode or JoinNode.
interface_function: Function the node should execute. Can be user specific or coming from an Interface.
labelname: Label name of the node in the workflow environment (defines the name of the working directory)

Let us take a look at an example: For this we need the Node module from Nipype, as well as the Function module. The second only serves a support function for this example. It isn't a prerequisite for a Node.
End of explanation
"""


addtwo.inputs.x_input = 4

"""
Explanation: As specified before, addtwo is the nodename, Node is the Nodetype, Function(...) is the interface_function and add_node is the labelname of the this node. In this particular case, we created an artificial input field, called x_input, an artificial output field called val_output and specified that this node should run the function add_two().
But before we can run this node, we need to declare the value of the input field x_input:
End of explanation
"""


addtwo.run()

"""
Explanation: After all input fields are specified, we can run the node with run():
End of explanation
"""


addtwo.result.outputs

"""
Explanation: And what is the output of this node?
End of explanation
"""


# Import BET from the FSL interface
from nipype.interfaces.fsl import BET

# Import the Node module
from nipype import Node

# Create Node
bet = Node(BET(), name='bet_node')

"""
Explanation: Example of a neuroimaging node
Let's get back to the BET example from the Interface tutorial. The only thing that differs from this example, is that we will put the BET() constructor inside a Node and give it a name.
End of explanation
"""


# Specify node inputs
bet.inputs.in_file = '/data/ds102/sub-02/anat/sub-02_T1w.nii.gz'
bet.inputs.out_file = '/data/ds102/sub-02/anat/node_T1w_bet.nii.gz'

res = bet.run()

"""
Explanation: In the Interface tutorial, we were able to specify the input file with the in_file parameter. This works exactly the same way in this case, where the interface is in a node. The only thing that we have to be careful about when we use a node is to specify where this node should be executed. This is only relevant for when we execute a node by itself, but not when we use them in a Workflow.
End of explanation
"""


%pylab inline
from nilearn.plotting import plot_anat
plot_anat(bet.inputs.in_file, title='BET input',
          display_mode='ortho', dim=-1, draw_cross=False, annotate=False)
plot_anat(res.outputs.out_file, title='BET output',
          display_mode='ortho', dim=-1, draw_cross=False, annotate=False)

"""
Explanation: As we know from the Interface tutorial, the skull stripped output is stored under res.outputs.out_file. So let's take a look at the before and the after:
End of explanation
"""


