/*
*  Copyright (C) 2007 Duncan Brown, Jolien Creighton, Robert Adam Mercer
*
*  This program is free software; you can redistribute it and/or modify
*  it under the terms of the GNU General Public License as published by
*  the Free Software Foundation; either version 2 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 General Public License for more details.
*
*  You should have received a copy of the GNU General Public License
*  along with with program; see the file COPYING. If not, write to the
*  Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
*  MA  02110-1301  USA
*/

/**
 * @defgroup lalfr_stat lalfr-stat
 * @ingroup lalframe_programs
 *
 * @brief Briefly describe frame files
 *
 * ### Synopsis
 *
 *     lalfr-stat [file ...]
 *
 * ### Description
 *
 * The `lalfr-stat` utility displays information about the file pointed to by
 * `file`.  If `file` is a single dash (`-`) or absent, `lalfr-stat` reads from
 * the standard input.
 *
 * For each `file`, `lalfr-stat` writes to standard output a line containing
 * five columns.  The first column contains the detector site information for
 * all detector structures contained in `file`.  The second column contains a
 * file description that is parsed from the file name.  The third column
 * contains the GPS second that is equal to or just before the beginning of the
 * data contained in `file`.  The fourth column contains an integer duration in
 * seconds such that the sum of columns three and four gives a GPS time after
 * the data contained in the file.  The fifth column contains the URL of the
 * file.
 *
 * If any of the above data is missing or unknown, the value in the column is
 * given by `-`.
 *
 * ### Example
 *
 * The command:
 *
 *     lalfr-stat *.gwf > cache
 *
 * will produce a standard LAL cache file `cache` describing all the frames in
 * the current directory.
 */

#define _GNU_SOURCE   /* for realpath() */
#include <config.h>
#include <stdlib.h>

#include <ctype.h>
#include <math.h>
#include <stdio.h>
#include <string.h>
#include <lal/LALFrameU.h>

#ifndef PATH_MAX
#define PATH_MAX FILENAME_MAX
#endif

#define H0 2.3e-18      /* Hubble constant, s */
#define SITE_MAX 26     /* in our dreams... */

#define FAILURE(...) do { fprintf(stderr, __VA_ARGS__); exit(1); } while (0)

int charcmp(const void *c1, const void *c2);
char *fnametodsc(const char *fname);

