"""
Terminal creation and cleanup.
Utility functions to run a terminal (connected via socat(1)) on each host.

Requires socat(1) and xterm(1).
Optionally uses gnome-terminal.
"""

from os import environ

from src.mininet.log import error
from src.mininet.util import quietRun, errRun

def tunnelX11( node, display=None):
    """Create an X11 tunnel from node:6000 to the root host
       display: display on root host (optional)
       returns: node $DISPLAY, Popen object for tunnel"""
    if display is None and 'DISPLAY' in environ:
        display = environ[ 'DISPLAY' ]
    if display is None:
        error( "Error: Cannot connect to display\n" )
        return None, None
    host, screen = display.split( ':' )
    # Unix sockets should work
    if not host or host == 'unix':
        # GDM3 doesn't put credentials in .Xauthority,
        # so allow root to just connect
        quietRun( 'xhost +si:localuser:root' )
        return display, None
    else:
        # Create a tunnel for the TCP connection
        port = 6000 + int( float( screen ) )
        connection = r'TCP\:%s\:%s' % ( host, port )
        cmd = [ "socat", "TCP-LISTEN:%d,fork,reuseaddr" % port,
                "EXEC:'mnexec -a 1 socat STDIO %s'" % connection ]
    return 'localhost:' + screen, node.popen( cmd )

def makeTerm( node, title='Node', term='xterm', display=None, cmd='bash'):
    """Create an X11 tunnel to the node and start up a terminal.
       node: Node object
       title: base title
       term: 'xterm' or 'gterm'
       returns: two Popen objects, tunnel and terminal"""
    title = '"%s: %s"' % ( title, node.name )
    if not node.inNamespace:
        title += ' (root)'
    cmds = {
        'xterm': [ 'xterm', '-title', title, '-display' ],
        'gterm': [ 'gnome-terminal', '--title', title, '--display' ]
    }
    if term not in cmds:
        error( 'invalid terminal type: %s' % term )
        return
    # Docker Hosts don't have DISPLAY. So instead of
    # X11 tunnel, we use terminals from outside Docker
    from src.mininet.node import Docker
    if isinstance( node, Docker ):
        if not node._is_container_running():
            return []
        if display is None:
            cmds[ term ] = cmds[ term ][:-1]
        else:
            cmds[ term ].append(display)
        cmds[term].append('-e')
        from subprocess import Popen, PIPE
        term = Popen( cmds[ term ] + 
            [ 'env TERM=ansi docker exec -it %s.%s %s' % ( 
            node.dnameprefix, node.name, cmd ) ], stdout=PIPE, stdin=PIPE, stderr=PIPE )
        if term:
            return [ term ]
        # Failover alternative for Docker
        # host = None --> Needs docker interface
        # port = 6000
        # from src.mininet.log import warn
        # warn('Warning: Docker Exec Failed. Trying TCP Connection for Terminal\n')
        # pipe = node.popen( [ 'socat', 'TCP-LISTEN:%d,fork,reuseaddr' % port,
        #     'EXEC:\'%s\',pty,stderr,setsid,sigint,sane' % cmd ] );
        # term = Popen( cmds[ term ] + 
        #     [ 'socat FILE:`tty`,raw,echo=0 TCP:%s:%s' % (
        #      host, port ) ], stdout=PIPE, stdin=PIPE, stderr=PIPE )
        # return [ pipe, term ] if pipe else [ term ]
        return []
    display, tunnel = tunnelX11( node, display )
    if display is None:
        return []
    term = node.popen( cmds[ term ] +
                       [ display, '-e', 'env TERM=ansi %s' % cmd ] )
    return [ tunnel, term ] if tunnel else [ term ]

def runX11( node, cmd ):
    "Run an X11 client on a node"
    _display, tunnel = tunnelX11( node )
    if _display is None:
        return []
    popen = node.popen( cmd )
    return [ tunnel, popen ]

def cleanUpScreens():
    "Remove moldy socat X11 tunnels."
    errRun( "pkill -9 -f mnexec.*socat" )

def makeTerms( nodes, title='Node', term='xterm' ):
    """Create terminals.
       nodes: list of Node objects
       title: base title for each
       returns: list of created tunnel/terminal processes"""
    terms = []
    for node in nodes:
        terms += makeTerm( node, title, term )
    return terms
