/*++++++++++++++
.IDENTIFICATION fitsub.c
.LANGUAGE       C
.AUTHOR         Francois Ochsenbein [CDS]
.ENVIRONMENT    GSC2.2 Catalogue, compressed version
.KEYWORDS       CDS Catalogue Server
.VERSION  1.0   27-Jul-2001
.VERSION  1.1   20-Jan-2002: Replace large values of magnitude arrors by blanks
.VERSION  1.2   07-Feb-2002: Take care, thousands of records have RA=0 Dec=0 !!
.COMMENTS       Edition of GSC2.2 data from FITS file
---------------*/

#include <stdio.h>
#include <stdlib.h>	/* Malloc   */
#include <string.h>
#include <ctype.h>
#include <fcntl.h>	/* O_BINARY */
#include <math.h>
#include <gsc2.h>	/* GSC2 Structure */
#include <htm1.h>	/* HTM Operations */

#define  sind(x)	sin(x*(M_PI/180.))
#define  cosd(x)	cos(x*(M_PI/180.))
#define  asind(x)	(180./M_PI)*asin(x)

#ifndef O_BINARY	/* Required for MS-DOS	*/
#define O_BINARY 0
#endif
#include <stdio.h>

#define EDITED_LEN 	250	/* Maximal length of edited record */
#define MIN(a,b)	((a)<=(b) ? (a) : (b))
#define MAX(a,b)	((a)>=(b) ? (a) : (b))

typedef int (*SORT_FCT)(const void *, const void *) ;

/* Options for GSC2.2 queries.......	
   Flags used:
   0x01 = Verbose option
   0x10 = Read files by increasing distance from the center.
   
*/
int gsc2_options ;	/* Shared with main prog */

static struct {
    char *root;			/* Root name of GSC2	*/
    char *name ;		/* Buffer for full name	*/
    int fno;			/* Opened file number 	*/
    int htm8 ;			/* Current HTM zone	*/
    int htm0;			/* HTM given at open	*/
    int mbuf, nbuf, ibuf ;	/* Max, used, offset	*/
    long opos[17] ; 		/* List of Chuncks	*/
    long orec[257] ;		/* Cumulative Counts	*/
    unsigned char *abuf;	/* Allocated data	*/
} GSCat ;

static int *alevel8;		/* (HTM-no, dist)	*/
static int  mlevel8, nlevel8 ;
static double u_center[3], s2r;	/* Center, 2sin<2(r/2)	*/
static double ralim[2], 
	      delim[2];		/* RADIANS limits	*/
static int (*digester)() ;
static int (*checker)() ;
static long tested_records ;

static int swapping ;		/* Set to 1 or 2 (swap)	*/

/*==================================================================
		Internal Utilities
 *==================================================================*/

static void bincpy(char *dest, char *source, int len)
/*++++++++++++++++
.PURPOSE  Copy with swapping if necessary
.RETURNS  ---
-----------------*/
{
  char *a ;
    if (swapping) for (a=dest+len; a>dest; ) *--a = *(source++) ;
    else memcpy(dest, source, len) ;
}

static void tr_ou(double o[2], double u[3])
/*++++++++++++++++
.PURPOSE  Compute direction cosines (angles in degrees)
.RETURNS  ---
-----------------*/
{
  double cosdec ;
    cosdec = cosd(o[1]) ;
    u[0] = cosdec * cosd(o[0]) ;
    u[1] = cosdec * sind(o[0]) ;
    u[2] = sind(o[1]) ;
}

/*==================================================================
		Open/Close GSC2 Files
 *==================================================================*/