int main(int argc, char *argv[])
{
    char stdio[] = "-";
    char *defaultfilev[1] = { stdio };
    int filec = (argc == 1 ? 1 : argc - 1);
    char **filev = (argc == 1 ? defaultfilev : &argv[1]);
    int f;

    for (f = 0; f < filec; ++f) {
        char *fname = filev[f];
        char *dsc;
        LALFrameUFrFile *frfile;
        LALFrameUFrTOC *toc;
        char sites[SITE_MAX + 1] = "";
        char path[PATH_MAX + 1] = "";
        size_t ndet;
        size_t nadc;
        size_t nsim;
        size_t nproc;
        size_t nframe;
        size_t pos;
        size_t det;
        double t0min = +1.0 / H0;       /* ridiculously far in future */
        double t1max = -1.0 / H0;       /* ridiculously far in past */
        int dt;

        frfile = XLALFrameUFrFileOpen(fname, "r");
        if (!frfile)
            FAILURE("file %s not found\n", fname);

        toc = XLALFrameUFrTOCRead(frfile);
        if (!toc)
            FAILURE("no TOC found\n");

        nframe = XLALFrameUFrTOCQueryNFrame(toc);
        if ((int)(nframe) <= 0)
            FAILURE("no frames found\n");

        ndet = XLALFrameUFrTOCQueryDetectorN(toc);
        nadc = XLALFrameUFrTOCQueryAdcN(toc);
        nsim = XLALFrameUFrTOCQuerySimN(toc);
        nproc = XLALFrameUFrTOCQueryProcN(toc);

        for (det = 0; det < ndet; ++det) {
            LALFrameUFrDetector *detector;
            const char *prefix;
            const char *name;
            name = XLALFrameUFrTOCQueryDetectorName(toc, det);
            detector = XLALFrameUFrDetectorRead(frfile, name);
            prefix = XLALFrameUFrDetectorQueryPrefix(detector);
            /* add site if it is new */
            if (prefix && isupper(*prefix))
                if (strchr(sites, *prefix) == NULL)
                    strncat(sites, prefix, 1);
            XLALFrameUFrDetectorFree(detector);
        }
        /* sort letters in site string */
        if (*sites)
            qsort(sites, strlen(sites), sizeof(*sites), charcmp);
        else
            strcpy(sites, "-");

        for (pos = 0; pos < nframe; ++pos) {
            LALFrameUFrChan *channel;
            double tip;
            double tfp;
            double t0;
            double t1;
            size_t chan;
            tfp = XLALFrameUFrTOCQueryGTimeModf(&tip, toc, pos);
            t0 = tip + tfp;
            t1 = t0 + XLALFrameUFrTOCQueryDt(toc, pos);
            if (t0 < t0min)
                t0min = t0;
            if (t1 > t1max)
                t1max = t1;

            for (chan = 0; chan < nadc; ++chan) {
                const char *name;
                double toff;
                name = XLALFrameUFrTOCQueryAdcName(toc, chan);
                channel = XLALFrameUFrChanRead(frfile, name, pos);
                if (!channel)
                    continue;
                toff = XLALFrameUFrChanQueryTimeOffset(channel);
                if (t0 + toff < t0min)
                    t0min = t0 + toff;
                if (t1 + toff > t1max)
                    t1max = t1 + toff;
                XLALFrameUFrChanFree(channel);
            }

            for (chan = 0; chan < nsim; ++chan) {
                const char *name;
                double toff;
                name = XLALFrameUFrTOCQuerySimName(toc, chan);
                channel = XLALFrameUFrChanRead(frfile, name, pos);
                if (!channel)
                    continue;
                toff = XLALFrameUFrChanQueryTimeOffset(channel);
                if (t0 + toff < t0min)
                    t0min = t0 + toff;
                if (t1 + toff > t1max)
                    t1max = t1 + toff;
                XLALFrameUFrChanFree(channel);
            }

            for (chan = 0; chan < nproc; ++chan) {
                const char *name;
                double toff;
                name = XLALFrameUFrTOCQueryProcName(toc, chan);
                channel = XLALFrameUFrChanRead(frfile, name, pos);
                if (!channel)
                    continue;
                toff = XLALFrameUFrChanQueryTimeOffset(channel);
                if (t0 + toff < t0min)
                    t0min = t0 + toff;
                if (t1 + toff > t1max)
                    t1max = t1 + toff;
                XLALFrameUFrChanFree(channel);
            }
        }

        dsc = fnametodsc(fname);
        if (!realpath(fname, path))
            strcpy(path, "-");
        dt = ceil(t1max) - floor(t0min);
        printf("%s", sites);
        printf("\t%s", dsc);
        printf("\t%d", (int)floor(t0min));
        printf("\t%d", dt > 0 ? dt : 1);
        if (strcmp(path, "-") == 0)
            printf("\t-");
        else
            printf("\tfile://%s", path);
        printf("\n");

        free(dsc);
        XLALFrameUFrTOCFree(toc);
        XLALFrameUFrFileClose(frfile);
    }

    return 0;
}

int charcmp(const void *c1, const void *c2)
{
    char a = *((const char *)c1);
    char b = *((const char *)c2);
    return (a > b) - (a < b);
}

char *fnametodsc(const char *fname)
{
    if (fname && *fname && strcmp(fname, "-") && strlen(fname) < FILENAME_MAX) {
        char src[FILENAME_MAX];
        char dsc[FILENAME_MAX];
        const char *base;
        char *s;
        unsigned int t0;
        unsigned int dt;
        int n;

        /* get basename */
        base = strrchr(fname, '/');
        base = base ? base + 1 : fname;

        /* see if basename is in canonical form */
        n = sscanf(base, "%[A-Z]-%[a-zA-Z0-9_+#]-%u-%u.gwf", src, dsc, &t0,
            &dt);
        if (n == 4)     /* correct number of conversions */
            return strdup(dsc); /* return description field */

        /* try to convert the full basename */
        base = strrchr(fname, '/');
        base = base ? base + 1 : fname;

        /* translate spaces & hyphens to octothorpes & underscores */
        s = dsc;
        while (*base && *base != '.')
            switch (*base) {
            case ' ':
                *s++ = '#';
                ++base;
                break;
            case '-':
                *s++ = '_';
                ++base;
                break;
            default:
                *s++ = *base++;
                break;
            }
        *s = '\0';
        if (strlen(dsc))
            return strdup(dsc);
    }

    /* all else fails... */
    return strdup("-");
}
