#!/usr/bin/env python
import os, subprocess, sys, signal, termios, atexit, socket, getopt

def testament_child_death():
    rv = os.fork()
    if rv == 0:
        os.kill(os.getpid(), signal.SIGSTOP)
        os._exit(0)
    os.waitpid(rv, os.WUNTRACED)

PREFIX = subprocess.Popen(". $(pwd)/.configuration && echo $prefix", shell = True, stdout=subprocess.PIPE).communicate()[0].rstrip()

def setup_path():
    def ebin_search(path):
        return subprocess.Popen(["find", path, "-name", "ebin", "-type", "d"],
                                stdout=subprocess.PIPE).communicate()[0].split()

    path = ebin_search(".")
    couchpath = ebin_search("{0}/lib/couchdb/erlang/lib".format(PREFIX))

    if len(couchpath) == 0:
       print("Couch libs wasn't found. Will disable couch support\n")
       os.environ['DONT_START_COUCH'] = '1'

    return couchpath + path


def start_cluster(num_nodes, start_index, host, extra_args):
    ebin_path = setup_path()

    base_direct_port = 12000
    base_api_port = 9000
    base_couch_port = 9500

    def start_node(i):
        logdir = "logs/n_{0}".format(i)
        try:
            os.makedirs(logdir)
        except:
            pass
        try:
            os.stat("data/n_{0}/mnesia".format(i))
        except:
            os.makedirs("data/n_{0}/mnesia".format(i))
        subprocess.Popen(["./scripts/mkcouch.sh", "n_{0}".format(i), str(base_couch_port + i)]).communicate()
        args = ["erl", "+A", "16", "-pa"] + ebin_path + [
            "-setcookie", "nocookie",
            "-name", "n_{0}@{1}".format(i, host),
            "-kernel", "inet_dist_listen_min", "21100",
            "inet_dist_listen_max", "21199",
            "-couch_ini", "{0}/etc/couchdb/default.ini".format(PREFIX), "couch/n_{0}_conf.ini".format(i),
            "-ns_server", "config_path", '"etc/static_config.in"',
            "error_logger_mf_dir", '"{0}"'.format(logdir),
            "error_logger_mf_maxbytes", "10485760",
            "error_logger_mf_maxfiles", "10",
            "dont_suppress_stderr_logger", "true",
            "path_config_etcdir", '"priv"',
            "path_config_bindir", '"{0}"'.format(PREFIX+"/bin"),
            "path_config_libdir", '"{0}"'.format(PREFIX+"/lib"),
            "path_config_datadir", '"data/n_{0}"'.format(i),
            "path_config_tmpdir", '"tmp/"',
            "rest_port", str(base_api_port + i),
            "memcached_port", str(base_direct_port + i * 2),
            "moxi_port", str(base_direct_port + i * 2 + 1),
            "short_name", '"n_{0}"'.format(i)
            ] + extra_args
        if i == 0:
            return subprocess.Popen(args)
        else:
            return subprocess.Popen(args + ["-noshell"], stdin=subprocess.PIPE)

    return [start_node(i + start_index) for i in xrange(num_nodes)]

def usage():
    sys.exit("Usage: {0} [--nodes=N] [--dont-rename] [--dont-start] [--static-cookie] [ns_server args]".format(sys.argv[0]))

def maybe_spawn_epmd():
    try:
        socket.create_connection(("127.0.0.1", 4369)).close()
    except socket.error:
        print("Spawning epmd...\n")
        subprocess.Popen("erl -noshell -setcookie nocookie -sname init -run init stop 2>&1 > /dev/null",
                         shell = True).communicate()

def find_primary_addr():
    s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
    try:
        s.connect(("8.8.8.8", 56))
        addr, port = s.getsockname()
        return addr
    except socket.error:
        return None
    finally:
        s.close()

def main():
    try:
        optlist, args = getopt.gnu_getopt(sys.argv[1:], "hn:", ["help", "start-index=", "nodes=", "static-cookie", "dont-start", "host="])
    except getopt.GetoptError, err:
        # print help information and exit:
        print str(err) # will print something like "option -a not recognized"
        usage()
        sys.exit(2)

    primary_addr = find_primary_addr()

    dont_start = False
    static_cookie = False
    start_index = 0
    num_nodes = 1
    host = "127.0.0.1" if primary_addr == None else primary_addr

    for o, a in optlist:
        if o in ("--nodes", "-n"):
            num_nodes = int(a)
        elif o == '--dont-start':
            dont_start = True
        elif o == '--host':
            host = a
        elif o == '--start-index':
            start_index = int(a)
        elif o in ("--help", "-h"):
            usage()
            exit(0)
        elif o in("--static-cookie"):
            static_cookie = True
        else:
            assert False, "unhandled options"

    nodes = []
    terminal_attrs = None

    def kill_nodes(*args):
        for n in nodes:
            try:
                n.kill()
                n.wait()
            except OSError:
                pass
        if terminal_attrs != None:
            termios.tcsetattr(sys.stdin, termios.TCSANOW, terminal_attrs)

    atexit.register(kill_nodes)

    testament_child_death()

    try:
        terminal_attrs = termios.tcgetattr(sys.stdin)
    except:
        pass

    maybe_spawn_epmd()

    extra_args = args[1:]

    if static_cookie:
        extra_args += ["-ns_server", "dont_reset_cookie", "true"]

    if not dont_start:
        extra_args += ["-run", "ns_bootstrap"]

    nodes = start_cluster(num_nodes, start_index, host, extra_args)

    for node in nodes:
        node.wait()


if __name__ == '__main__':
    os.environ['DONT_START_COUCH'] = '1'
    main()