static void init(char *root)
/*++++++++++++++++
.PURPOSE  Initialisation of GSC2 access.
.RETURNS  ---
-----------------*/
{
  static int value ;
  char *v ;
  int n ;

    if (GSCat.root) return ; 		/* Already opened... */

    /* Verify the Swapping ! */
  
    value = 0x010203 ;
    v = (char *)(&value) ;
    if ((v[0] == 0) && (v[1] == 1) && (v[2] == 2)) 
        swapping = 0 ; 	/* No swap necessary */
    else if ((v[0] == 3) && (v[1] == 2) && (v[2] == 1)) 
        swapping = 2 ; 	/* Full-word swap */
    else {
        fprintf(stderr, "****Irrationnal Byte Swap %02x%02x%02X%02x\n", 
            v[0]&0xff, v[1]&0xff, v[2]&0xff, v[3]&0xff) ;
	exit(2) ;
    }

    /* Choose the root name */
    if (root)  GSCat.root = root ;
    if (!GSCat.root) GSCat.root = getenv("GSC2root") ;
    if (!GSCat.root) GSCat.root = "/GSC2" ;
    n = 20 + strlen(GSCat.root);	/* Size of file name */
    if (n < 128) n = 128 ;
    GSCat.name = malloc(n) ;
    GSCat.htm8 = 0 ;
    GSCat.htm0 = 0 ;
}

int gsc2_fopen(char *filename)
/*++++++++++++++++
.PURPOSE  Open a file representing one of the GSC2.2 files
.RETURNS  0 = OK / -1 = error
-----------------*/
{
  char buffer[2880*3] ;
  int file, len, reclen, i ;
  char *p ;

    if (gsc2_options&1) printf("#...gsc2_fopen(%s)\n", filename) ;
    if (!GSCat.root) init((char *)0) ;
    if (GSCat.fno > 0) {
	if (close(GSCat.fno)<0) fprintf(stderr, "****"), 
	    perror(GSCat.name) ;
	GSCat.fno = -1 ;
	GSCat.htm0 = 0 ;
    }
    file = open(filename, O_BINARY);
    if (file <= 0) {  
        fprintf(stderr, "****"), perror(filename);  
	return (-1) ; 
    }

    /* Read the FITS header */
    read(file, buffer, 2880); 
    p = "SIMPLE  =" ;
    if (strncmp(buffer, p, strlen(p))) {
        fprintf(stderr, "****Not FITS: %s\n", filename) ;
	return (-1); 
    }
    p = "XTENSION= 'TABLE" ;
    read(file, buffer, 2880); 
    if (strncmp(buffer, p, strlen(p))) {
        fprintf(stderr, "****Not 1st TABLE: %s\n", filename) ;
	return (-1); 
    }
    read(file, buffer, 2880*2) ;
    p = buffer ;
    for (i=0; i<256; i++) {
        GSCat.orec[i] = atoi(p+11) ;
	p += 21;
    }

    read(file, buffer, 2880*3) ;
    p = "XTENSION= 'BINTABLE" ;
    if (strncmp(buffer, p, strlen(p))) {
        fprintf(stderr, "****No BIN TABLE: %s\n", filename) ;
	return (-1); 
    }
    buffer[2880*3-1] = 0 ;
    for (p=buffer; p< &buffer[2880*3]; p += 80) {
        if (strncmp(p, "END     ", 8) == 0) break ;
	if (strncmp(p, "NAXIS2  ", 8) == 0) 
	    GSCat.orec[256] = atoi(p+10) ;
    }
    if (strncmp(p, "END     ", 8)) {
        fprintf(stderr, "****No END      : %s\n", filename) ;
	return (-1); 
    }

    /* Convert the record numbers into file positions */
    GSCat.opos[0] = lseek(file, 0, 1) ;
    for (i=1; i<=16; i++) 
        GSCat.opos[i] = GSCat.orec[i<<4]*100 + GSCat.opos[0] ;

    /* Fill with known values */
    GSCat.fno = file ;
    GSCat.nbuf = 0 ;		/* Nothing loaded yet */
    GSCat.ibuf = 0 ;

    return(0) ;
}

