/***********************************************************************
 *                                                                      *
 *               This software is part of the ast package               *
 *          Copyright (c) 1999-2011 AT&T Intellectual Property          *
 *                      and is licensed under the                       *
 *                 Eclipse Public License, Version 1.0                  *
 *                    by AT&T Intellectual Property                     *
 *                                                                      *
 *                A copy of the License is available at                 *
 *          http://www.eclipse.org/org/documents/epl-v10.html           *
 *         (with md5 checksum b35adb5213ca9657e911e9befb180842)         *
 *                                                                      *
 *              Information and Software Systems Research               *
 *                            AT&T Research                             *
 *                           Florham Park NJ                            *
 *                                                                      *
 *               Glenn Fowler <glenn.s.fowler@gmail.com>                *
 *                                                                      *
 ***********************************************************************/
#include "config_ast.h"  // IWYU pragma: keep

#include <string.h>
#include <unistd.h>

#include "sfio.h"
#include "terror.h"

#define ITER 10
#define CNT 2000

typedef struct _mydisc_s {
    Sfdisc_t disc;
    int size; /* remaining size of readable data        */
    int send; /* channel to send size of a read */
    int recv; /* channel to read amount of data */
} Mydisc_t;

ssize_t discread(Sfio_t *f, void *buf, size_t n, Sfdisc_t *disc) {
    Mydisc_t *dc = (Mydisc_t *)disc;
    int r;

    if (dc->size <= 0) {
        if ((r = read(dc->recv, &dc->size, sizeof(int))) != sizeof(int)) return 0;

        if (dc->size == 0) return 0;
    }

    if (n > dc->size) n = dc->size;
    if ((r = read(sffileno(f), buf, n)) <= 0) {
        terror("Bad reading of data from file r=%d n=%d", r, n);
    }
    /**/ TSTDEBUG(("Reader just read %d bytes", r));

    dc->size -= r;

    if ((n = write(dc->send, &r, sizeof(int))) != sizeof(int)) terror("Sending read size");
    /**/ TSTDEBUG(("Reader just told sender of a %d byte read", r));

    return r;
}

void writeprocess(int send, int recv, Sfio_t *f) {
    char buf[11 * CNT], *bp;
    int i, s, size, rv;

    for (s = 0, bp = buf; s < CNT; ++s) {
        for (i = 0; i < 10; ++i) *bp++ = '0' + i % 10;
        *bp++ = '\0';
    }

    for (i = 0; i < ITER; i++) {
        /* write out a buffer of data */
        if ((size = write(sffileno(f), buf, sizeof(buf))) != sizeof(buf)) {
            terror("Bad write to file");
        }
        if (write(send, &size, sizeof(int)) != sizeof(int)) terror("Sending output size %d", size);
        /**/ TSTDEBUG(("Writer just wrote %d bytes", size));

        /* now wait until readprocess exhausts the data */
        while (size > 0) {
            if ((rv = read(recv, &s, sizeof(int))) != sizeof(int)) {
                terror("Reading amount of consumed data, rv=%d", rv);
            }
            /**/ TSTDEBUG(("Writer was told of a %d byte read", s));

            if (s <= 0 || s > size) terror("Reader just told a bad read size %d", s);
            size -= s;
        }
    }

    size = 0;
    if (write(send, &size, sizeof(int)) != sizeof(int)) terror("Sending eof signal");
}

tmain() {
    UNUSED(argc);
    UNUSED(argv);
    Sfio_t *fw, *fr;
    Mydisc_t disc;
    int parent[2], child[2];
    char buf[11];
    int i, s, rv;

    if (pipe(parent) < 0 || pipe(child) < 0) terror("Making pipes for communications");

    if (!(fw = sfopen(NULL, tstfile("sf", 0), "w"))) terror("Creating temp file");

    if (!(fr = sfopen(NULL, tstfile("sf", 0), "r"))) terror("Opening temp file to read");

    disc.disc.readf = discread;
    disc.disc.writef = 0;
    disc.disc.seekf = 0;
    disc.disc.exceptf = 0;
    disc.send = parent[1];
    disc.recv = child[0];
    disc.size = 0;
    sfdisc(fr, &disc.disc);

    switch (fork()) {
        case -1:
            terror("fork() failed");
            break;
        case 0:
            close(child[0]);
            close(parent[1]);
            writeprocess(child[1], parent[0], fw);
            break;
        default:
            close(child[1]);
            close(parent[0]);
            for (i = 0, s = 0; i <= ITER * CNT; ++i) {
                if ((rv = sfread(fr, buf, 11)) != 11) break;
                s += rv;
                if (strcmp(buf, "0123456789") != 0) terror("Bad data");
                for (rv = 0; rv < 11; ++rv) buf[rv] = 1;
            }
            if (s != ITER * CNT * 11) terror("Only read %d, expected %d", s, ITER * CNT * 11);
            break;
    }

    texit(0);
}
