### codetube
    Copyright (C) 2011 payload payload@lavabit.com
    Copyright (C) 2011 dodo dodo.the.last@gmail.com

    This program is free software: you can redistribute it and/or modify
    it under the terms of the GNU Affero General Public License as published by
    the Free Software Foundation, either version 3 of the License, or
    (at your option) any later version.

    This program is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU Affero General Public License for more details.

    You should have received a copy of the GNU Affero General Public License
    along with this program.  If not, see <http://www.gnu.org/licenses/>
###

fs = require('fs')
net = require('net')
path = require('path')
async = require('async')
{spawn} = require('child_process')
BufferStream = require('bufferstream')
GitProxy = require('./git-proxy')
config = require('./config')
db = require('./db')
µ = (next) -> next()


class GitDaemon # Singleton
    singleton = null

    constructor: () ->
        return singleton if singleton
        singleton = this
        @child = null # spawned git daemon
        @spawn_git_daemon()

    spawn_git_daemon: () =>
        args = ["daemon", "--reuseaddr", "--export-all"
            "--base-path=" + path.join(config.cwd, config.repositories)
            "--pid-file=" + config.gitdaemon.pidfile
            "--listen=" + config.gitdaemon.address
            "--port=" + config.gitdaemon.serve
        ]
        args.push("--verbose") if config.debug.enable
        async.waterfall [µ
        ,(next) =>
            fs.readFile config.gitdaemon.pidfile, (err, pid) =>
                return next() if err
                pid = pid.toString().replace(/\n/g,'')
                console.log("found #{pid} in #{config.gitdaemon.pidfile}")
                return next() unless pid.length and pid
                pid = parseInt(pid)
                return next() unless pid
                i = 0
                while on
                    try
                        process.kill(pid)
                        i++
                    catch e
                        console.log("killed #{pid} "+(!i and "ignored" or ""))
                        break
                next()
        ,(next) =>
            return if @child and @child.pid
            @child = spawn("git", args, customFds: [-1, process.stdout, -1])
            return next("Couldn't start daemon!") unless @child.pid
            console.log("spawned daemon (#{@child.pid})")
            next()
        ,(next) =>
            @child.on 'exit', @spawn_git_daemon
            @child.stderr.pipe(process.stderr, { end:off })
            @child.stderr.on 'data', (data) =>
                if /^execvp\(\)/.test(data)
                    console.log("failed to start daemon.")
                    @running = off
            next()
        ], (err) =>
            if err
                console.error(err)
                process.exit(13)


class GitProxyDaemon
    constructor: (options, connectionListener) ->
        @other_conn_listener = connectionListener
        @server = net.createServer(options, @my_conn_listener)
        listen = @server.listen
        @server.listen = (port, host, args...) =>
            return unless config.gitdaemon.enable
            listen.call(@server, port, host, args...)
            console.log("git daemon proxy listening on %s:%d …".magenta, host, port)
        db.start () =>
            console.log("db started.")
            @create()

    create: () =>
        @git_daemon =
            port: config.gitdaemon.serve
            address: config.gitdaemon.address
        @proxy = new GitProxy()
        # git daemon must be a cluster wide singleton
        # so it is instantiated only if this cluster isMaster
        @daemon = new GitDaemon() if config.gitdaemon.enable

    my_conn_listener: (client) =>
        daemon = net.createConnection(
                @git_daemon.port,
                @git_daemon.address)

        buffer =
            client: new BufferStream(encoding:'binary', size:'flexible')
            daemon: new BufferStream(encoding:'binary', size:'flexible')
        # client → buffer.client|proxy.request → daemon
        client.pipe(buffer.client)
        @proxy.request(buffer.client)
        buffer.client.pipe(daemon)
        # daemon → buffer.daemon|proxy.response → client
        daemon.pipe(client)# FIXME buffer.daemon) ###
        #@proxy.response(buffer.daemon, "stranger") ###
        #buffer.daemon.pipe(client)# ###

        @other_conn_listener?(client)

# exports

module.exports = new GitProxyDaemon