static int read_chunk(int chunkno)
/*++++++++++++++++
.PURPOSE  Load the specified chunk (level8 HTM)
.RETURNS  0 = OK / -1 = error
-----------------*/
{
  long o, len ;
  int  *p; short *s; 

    /* The file must be started and loaded */
    if (gsc2_options&1) printf("#...read_chunk(%d) in file: %s\n", 
        chunkno, GSCat.name ? GSCat.name : "") ;

    /* The table of Offsets gives the location in the File */
    o = GSCat.opos[chunkno] ;
    len = GSCat.opos[chunkno+1] - o ;
    if (len < 0) return(-1) ;

    if (lseek(GSCat.fno, o, 0) != o) {
	fprintf(stderr, "****Can't move to %ld, file: %s\n",
	    o, GSCat.name) ;
	return(-1) ;
    }

    /* Need to read the whole chunk -- enough memory ? */
    if (GSCat.mbuf < len) {
        if (GSCat.abuf) free(GSCat.abuf) ;
	GSCat.mbuf = 1+(len|0xffff) ;	/* Multiple of 64Kbytes */
	GSCat.abuf = malloc(GSCat.mbuf) ;
    }
    GSCat.nbuf = read(GSCat.fno, GSCat.abuf, len) ;
    if (GSCat.nbuf != len) {
	fprintf(stderr, "****Got %d/%d bytes, ", GSCat.nbuf, len) ;
        if (GSCat.nbuf < 0) perror(GSCat.name); 
	else fprintf(stderr, "file: %s\n", GSCat.name) ;
	return(-1) ; 
    }
    GSCat.ibuf = 0 ;	/* Current position */

    return(0) ;
}


int gsc2_open(int htm)
/*++++++++++++++++
.PURPOSE Open for reading the specified HTM cell (0 = whole sky)
.RETURNS 0 = OK / -1 = error
.REMARKS Next gsc2_read calls will stop at end of HTL cell
-----------------*/
{
  int level, htm8, stat ;
  char *p, *z ;

    if (gsc2_options&1) printf("#...gsc2_open(%s)\n", htm2a(htm)) ;
    if (!GSCat.root) init((char *)0) ;
    level = htm_level(htm) ;
    if (level > 8) htm >>= (level-8)<<1 ;
    if (level < 8) htm8 = htm << ((8-level)<<1) ;
    else htm8 = htm ;	/* First HTM cell to read */
    if (htm8 == 0) 	/* Whole sky was asked... */
        htm8 = 8 << (8*2) ;

    if (htm8 == GSCat.htm8) {	/* Already loaded ! */
	GSCat.htm0 = htm ;
	GSCat.ibuf = 0 ;
	return(0) ;
    }

    if ((htm8&(~15)) != (GSCat.htm8&(~15))) {	/* Another file ! */
        strcpy(GSCat.name, GSCat.root) ;
        z = htm2a(htm8>>4); p = GSCat.name + strlen(GSCat.name) ;
        *(p++) = '/';  *(p++) = z[0] ; *(p++) = z[1] ;
        *(p++) = '/';  *(p++) = z[2] ; *(p++) = z[3] ;
        *(p++) = '/';  *(p++) = z[4] ; *(p++) = z[5] ;
        *(p++) = '/';  strcpy(p, z) ; strcat(p, ".fit") ;
        stat = gsc2_fopen(GSCat.name) ;
        if (stat) return(stat)  ;
    }

    /* Load the asked chunk */
    if (stat = read_chunk(htm8&15)) 
	return(stat) ;

    GSCat.htm0 = htm ;
    GSCat.htm8 = htm8;
    return(0) ;
}

int gsc2_close()
/*++++++++++++++++
.PURPOSE  Close the currently opened part
.RETURNS  0 = OK / -1 = error
-----------------*/
{
    if (close(GSCat.fno) < 0) perror(GSCat.name) ;
    GSCat.htm0 = GSCat.htm8 = 0 ;
    GSCat.fno = -1 ;
    GSCat.nbuf = GSCat.ibuf = 0 ;
    return(0) ;
}

#define cpf(n, len)	bincpy((char *)(&(rec->n)), (char *)input, len), \
			input += len

