# This file is a part of Julia. License is MIT: https://julialang.org/license

import Base: launch, manage, connect, exit

mutable struct UnixDomainCM <: ClusterManager
    np::Integer
end

function launch(manager::UnixDomainCM, params::Dict, launched::Array, c::Condition)
#    println("launch $(manager.np)")
    cookie = Base.cluster_cookie()
    for i in 1:manager.np
        sockname = tempname()
        try
            cmd = `$(params[:exename]) --startup-file=no $(@__FILE__) udwrkr $sockname $cookie`
            io, pobj = open(cmd, "r")

            wconfig = WorkerConfig()
            wconfig.userdata = Dict(:sockname=>sockname, :io=>io, :process=>pobj)
            push!(launched, wconfig)
            notify(c)
        catch e
            println(e)
        end
    end
end

function connect(manager::UnixDomainCM, pid::Int, config::WorkerConfig)
    if myid() == 1
#        println("connect_m2w")
        config.connect_at = get(config.userdata)[:sockname] # This will be useful in the worker-to-worker connection setup.

        print_worker_stdout(get(config.userdata)[:io], pid)
    else
#        println("connect_w2w")
        sockname = get(config.connect_at)
        config.userdata = Dict{Symbol, Any}(:sockname=>sockname)
    end

    t = time()
    while true
        try
            address = get(config.userdata)[:sockname]
            if isa(address, Tuple)
                sock = connect(address...)
            else
                sock = connect(address)
            end
            return (sock, sock)
        catch e
            if (time() - t) > 30.0
                rethrow(e)
            else
                sleep(0.1)
            end
        end
    end

end

# WORKER
function start_worker(sockname, cookie)
    Base.init_worker(cookie, UnixDomainCM(0))

    srvr = listen(sockname)
    while true
        sock = accept(srvr)
        Base.process_messages(sock, sock)
    end
end

function manage(manager::UnixDomainCM, id::Int, config::WorkerConfig, op)
    # Does not seem to be required, filesystem entry cleanup is happening automatically on process exit
#     if op == :deregister
#         try
#             rm(get(config.userdata)[:sockname])
#         end
#     end
    nothing
end

function print_worker_stdout(io, pid)
    @schedule while !eof(io)
        line = readline(io)
        println("\tFrom worker $(pid):\t$line")
    end
end

if (length(ARGS) > 0) && (ARGS[1] == "udwrkr")
    # script has been launched as a worker
    start_worker(ARGS[2], ARGS[3])
end
