/*
# 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 <stdint.h>
#include <math.h>
#include <sys/types.h>
#include <sys/mman.h>
#include <string.h>
#include <ctype.h>

#include "starutil.h"
#include "bl.h"
#include "mathutil.h"
#include "an-endian.h"

const char* OPTIONS = "h";

// size of entries in Stellarium's hipparcos.fab file.
static int HIP_SIZE = 15;
// byte offset to the first element in Stellarium's hipparcos.fab file.
static int HIP_OFFSET = 4;

void print_help(char* progname) {
    //BOILERPLATE_HELP_HEADER(stdout);
    printf("\nUsage: %s\n"
           "\n", progname);
}

static char* const_dirs[] = {
    ".",
    "/usr/share/stellarium/data/sky_cultures/western", // Debian
    "/home/gmaps/usnob-map/execs" // FIXME
};

static char* hipparcos_fn = "hipparcos.fab";
static char* constfn = "constellationship.fab";
static char* hip_dirs[] = {
    ".",
    "/usr/share/stellarium/data", // Debian
    "/home/gmaps/usnob-map/execs"
};

typedef union {
    uint32_t i;
    float    f;
} intfloat;

static void hip_get_radec(unsigned char* hip, int star1,
                          double* ra, double* dec) {
    intfloat ifval;
    ifval.i = *((uint32_t*)(hip + HIP_SIZE * star1));
    v32_letoh(&ifval.i);
    *ra = ifval.f;
    // Stellarium stores RA in hours...
    *ra *= (360.0 / 24.0);
    ifval.i = *((uint32_t*)(hip + HIP_SIZE * star1 + 4));
    v32_letoh(&ifval.i);
    *dec = ifval.f;
}


int main(int argc, char** args) {
    int c;
    FILE* fconst = NULL;
    uint32_t nstars;
    size_t mapsize;
    void* map;
    unsigned char* hip;
    FILE* fhip = NULL;
    int i;
	pl* cstars;
	il* alluniqstars;
	sl* shortnames;

    while ((c = getopt(argc, args, OPTIONS)) != -1) {
        switch (c) {
        case 'h':
            print_help(args[0]);
            exit(0);
        }
    }

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

	for (i=0; i<sizeof(const_dirs)/sizeof(char*); i++) {
		char fn[256];
		snprintf(fn, sizeof(fn), "%s/%s", const_dirs[i], constfn);
		fprintf(stderr, "render_constellation: Trying file: %s\n", fn);
		fconst = fopen(fn, "rb");
		if (fconst)
			break;
	}
	if (!fconst) {
		fprintf(stderr, "render_constellation: couldn't open any constellation files.\n");
		return -1;
	}

	for (i=0; i<sizeof(hip_dirs)/sizeof(char*); i++) {
		char fn[256];
		snprintf(fn, sizeof(fn), "%s/%s", hip_dirs[i], hipparcos_fn);
		fprintf(stderr, "render_constellation: Trying hip file: %s\n", fn);
		fhip = fopen(fn, "rb");
		if (fhip)
			break;
	}
	if (!fhip) {
		fprintf(stderr, "render_constellation: unhip\n");
		return -1;
	}

	// first 32-bit int: 
	if (fread(&nstars, 4, 1, fhip) != 1) {
		fprintf(stderr, "render_constellation: failed to read nstars.\n");
		return -1;
	}
	v32_letoh(&nstars);
	fprintf(stderr, "render_constellation: Found %i Hipparcos stars\n", nstars);

	mapsize = nstars * HIP_SIZE + HIP_OFFSET;
	map = mmap(0, mapsize, PROT_READ, MAP_SHARED, fileno(fhip), 0);
	hip = ((unsigned char*)map) + HIP_OFFSET;

	// for each constellation, its il* of lines.
	cstars = pl_new(16);
	alluniqstars = il_new(16);
	shortnames = sl_new(16);

	for (c=0;; c++) {
		char shortname[16];
		int nlines;
		int i;
		il* stars;

		if (feof(fconst))
			break;

		if (fscanf(fconst, "%s %d ", shortname, &nlines) != 2) {
			fprintf(stderr, "failed to parse name+nlines (constellation %i)\n", c);
			fprintf(stderr, "file offset: %i (%x)\n",
					(int)ftello(fconst), (int)ftello(fconst));
			return -1;
		}
		//fprintf(stderr, "Name: %s.  Nlines %i.\n", shortname, nlines);

		stars = il_new(16);

		sl_append(shortnames, shortname);
		pl_append(cstars, stars);

		for (i=0; i<nlines; i++) {
			int star1, star2;

			if (fscanf(fconst, " %d %d", &star1, &star2) != 2) {
				fprintf(stderr, "failed parse star1+star2\n");
				return -1;
			}

			il_insert_unique_ascending(alluniqstars, star1);
			il_insert_unique_ascending(alluniqstars, star2);

			il_append(stars, star1);
			il_append(stars, star2);
		}
		fscanf(fconst, "\n");
	}
	fprintf(stderr, "render_constellations: Read %i constellations.\n", c);

	printf("static const int constellations_N = %i;\n", sl_size(shortnames));

	/*
	  for (c=0; c<sl_size(shortnames); c++) {
	  printf("static const char* shortname_%i = \"%s\";\n", c, sl_get(shortnames, c));
	  }
	  printf("static const char* shortnames[] = {");
	  for (c=0; c<sl_size(shortnames); c++) {
	  printf("shortname_%i,", c);
	  }
	  printf("};\n");
	*/
	printf("static const char* shortnames[] = {");
	for (c=0; c<sl_size(shortnames); c++) {
		printf("\"%s\",", sl_get(shortnames, c));
	}
	printf("};\n");

	printf("static const int constellation_nlines[] = {");
	for (c=0; c<pl_size(cstars); c++) {
		il* stars = pl_get(cstars, c);
		printf("%i,", il_size(stars)/2);
	}
	printf("};\n");

	for (c=0; c<pl_size(cstars); c++) {
		il* stars = pl_get(cstars, c);
		printf("static const int constellation_lines_%i[] = {", c);
		for (i=0; i<il_size(stars); i++) {
			int s = il_get(stars, i);
			int ms = il_index_of(alluniqstars, s);
			printf("%s%i", (i?",":""), ms);
		}
		printf("};\n");
	}

	printf("static const int* constellation_lines[] = {");
	for (c=0; c<pl_size(cstars); c++) {
		printf("constellation_lines_%i,", c);
	}
	printf("};\n");

	printf("static const int stars_N = %i;\n", il_size(alluniqstars));

	printf("static const double star_positions[] = {");
	for (i=0; i<il_size(alluniqstars); i++) {
		int s = il_get(alluniqstars, i);
		double ra, dec;
		hip_get_radec(hip, s, &ra, &dec);
		printf("%g,%g,", ra, dec);
	}
	printf("};\n");

	munmap(map, mapsize);
	
	fclose(fconst);
	fclose(fhip);

	return 0;
}