static int ed_rec(unsigned char *input, GSC2rec *rec)
/*++++++++++++++++
.PURPOSE  Convert the current 
.RETURNS  0 (spurious) / 1 (OK) / 2 (modified sigmas)  
-----------------*/
{
  int i ;
  char *t ;
    rec->rho = rec->theta = rec->xy[0] = rec->xy[1] = NULLxy ;
    rec->htm6 = GSCat.htm8 >> 4 ;
    cpf(id, 4) ;
    cpf(ra, 8) ;
    cpf(de, 8) ;
    t = (char *)(&(rec->ep)) ;
    for (i=20; --i >= 0; t += 4) bincpy(t, (char *)input, 4), input += 4 ;
    i = 1 ;					/* Status  ... */
    if (rec->ep < 10) i=2, rec->ep += 1990. ;	/* Strange !!! */
    if ((rec->ra == 0) && (rec->de == 0) && (rec->e_ra < 1.e-20) && 
	(rec->e_de < 1.e-20)) return(0) ;	/* 07-Feb-2002 */
    else return(i) ;
}

int gsc2_read(GSC2rec *rec)
/*++++++++++++++++
.PURPOSE  Read next record in GSC2
.RETURNS  0(End) / -1 (Err) / 1(OK) / 2 (OK, but was modified)
-----------------*/
{
  int htm8, htm0, level, found ; 

    /* First Record in File */
    if (GSCat.fno < 0) {	/* Not yet opened ! */
	if (gsc2_open(0)) return(-1) ;	
    }

    found = 0 ;
    while(found == 0) {
        /* Buffer exhausted */
        if (GSCat.ibuf >= GSCat.nbuf) {	/* Load next record */
	    htm8 = GSCat.htm8 + 1 ;
	    level = htm_level(GSCat.htm0) ;
	    if ((htm8 >> ((8-level)<<1)) != GSCat.htm0)
	        return(0) ;			/* All was read !   */
	    htm0 = GSCat.htm0 ;
	    if (gsc2_open(htm8)) return(-1) ;
	    GSCat.htm0 = htm0 ;
        }

        found = ed_rec(GSCat.abuf + GSCat.ibuf, rec) ;
        GSCat.ibuf += 100 ;
    }
    return(found) ;
}

/*==================================================================
		Convert GSC2 Records
 *==================================================================*/

char *gsc2_head(int opt /*0=deg, 1=sexa, 2=ID, 4=Date, 8=(x,y)*/)
/*++++++++++++++++
.PURPOSE  Edit (in a static buffer) the Record Header
.RETURNS  The edited record
-----------------*/
{
  static char buf[EDITED_LEN] ;
  char *p ; int i ;
  long value ;

    p = buf ;

    sprintf(p, "%8s%-6s", "#GSC2-ID", "") ; 
    p += strlen(p) ;

    /* Edit the Position */
    sprintf(p, " %10s %10s", "RA(J2000)", "Dec(J2000)") ;
    p += strlen(p) ;

    sprintf(p, " %8s", "Epoch ") ;
    p += strlen(p) ;

    sprintf(p, " %5s %5s", "e_RA", "e_Dec") ;
    p += strlen(p) ;

    sprintf(p, " %5s ,%-4s", "Fmag", "mag") ;
    p += strlen(p) ;
    sprintf(p, " %5s ,%-4s", "Jmag", "mag") ;
    p += strlen(p) ;
    sprintf(p, " %5s ,%-4s", "Vmag", "mag") ;
    p += strlen(p) ;
#if 1	/* NO Nmag in GSC2.2 */
    sprintf(p, " %5s ,%-4s", "Nmag", "mag") ;
    p += strlen(p) ;
#else
#endif

    sprintf(p, " %4s", "Clas") ;
    p += strlen(p) ;

    sprintf(p, " %6s", "Axis") ;
    p += strlen(p) ;
    sprintf(p, " %4s", "ecc") ;
    p += strlen(p) ;
    sprintf(p, " %5s", "PA  ") ;
    p += strlen(p) ;

    sprintf(p, " %10s", "Status") ;
    p += strlen(p) ;

    *(p++) = ' ' ; *(p++) = ';' ; *(p++) = ' ' ;
    if (opt&8) 	/* Edit x,y */
	 sprintf(p, "%10s %10s", "x(\")", "y(\")") ;
    else sprintf(p, "%10s", "r(\")") ;
    p += strlen(p) ;

    return(buf) ;
}

