class CommandRunner
    attr :command
    attr :child_pid

    def initialize(*command)
        @command        = command
        @child_pid      = nil
        @waiting_thread = nil

        @read_pipe        = nil
        @read_error_pipe  = nil
        @write_pipe       = nil
    end

    def closeWrite
        @write_pipe.close
    end

    def kill
        Process.kill("KILL", @child_pid)
    end

    def read
        return @read_pipe.read
    end

    def readError
        return @read_error_pipe.read
    end

    def run
        parent_to_child_read, parent_to_child_write             = IO.pipe
        child_to_parent_read, child_to_parent_write             = IO.pipe
        child_to_parent_error_read, child_to_parent_error_write = IO.pipe

        @child_pid = fork do
            parent_to_child_write.close
            child_to_parent_read.close
            child_to_parent_error_read.close

            # Wait until parent is ready before we start doing anything
            if parent_to_child_read.readchar.chr != "R"
                raise "Unexpected input from parent"
            end

            $stdin.reopen(parent_to_child_read)
            $stdout.reopen(child_to_parent_write)
            $stderr.reopen(child_to_parent_error_write)

            exec(*@command)
        end

        @waiting_thread = Thread.new(@child_pid) do |pid|
            return_code = -1

            begin
                return_code = Process.waitpid2(pid)
            rescue SystemError
                raise "Process finished running already!"
            end
            return_code = return_code[1].exitstatus

            return_code
        end

        child_to_parent_write.close
        child_to_parent_error_write.close
        parent_to_child_read.close

        @read_pipe        = child_to_parent_read
        @read_error_pipe  = child_to_parent_error_read
        @write_pipe       = parent_to_child_write

        @write_pipe.write("R")
        @write_pipe.flush
    end

    #--------------------------------------------------------------------------
    # Waits for command to exit
    # Returns: The return code of the program when it exited
    #--------------------------------------------------------------------------
    def wait
        if not @child_pid or not @waiting_thread
            raise "Waiting for a process that has not started"
        end

        return_value = @waiting_thread.value

        @waiting_thread = nil
        @child_pid = nil

        return return_value
    end

    def write(string)
        @write_pipe.write(string)
    end
end