/*
 # This file is part of the Astrometry.net suite.
 # Licensed under a 3-clause BSD style license - see LICENSE
 */

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <errno.h>
#include <sys/types.h>
#include <sys/mman.h>
#include <assert.h>

#include "tycho2.h"
#include "tycho2-fits.h"
#include "starutil.h"
#include "healpix.h"
#include "boilerplate.h"
#include "fitsioutils.h"

#define OPTIONS "ho:HN:"

void print_help(char* progname) {
    BOILERPLATE_HELP_HEADER(stdout);
    printf("\nUsage:\n"
           "  %s -o <output-filename(-template)>   (eg, tycho2_hp%%02i.fits if you use the -H option)\n"
           "  [-H]: do healpixification\n"
           "  [-N <healpix-nside>]\n"
           "  <input-file> [<input-file> ...]\n\n"
           "(Healpixification isn't usually necessary because the Tycho-2 catalog is small.)\n\n",
           progname);
}


int main(int argc, char** args) {
    char* outfn = NULL;
    int c;
    int nrecords, nobs;
    int Nside = 8;
    tycho2_fits** tycs;
    int i, HP;
    int do_hp = 0;

    while ((c = getopt(argc, args, OPTIONS)) != -1) {
        switch (c) {
        case '?':
        case 'h':
            print_help(args[0]);
            exit(0);
        case 'H':
            do_hp = 1;
            break;
        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);
    }

    if (do_hp) {
        HP = 12 * Nside * Nside;
    } else {
        HP = 1;
    }
		
    tycs = malloc(HP * sizeof(tycho2_fits*));
    memset(tycs, 0, HP * sizeof(tycho2_fits*));

    nrecords = 0;
    nobs = 0;

    printf("Reading Tycho-2 files... \n");
    fflush(stdout);

    for (; optind<argc; optind++) {
        char* infn;
        FILE* fid;
        char* map;
        size_t map_size;
        int i;
        anbool supplement;
        int recsize;

        infn = args[optind];
        fid = fopen(infn, "rb");
        if (!fid) {
            fprintf(stderr, "Couldn't open input file %s: %s\n", infn, strerror(errno));
            exit(-1);
        }

        if (fseeko(fid, 0, SEEK_END)) {
            fprintf(stderr, "Couldn't seek to end of input file %s: %s\n", infn, strerror(errno));
            exit(-1);
        }
        map_size = ftello(fid);
        fseeko(fid, 0, SEEK_SET);
        map = mmap(NULL, map_size, PROT_READ, MAP_SHARED, fileno(fid), 0);
        if (map == MAP_FAILED) {
            fprintf(stderr, "Couldn't mmap input file %s: %s\n", infn, strerror(errno));
            exit(-1);
        }
        fclose(fid);

        supplement = tycho2_guess_is_supplement(map);
        printf("File %s: supplement format: %s\n", infn, (supplement ? "Yes" : "No"));

        if (supplement) {
            recsize = TYCHO_SUPPLEMENT_RECORD_SIZE_RAW;
        } else {
            recsize = TYCHO_RECORD_SIZE_RAW;
        }

        if ((map_size % recsize) && (map_size % (recsize+1)) && (map_size % (recsize+2))) {
            fprintf(stderr, "Warning, input file %s has size %u which is not divisible into %i-, %i-, or %i-byte records.\n",
                    infn, (uint)map_size, recsize, recsize+1, recsize+2);
        }

        for (i=0; i<map_size;) {
            tycho2_entry entry;
            int hp;

            if (supplement) {
                if (tycho2_supplement_parse_entry(map + i, &entry)) {
                    fprintf(stderr, "Failed to parse TYCHO-2 supplement entry: offset %i in file %s.\n",
                            i, infn);
                    exit(-1);
                }
            } else {
                if (tycho2_parse_entry(map + i, &entry)) {
                    fprintf(stderr, "Failed to parse TYCHO-2 entry: offset %i in file %s.\n",
                            i, infn);
                    exit(-1);
                }
            }
            //printf("RA, DEC (%g, %g)\n", entry.RA, entry.DEC);

            i += recsize;
            // skip past "\r" and "\n".
            while ((i < map_size) &&
                   ((map[i] == '\r') || (map[i] == '\n')))
                i++;

            if (do_hp) {
                hp = radectohealpix(deg2rad(entry.ra), deg2rad(entry.dec), Nside);
            } else {
                hp = 0;
            }

            if (!tycs[hp]) {
                char fn[256];
                qfits_header* hdr;
                sprintf(fn, outfn, hp);
                tycs[hp] = tycho2_fits_open_for_writing(fn);
                if (!tycs[hp]) {
                    fprintf(stderr, "Failed to initialized FITS output file %s.\n", fn);
                    exit(-1);
                }
                hdr = tycho2_fits_get_header(tycs[hp]);

                // header remarks...
                qfits_header_add(hdr, "HEALPIXD", (do_hp ? "T" : "F"), "Is this catalog healpixified?", NULL);
                if (do_hp) {
                    fits_header_add_int(hdr, "HEALPIX", hp, "The healpix number of this catalog.");
                    fits_header_add_int(hdr, "NSIDE", Nside, "The healpix resolution.");
                }

                BOILERPLATE_ADD_FITS_HEADERS(hdr);

                qfits_header_add(hdr, "HISTORY", "Created by the program \"tycho2tofits\"", NULL, NULL);
                qfits_header_add(hdr, "HISTORY", "tycho2tofits command line:", NULL, NULL);
                fits_add_args(hdr, args, argc);
                qfits_header_add(hdr, "HISTORY", "(end of command line)", NULL, NULL);

                if (tycho2_fits_write_headers(tycs[hp])) {
                    fprintf(stderr, "Failed to write header for FITS file %s.\n", fn);
                    exit(-1);
                }
            }

            if (tycho2_fits_write_entry(tycs[hp], &entry)) {
                fprintf(stderr, "Failed to write Tycho-2 FITS entry.\n");
                exit(-1);
            }

            if (i && ((i/recsize) % 100000 == 0)) {
                printf(".");
                fflush(stdout);
            }

            nrecords++;
            nobs += entry.nobs;
        }

        munmap(map, map_size);

        printf(".");
        fflush(stdout);
    }
    printf("\n");

    // close all the files...
    for (i=0; i<HP; i++) {
        if (!tycs[i])
            continue;
        if (tycho2_fits_fix_headers(tycs[i]) ||
            tycho2_fits_close(tycs[i])) {
            fprintf(stderr, "Failed to close Tycho-2 FITS file.\n");
            exit(-1);
        }
    }
	
    printf("Read %u records, %u observations.\n", nrecords, nobs);
	
    free(tycs);
    return 0;
}