static void pramag(char *p, float *val)
/*++++++++++++++++
.PURPOSE  Edit a magnitude + its value
.RETURNS  ---
.REMARKS  Sigma larger than 10 ==> Edit as BLANKS
-----------------*/
{
    if (__isnan(val[0]))       strcpy(p, "   --- ,    ") ; 
    else if (__isnan(val[1])) sprintf(p, " %5.2f ,    ", val[0]) ;
    else if (val[1]>=10.0)    sprintf(p, " %5.2f ,    ", val[0]) ;
    else sprintf(p, " %5.2f ,%4.2f", val[0], val[1]) ;
}

char *gsc2a(GSC2rec *pr, int opt /*0=deg, 1=sexa, 2=ID, 4=Date, 8=(x,y)*/)
/*++++++++++++++++
.PURPOSE  Edit (in a static buffer) one record.
.RETURNS  The edited record
-----------------*/
{
  static char buf[EDITED_LEN] ;
  char *p ; int i ;
  long value ;

    p = buf ;

    sprintf(p, "%s%-6d", htm2a(pr->htm6), pr->id) ; 
    p += strlen(p) ;

    /* Edit the Position */
    sprintf(p, " %010.6f %+010.6f", pr->ra*(180./M_PI), pr->de*(180./M_PI)) ;
    p += strlen(p) ;

    sprintf(p, " %8.3f", pr->ep) ;
    p += strlen(p) ;

    sprintf(p, " %5.3f %5.3f", pr->e_ra, pr->e_de) ;
    p += strlen(p) ;

#if 1
    pramag(p, &(pr->Fmag)); p += strlen(p) ;
    pramag(p, &(pr->Jmag)); p += strlen(p) ;
    pramag(p, &(pr->Vmag)); p += strlen(p) ;
    pramag(p, &(pr->Nmag)); p += strlen(p) ;
#else
    if (__isnan(pr->Fmag)) strcpy(p, "   --- ±    ") ; 
    else if (__isnan(pr->e_Fmag)) sprintf(p, " %5.2f ±    ", pr->Fmag) ;
    else sprintf(p, " %5.2f ±%4.2f", pr->Fmag, pr->e_Fmag) ;
    p += strlen(p) ;

    if (__isnan(pr->Jmag)) strcpy(p, "   --- ±    ") ; 
    else if (__isnan(pr->e_Jmag)) sprintf(p, " %5.2f ±    ", pr->Jmag) ;
    else sprintf(p, " %5.2f ±%4.2f", pr->Jmag, pr->e_Jmag) ;
    p += strlen(p) ;

    if (__isnan(pr->Vmag)) strcpy(p, "   --- ±    ") ; 
    else if (__isnan(pr->e_Vmag)) sprintf(p, " %5.2f ±    ", pr->Vmag) ;
    else sprintf(p, " %5.2f ±%4.2f", pr->Vmag, pr->e_Vmag) ;
    p += strlen(p) ;

#if 1	/* NO Nmag in GSC2.2 */
    if (__isnan(pr->Nmag)) strcpy(p, "   --- ±    ") ; 
    else if (__isnan(pr->e_Nmag)) sprintf(p, " %5.2f ±    ", pr->Nmag) ;
    else sprintf(p, " %5.2f ±%4.2f", pr->Nmag, pr->e_Nmag) ;
    p += strlen(p) ;
#endif
#endif

    sprintf(p, " %4d", pr->class) ;
    p += strlen(p) ;

    if (pr->e > 9.) {
        sprintf(p, " %6s %4s %5s", "---", "---", "---") ;
    }
    else {
        sprintf(p, " %6.2f", pr->a) ;
        p += strlen(p) ;
        sprintf(p, " %4.2f", pr->e) ;
        p += strlen(p) ;
        sprintf(p, " %5.1f", pr->pa) ;
    }
    p += strlen(p) ;

    sprintf(p, " %10d", pr->status) ;
    p += strlen(p) ;

    /* Distance in arcsec */
    if (pr->rho >= 0)  {
	*(p++) = ' ' ; *(p++) = ';' ; *(p++) = ' ' ;
	if (opt&8) 	/* Edit x,y */
	    sprintf(p, "%+10.3f %+10.3f", pr->xy[0]*3600., pr->xy[1]*3600) ;
	else sprintf(p, "%10.3f", pr->rho*3600.) ;
    }

    return(buf) ;
}

