// WARNING: seems that this maybe can't work reliably?
// in case of 'git grep a' it's possible that child process spawns it's own
// child process and then it won't print anything because it won't have
// the appropriate handles. This happens in this case when git grep calls
// 'less' program which errors out and doesn't print anything
// @todo: maybe I should ask someone smarter about this!
void UpdateProcesses() {
    ProfileFunction();
    IterRemove(ActiveProcesses) {
        IterRemovePrepare(ActiveProcesses);
        Scratch scratch;
        View   *view = GetView({it.view_id});

        String poll = PollStdout(scratch, &it, false);
        if (poll.len) {
            Append(view, poll, it.scroll_to_end);
        }
        if (!IsValid(&it)) {
            ReportDebugf("process %lld exit code = %d", it.id, it.exit_code);
            remove_item = true;
        }
    }
}

void Exec(ViewID view, bool scroll_to_end, String cmd, String working_dir) {
    Process process       = SpawnProcess(cmd, working_dir, {}, ProcessEnviroment);
    ReportDebugf("process %lld start. is_valid = %d cmd = %S working_dir = %S", process.id, process.is_valid, cmd, working_dir);
    process.view_id       = view.id;
    process.scroll_to_end = scroll_to_end;
    if (process.is_valid) Add(&ActiveProcesses, process);
}

void Exec(ViewID view, bool scroll_to_end, String16 cmd16, String working_dir) {
    Scratch scratch;
    String  cmd = ToString(scratch, cmd16);
    Exec(view, scroll_to_end, cmd, working_dir);
}

Buffer *ExecAndWait(Allocator allocator, String cmd, String working_dir, String stdin_string = {}) {
    ReportDebugf("ExecAndWait cmd = %S working_dir = %S stdin_string = %S", cmd, working_dir, stdin_string);

    Buffer *temp_buffer = CreateTempBuffer(allocator, 4096 * 4);
    for (Process process = SpawnProcess(cmd, working_dir, stdin_string, ProcessEnviroment); IsValid(&process);) {
        Scratch scratch(allocator);
        String  poll = PollStdout(scratch, &process, true);
        if (poll.len) RawAppend(temp_buffer, poll);
    }

    return temp_buffer;
}

void KillProcess(View *view) {
    IterRemove(ActiveProcesses) {
        IterRemovePrepare(ActiveProcesses);

        ViewID view_id = {it.view_id};
        if (view_id == view->id) {
            KillProcess(&it);
            remove_item   = true;
            String string = "process was killed by user\n";
            Append(view, string, it.scroll_to_end);
            // dont break because that will fuck with removal ...
        }
    }
}

bool ProcessIsActive(ViewID view) {
    For (ActiveProcesses) {
        if (it.view_id == view.id) {
            return true;
        }
    }
    return false;
}
