import subprocess
import threading
import io
import json
import sys
import os
import pprint
import traceback
import time


def forward(config_dir, config):
    server_address = config['server_address']
    username = config['username']
    target_address = config['target_address']
    blank_endpoint = config['blank_endpoint']
    target_ports = config['target_ports']
    app_name = config['app_name']
    ssh_key = os.path.join(
        config_dir,
        config['ssh_key']
    )
    ssh_command = [
        'ssh',
        '-o', 'ExitOnForwardFailure yes',
        '-o', 'StrictHostKeyChecking no',
        '-o', 'ServerAliveInterval 1',
        '-o', 'ConnectTimeout 1',
    ]

    ports = dict(
        target=[
            *ssh_command,
            '-i', ssh_key,
            '%s@%s' % (username, server_address),
            *sum([
                [
                    '-R', '0.0.0.0:%d:%s:%d' % (
                        pair[0],
                        target_address,
                        pair[1],
                    )
                ]
                for pair in target_ports
            ], []),
            '-N'
        ],
        blank=[
            *ssh_command,
            '-i', ssh_key,
            '%s@%s' % (username, server_address),
            *sum([
                [
                    '-R', '0.0.0.0:%d:%s' % (
                        pair[0],
                        blank_endpoint,
                    )
                ]
                for pair in target_ports
            ], []),
            '-N'
        ]
    )

    has_server = lambda : subprocess.call([
        'ping','-c', '1', target_address,
    ], stdout=subprocess.PIPE, stderr=subprocess.PIPE, stdin=subprocess.PIPE) == 0

    #notify = lambda msg : subprocess.check_output(['notify-send', '-t', '5000', app_name, msg])
    notify = lambda msg: subprocess.call(['echo', app_name, msg])

    while True:
        notify('started')
        if has_server():
            t6 = 'target'
            notify('has_server')
        else:
            t6 = 'blank'
            notify('blank_app')
        t2 = ports[t6]
        with subprocess.Popen(t2) as p:
            try:
                while True:
                    time.sleep(10)
                    t3 = has_server()
                    t4 = None
                    if t6 == 'target' and not t3:
                        t4 = 'no server'
                    elif t6 == 'blank' and t3:
                        t4 = 'server found'
                    if not t4 is None:
                        notify(t4)
                        raise RuntimeError(t4)
                    assert p.poll() is None
            except KeyboardInterrupt:
                break
            except:
                pprint.pprint(traceback.format_exc())
                continue
            finally:
                p.terminate()
                notify('stopped')

config_path = sys.argv[1]
config_dir = os.path.split(
    config_path
)[0]

with io.open(config_path, 'r') as f:
    configs = json.load(f)

t1 = []
for config in configs:
    t2 = threading.Thread(
        target=forward,
        args=[config_dir, config],
    )
    t2.start()
    t1.append(t2)

for o in t1:
    o.join()