static int prec(GSC2rec *rec) { printf("%s\n", gsc2a(rec, 3)); return(1); }

/*==================================================================
			Fine checking
 *==================================================================*/

static int check_dist(GSC2rec *rec)
/*++++++++++++++++
.PURPOSE  Verify rec is close enough to center u_center
.RETURNS  0 (no) / 1 (yes)
.REMARKS  Use (dx/2)^2 + (dy/2)^2 + (dz/2)^2 <= 2 sin^2(r/2)
-----------------*/
{
  double u[3], cosdec, dr, dx ;
    cosdec = cos(rec->de) ;		/* RADIANS */
    u[0] = cosdec * cos(rec->ra) ;
    u[1] = cosdec * sin(rec->ra) ;
    u[2] = sin(rec->de);
    dx = u[0] - u_center[0] ;
    dr = dx * dx ;
    dx = u[1] - u_center[1] ;
    dr += dx * dx ;
    dx = u[2] - u_center[2] ;
    dr += dx * dx ;
    dr /= 4. ;
    return(dr <= s2r) ;
}

static int check_radec(GSC2rec *rec)
/*++++++++++++++++
.PURPOSE  Verify rec is within RA/Dec limits
.RETURNS  0 (no) / 1 (yes)
.REMARKS  Use (dx/2)^2 + (dy/2)^2 + (dz/2)^2 <= 2 sin^2(r/2)
-----------------*/
{
    if (rec->de < delim[0] || rec->de > delim[1]) return(0) ;
    if (ralim[0] <= ralim[1]) {
	if (rec->ra < ralim[0] || rec->ra > ralim[1]) return(0) ;
	return(1) ;
    }
    return(rec->ra >= ralim[0] || rec->ra <= ralim[1]) ;
}

/*==================================================================
		Action Routines operating on HTM
 *==================================================================*/

static int collect(int htm, double *triangle[3], int status)
/*++++++++++++++++
.PURPOSE  Collect the valid HTM zones in alevel8 array.
.RETURNS  1
.REMARKS  Action routine for htm_circle
-----------------*/
{
  double uct[3], du, dr ;
  int n ;
    /* Add the triangle to the list */
    if (nlevel8 >= mlevel8) {
        mlevel8 += 512 ;
	n = mlevel8*sizeof(int) ;
	if (alevel8) alevel8 = realloc(alevel8, n) ;
	else alevel8 = malloc(n) ;
    }
    alevel8[nlevel8++] = htm ;
    htm_triangle(triangle, uct) ;
    du = uct[0] - u_center[0] ; dr  = du*du ;
    du = uct[1] - u_center[1] ; dr += du*du ;
    du = uct[2] - u_center[2] ; dr += du*du ;
    dr *= 1.e8 ;
    alevel8[nlevel8++] = dr ;
    return(1) ;
}

static int read_triangle(int htm, double *triangle[3], int status)
/*++++++++++++++++
.PURPOSE  Read a full HTM triangle, and pass to the action routines.
.RETURNS  0 = OK / -1 = Stop 
-----------------*/
{
  static GSC2rec rec ; int ret ;
    if (!status) return(0) ;
    if (gsc2_open(htm)) return(-1) ;
    while ((ret = gsc2_read(&rec)) > 0) {
	tested_records++ ;
	if (checker) ret = (*checker)(&rec) ;
	if (ret) ret = (*digester)(&rec) ;
	if (ret < 0) break ;
    }
    return(ret) ;
}

