/*
 # This file is part of the Astrometry.net suite.
 # Licensed under a 3-clause BSD style license - see LICENSE
 */
#include <math.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include <unistd.h>
#include <zlib.h>

#include "2mass.h"
#include "2mass-fits.h"
#include "healpix.h"
#include "starutil.h"
#include "boilerplate.h"
#include "fitsioutils.h"

#define OPTIONS "ho:N:"

void print_help(char* progname) {
    BOILERPLATE_HELP_HEADER(stdout);
    printf("usage:\n"
           "  %s -o <output-filename-template>\n"
           "  [-N <healpix-nside>]  (default = 8.)\n"
           "  <input-file> [<input-file> ...]\n"
           "\n"
           "Input files are gzipped 2MASS PSC catalog files, named like psc_aaa.gz."
           "\n", progname);
}


int main(int argc, char** args) {
    int c;
    char* outfn = NULL;
    int startoptind;
    int Nside = 8;
    int HP;
    int i;

    twomass_fits** cats;

    while ((c = getopt(argc, args, OPTIONS)) != -1) {
        switch (c) {
        case '?':
        case 'h':
            print_help(args[0]);
            exit(0);
        case 'N':
            Nside = atoi(optarg);
            break;
        case 'o':
            outfn = optarg;
            break;
        }
    }

    if (!outfn || (optind == argc)) {
        print_help(args[0]);
        exit(-1);
    }

    if (Nside < 1) {
        fprintf(stderr, "Nside must be >= 1.\n");
        print_help(args[0]);
        exit(-1);
    }

    HP = 12 * Nside * Nside;
    cats = calloc((size_t)HP, sizeof(twomass_fits*));

    printf("Nside = %i, using %i healpixes.\n", Nside, HP);

    printf("Reading 2MASS files... ");
    fflush(stdout);

    startoptind = optind;
    for (; optind<argc; optind++) {
        char* infn;
        gzFile fiz = NULL;
        char line[1024];
        int nentries;

        infn = args[optind];
        printf("\nReading file %i of %i: %s\n", 1 + optind - startoptind,
               argc - startoptind, infn);
        infn = args[optind];
        fiz = gzopen(infn, "rb");
        if (!fiz) {
            fprintf(stderr, "Failed to open file %s: %s\n", infn, strerror(errno));
            exit(-1);
        }
        nentries = 0;
        for (;;) {
            twomass_entry e;
            int hp;

            if (gzeof(fiz))
                break;

            if (gzgets(fiz, line, 1024) == Z_NULL) {
                if (gzeof(fiz))
                    break;
                fprintf(stderr, "Failed to read a line from file %s: %s\n", infn, strerror(errno));
                exit(-1);
            }

            if (twomass_parse_entry(&e, line)) {
                fprintf(stderr, "Failed to parse 2MASS entry from file %s.\n", infn);
                exit(-1);
            }

            hp = radectohealpix(deg2rad(e.ra), deg2rad(e.dec), Nside);
            if (!cats[hp]) {
                char fn[256];
                qfits_header* hdr;

                sprintf(fn, outfn, hp);
                cats[hp] = twomass_fits_open_for_writing(fn);
                if (!cats[hp]) {
                    fprintf(stderr, "Failed to open 2MASS catalog for writing to file %s (hp %i).\n", fn, hp);
                    exit(-1);
                }
                // header remarks...
                hdr = twomass_fits_get_primary_header(cats[hp]);
                BOILERPLATE_ADD_FITS_HEADERS(hdr);
                fits_header_add_int(hdr, "HEALPIX", hp, "The healpix number of this catalog.");
                fits_header_add_int(hdr, "NSIDE", Nside, "The healpix resolution.");

                fits_add_long_comment(hdr, "The fields are as described in the 2MASS documentation:");
                fits_add_long_comment(hdr, "  ftp://ftp.ipac.caltech.edu/pub/2mass/allsky/format_psc.html");
                fits_add_long_comment(hdr, "with a few exceptions:");
                fits_add_long_comment(hdr, "* all angular fields are measured in degrees");
                fits_add_long_comment(hdr, "* the photometric quality flag values are:");
                fits_add_long_comment(hdr, "    %i: 'X' in 2MASS, No brightness info available.", TWOMASS_QUALITY_NO_BRIGHTNESS);
                fits_add_long_comment(hdr, "    %i: 'U' in 2MASS, The brightness val is an upper bound.", TWOMASS_QUALITY_UPPER_LIMIT_MAG);
                fits_add_long_comment(hdr, "    %i: 'F' in 2MASS, No magnitude sigma is available", TWOMASS_QUALITY_NO_SIGMA);
                fits_add_long_comment(hdr, "    %i: 'E' in 2MASS, Profile-fit photometry was bad", TWOMASS_QUALITY_BAD_FIT);
                fits_add_long_comment(hdr, "    %i: 'A' in 2MASS, Best quality", TWOMASS_QUALITY_A);
                fits_add_long_comment(hdr, "    %i: 'B' in 2MASS, ...", TWOMASS_QUALITY_B);
                fits_add_long_comment(hdr, "    %i: 'C' in 2MASS, ...", TWOMASS_QUALITY_C);
                fits_add_long_comment(hdr, "    %i: 'D' in 2MASS, Worst quality", TWOMASS_QUALITY_D);
                fits_add_long_comment(hdr, "* the confusion/contamination flag values are:");
                fits_add_long_comment(hdr, "    %i: '0' in 2MASS, No problems.", TWOMASS_CC_NONE);
                fits_add_long_comment(hdr, "    %i: 'p' in 2MASS, Persistence.", TWOMASS_CC_PERSISTENCE);
                fits_add_long_comment(hdr, "    %i: 'c' in 2MASS, Confusion.", TWOMASS_CC_CONFUSION);
                fits_add_long_comment(hdr, "    %i: 'd' in 2MASS, Diffraction.", TWOMASS_CC_DIFFRACTION);
                fits_add_long_comment(hdr, "    %i: 's' in 2MASS, Stripe.", TWOMASS_CC_STRIPE);
                fits_add_long_comment(hdr, "    %i: 'b' in 2MASS, Band merge.", TWOMASS_CC_BANDMERGE);
                fits_add_long_comment(hdr, "* the association flag values are:");
                fits_add_long_comment(hdr, "    %i: none.", TWOMASS_ASSOCIATION_NONE);
                fits_add_long_comment(hdr, "    %i: Tycho.", TWOMASS_ASSOCIATION_TYCHO);
                fits_add_long_comment(hdr, "    %i: USNO A-2.", TWOMASS_ASSOCIATION_USNOA2);
                fits_add_long_comment(hdr, "* the NULL value for floats is %f", TWOMASS_NULL);
                fits_add_long_comment(hdr, "* the NULL value for the 'ext_key' aka 'xsc_key' field is");
                fits_add_long_comment(hdr, "   %i (0x%x).", TWOMASS_KEY_NULL, TWOMASS_KEY_NULL);

                if (twomass_fits_write_headers(cats[hp])) {
                    fprintf(stderr, "Failed to write 2MASS catalog headers: %s\n", fn);
                    exit(-1);
                }
            }
            if (twomass_fits_write_entry(cats[hp], &e)) {
                fprintf(stderr, "Failed to write 2MASS catalog entry.\n");
                exit(-1);
            }

            nentries++;
            if (!(nentries % 100000)) {
                printf(".");
                fflush(stdout);
            }
        }
        gzclose(fiz);
        printf("\n");

        printf("Read %i entries.\n", nentries);
    }

    printf("Finishing up...\n");
    for (i=0; i<HP; i++) {
        if (!cats[i])
            continue;
        if (twomass_fits_fix_headers(cats[i]) ||
            twomass_fits_close(cats[i])) {
            fprintf(stderr, "Failed to close 2MASS catalog.\n");
            exit(-1);
        }
    }
    free(cats);
    printf("Done!\n");

    return 0;
}