static int s2int(int *a1, int *a2)
/*++++++++++++++++
.PURPOSE  Sort routine (called by qsort), 2 integers (index distance)
.RETURNS  <0 0 >0
-----------------*/
{
  int diff ;
    diff = a1[1] - a2[1] ;
    if (!diff) diff = a1[0] - a2[0] ;
    return(diff) ;
}

long gsc2_circle(double o[2], double radius, int (*digest_routine)())
/*++++++++++++++++
.PURPOSE  Search GSC2.2 stars within target radius
.RETURNS  Number of read records (passed to digest)
.REMARKS  The supplied digest_routine(GSC2rec *record) does whatever,
	return -1 if stop asked. Default is just to print out.
-----------------*/
{
  static GSC2rec rec ;
  int i, saved_level ;

    if (!GSCat.root) init((char *)0) ;
    if (!digest_routine) digest_routine = prec ;
    tested_records = 0 ;
    digester = digest_routine ;
    checker = check_dist ;

    /* Compute Center, 2.sin^2(r/2) */
    tr_ou(o, u_center) ;
    s2r = sind(radius/2.) ;
    s2r = 2.*s2r*s2r; 

    saved_level = htm_set(8) ;

    /* Default search is by HTM increasing numbers */
    if ((gsc2_options&0x10) == 0) {
	htm_circle(u_center, radius, read_triangle) ;
	htm_set(saved_level) ;		/* Restore level */
	return(tested_records) ;
    }
    /* When the search is to be done by increasing distances,
       collect the level8 triangles, sort them, and execute */

    nlevel8 = 0 ;
    htm_circle(u_center, radius, collect) ;

    if (gsc2_options&1) fprintf(stderr, 
	"====List of the %d level8 chunks:\n", nlevel8/2) ;
    qsort(alevel8, nlevel8/2, 2*sizeof(int), (SORT_FCT)s2int);
    if (gsc2_options&1) for (i=0; i<nlevel8; i+=2) 
        fprintf(stderr, "%10d %s\n", alevel8[i+1], htm2a(alevel8[i])) ;
    htm_set(saved_level) ;		/* Restore level */

    /* Read now the chunks */
    for (i=0; i<nlevel8; i += 2) {
	/* read_triangle returns -1 when stop is asked */
	if (read_triangle(alevel8[i], (double **)0, 1) < 0) 
	    break;
    }
    return(tested_records) ;
}

long gsc2_radec(double ra[2], double de[2], int (*digest_routine)())
/*++++++++++++++++
.PURPOSE  Search GSC2.2 stars within (RA Dec) limits (degrees)
.RETURNS  Number of read records (passed to digest)
.REMARKS  The supplied digest_routine(GSC2rec *record) does whatever,
	return -1 if stop asked. Default is just to print out.
	NOTE: ra may be NULL, de may be null too
-----------------*/
{
  static GSC2rec rec ;
  static double radef[2] = { 0, 360. } ;
  static double dedef[2] = { -90, 90 } ;
  int saved_level ;
  double tmp ;

    if (!GSCat.root) init((char *)0) ;
    if (!digest_routine) digest_routine = prec ;
    tested_records = 0 ;
    digester = digest_routine ;
    checker = check_radec ;

    /* Compute the limits */
    if (!ra) ra = radef ;
    if (!de) de = dedef ;
    ralim[0] = ra[0] / (180./M_PI) ;
    ralim[1] = ra[1] / (180./M_PI) ;
    delim[0] = de[0] / (180./M_PI) ;
    delim[1] = de[1] / (180./M_PI) ;

    /* Be sure Dec limits in the right order... */
    if (delim[0] > delim[1]) {
	tmp = delim[1];
	delim[1] = delim[0];
	delim[0] = tmp ;
    }

    saved_level = htm_set(8) ;
    htm_radec(ra, de, read_triangle) ;
    htm_set(saved_level) ;		/* Restore level */

    return(tested_records) ;
}


int gsc2_get(char *gsc2id, GSC2rec *rec)
/*++++++++++++++++
.PURPOSE  Search GSC2.2 identification
.RETURNS  0 / 1 / -1 (illegal GSC2 identification)
.REMARKS  
-----------------*/
{
  int htm, id ;
  char *p, x ;
    /* The first 8 bytes give the lvel-6 HTM */
    if (strlen(gsc2id) <= 8) return(-1);

    x = gsc2id[8]; gsc2id[8] = 0 ;
    htm = a2htm(gsc2id) ;
    if (htm < 16) return(-1) ;
    gsc2id[8] = x ;
    id = atoi(gsc2id+8) ;
    if (gsc2_open(htm) < 0) return(-1) ;
    while (gsc2_read(rec) > 0) {
	if (rec->id == id) return(1) ;
    }
    return(0) ;
}

/*==================================================================
		Main Program
 *==================================================================*/
#ifdef TEST
static char usage[] = "\
Usage: gsc2sub [.|-q|-l|-g]\n\
";

static char help[] = "\
       -g: search a GSC2 id\n\
       -q: search by position\n\
	.: search by HTM zone\n\
       -l: search by RA/Dec limits\n\
";
main (argc, argv) int argc; char **argv;
{
  char buf[BUFSIZ], *p ;
  long id = 0, ra, sd ;
  double q[2], o[2], radius ;
  int  getpos = 0 ;
  int  i, z;
  GSC2rec rec ;

    gsc2_options = 1 ;
    htm_set(8) ;	/* Level in hierarchy */
    if(!getenv("GSC2root")) putenv("GSC2root=.");
    if (argc != 2) {
	fprintf(stderr, "%s%s", usage, help) ;
	exit(1) ;
    }
    ++argv;
    if (strcmp(*argv, "-q") == 0) getpos = 1 ;
    else if (strcmp(*argv, "-l") == 0) getpos = 2 ;
    else if (strcmp(*argv, "-g") == 0) getpos = -1 ;
    else ; 

    radius = 1;
    while (1) {
    	if (isatty(0)) { 
	    if (getpos == 2) 
		 printf("----Give RA and Dec limits  (degrees): ") ;
	    else if (getpos == 1) 
		 printf("----Give a position, radius (degrees): ") ;
	    else if (getpos < 0) 
		 printf("----Give a GSC2 Identification: ") ;
    	    else printf("----Give a zone GSC2.2 (e.g. N0000013) ") ;
        } 
	if (! gets(buf)) break ;
	if (buf[0]) {
	    for (p=buf; isspace(*p); p++) ;
	    if (getpos>0) {
	        q[0] = atof(p) ;
	        while (isgraph(*p)) p++ ;
		while (isspace(*p)) p++ ;
	        q[1] = atof(p) ;
		while (isgraph(*p)) p++ ;
		while (isspace(*p)) p++ ;
		if ((*p == ',') || (*p == ';')) p++ ;
		while (isspace(*p)) p++ ;
		if (getpos == 2) {
		    o[0] = atof(p) ;
		    while (isgraph(*p)) p++ ;
		    while (isspace(*p)) p++ ;
		    o[1] = atof(p) ;
		    gsc2_radec(q, o, 0) ;
		}
		else {
		    radius = atof(p) ;
		    gsc2_options |= 0x10 ;
		    gsc2_circle(q, radius, 0) ;
		}
	    } 
	    else if (getpos < 0) {
		i = gsc2_get(p, &rec) ;
		if (i>0) printf("%s\n", gsc2a(&rec, 3)) ;
		else printf("****Status of getting %s: %d\n", p, i) ;
	    }
	    else {
		gsc2_open(z = a2htm(p)) ;
    		while (gsc2_read(&rec)>0) printf("%s\n", gsc2a(&rec, 3)) ;
	    }
	}
	printf("\n");
    }
    gsc2_close() ;
    exit(0);
}
#endif
