/* A10_EXTR.CPP         Copyright 1997,  Project Pluto                  */
/* All rights reserved.  This source code may be used freely for        */
/* non-commercial uses.                                                 */

/* Under MSC,     compile as:  cl -W4 -Ox a10_extr.cpp            */
/* Under Watcom,  compile as:  wcl386 -W4 -Ox a10_extr.cpp        */
/* If all you want is the example DOS program,  add -c -DMAKE_EXE */

/* This includes some routines to figure out which CDs would be needed */
/* to cover a given area,  plus the actual code to do the extraction.  */
/* The choice between A1.0 and A2.0 is controlled via the global int   */
/* using_a20.  If you stick in an SA1.0 or SA2.0 disk,  the code will  */
/* automatically recognize that fact and extract data from it.         */

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#define MAX_RA (360L * 3600L * 100L)
#define SPACING (MAX_RA / n_entries)
#define ZONE_SIZE (MAX_RA / 48L)
#define BUFFSIZE 1000
#define EXTRACT struct extract
#define SWAP( A, B, TEMP)   { TEMP = A;  A = B;  B = TEMP; }
#define SA10_DISK   99

static long n_entries = 1440L;

/* Yes,  I realize globals are evil... but: */

int using_a20 = 0;

EXTRACT
   {
   long ra1, ra2, spd1, spd2;
   long start_rec, end_rec;
   int zone_no, cd_no;
   };

   /* The 24 declination zones of the A1.0 catalog are spread out in a */
   /* somewhat random manner across ten CDs,  as shown in this table:  */

static const char cd_table[24] =
                   { 1, 1, 6, 5, 3, 2,  1, 4, 6, 5, 7, 10,
                     8, 7, 8, 9, 9, 4, 10, 3, 2, 6, 2,  3 };

   /* Those of the A2.0 are spread out as follows,  over eleven CDs: */

static const char cd_table_a20[24] =
          { 1, 1, 9, 7, 5, 4,  3, 2, 1, 6, 7, 10,
            9, 8, 8,11,10,11,  6, 4, 2, 3, 3,  2 };
       /*   0    15    30     45    60    75     */
       /*  90   105   120    135   150   165     */

int find_needed_a10_cds( const double center_dec_degrees,
                     const double height_degrees, int *ret_arr)
{
   int zone, rval = 0, i, j, temp_int;
   double south_end_spd = 90. + center_dec_degrees - height_degrees / 2.;
   double north_end_spd = 90. + center_dec_degrees + height_degrees / 2.;
   const char *table = (using_a20 ? cd_table_a20 : cd_table);

   for( zone = 0; zone < 24; zone++)
      if( (double)(zone + 1) * 7.5 > south_end_spd &&
                                (double)zone * 7.5 < north_end_spd)
         {
         for( i = 0; i < rval && table[zone] != ret_arr[i]; i++)
            ;
         if( i == rval)
            ret_arr[rval++] = table[zone];
         }
           /* bubblesort in order of CD # (small array) */

   for( i = 0; i < rval; i++)
      for( j = i + 1; j < rval; j++)
         if( ret_arr[j] < ret_arr[i])
            SWAP( ret_arr[i], ret_arr[j], temp_int);

   return( rval);
}

static int find_extracts( EXTRACT *ret_arr, const long center_ra,
          const long center_spd, const long width, const long height)
{
   EXTRACT total, *tptr = ret_arr;
   int rval, i, j;
   long zone, delta = 0L;

   total.ra1 = center_ra - width / 2L;
   total.ra2 = center_ra + width / 2L;
   total.spd1 = center_spd - height / 2L;
   total.spd2 = center_spd + height / 2L;

   for( zone = 0; zone < 24L; zone++)
      if( zone * ZONE_SIZE <= total.spd2 && (zone + 1L) * ZONE_SIZE > total.spd1)
         {
         memcpy( tptr, &total, sizeof( EXTRACT));
         if( tptr->spd2 > (zone + 1L) * ZONE_SIZE)
            tptr->spd2 = (zone + 1L) * ZONE_SIZE;
         if( tptr->spd1 < zone * ZONE_SIZE)
            tptr->spd1 = zone * ZONE_SIZE;
         tptr->zone_no = (int)zone;
         tptr->cd_no = (using_a20 ? cd_table_a20[tptr->zone_no] :
                                    cd_table[tptr->zone_no]);
         tptr++;
         }
   rval = tptr - ret_arr;
   if( total.ra1 < 0L)
      delta = MAX_RA;
   if( total.ra2 > MAX_RA)
      delta = -MAX_RA;
   if( delta)              /* wraps around 0h: duplicating time: */
      {
      memcpy( tptr, ret_arr, rval * sizeof( EXTRACT));
      for( i = 0; i < rval; i++, tptr++)
         {
         tptr->ra1 += delta;
         tptr->ra2 += delta;
         }
      rval *= 2;
      tptr = ret_arr;
      for( i = 0; i < rval; i++, tptr++)
         {
         if( tptr->ra1 < 0L)
            tptr->ra1 = 0L;
         if( tptr->ra2 < 0L)
            tptr->ra2 = 0L;
         if( tptr->ra1 > MAX_RA)
            tptr->ra1 = MAX_RA;
         if( tptr->ra2 > MAX_RA)
            tptr->ra2 = MAX_RA;
         }
      }
               /* bubblesort in order of CD # (small array) */
               /* within a CD,  sort by zone # */

   for( i = 0; i < rval; i++)
      for( j = i + 1; j < rval; j++)
         if( ret_arr[j].cd_no < ret_arr[i].cd_no ||
             (ret_arr[j].cd_no == ret_arr[i].cd_no &&
              ret_arr[j].zone_no < ret_arr[i].zone_no))
            {
            total = ret_arr[j];
            ret_arr[j] = ret_arr[i];
            ret_arr[i] = total;
            }

   return( rval);
}

static int set_file_extents( int n_extents, EXTRACT *ret_arr)
{
   char *filename = (n_entries == 180L ? "sa10.idx" : "a10.idx");
   FILE *ifile;

   if( using_a20)
      filename = (n_entries == 180L ? "sa20.idx" : "a20.idx");
   ifile = fopen( filename, "rb");
   if( !ifile)
      return( -2);

   while( n_extents--)
      {
      int start = (int)( ret_arr->ra1 / SPACING);
      int end = (int)( (ret_arr->ra2 + SPACING - 1) / SPACING);
      long loc = ((n_entries + 1L) * (long)ret_arr->zone_no + start);

      fseek( ifile, loc * 4L, SEEK_SET);
      fread( &ret_arr->start_rec, sizeof( long), 1, ifile);
      loc += (long)( end - start);
      fseek( ifile, loc * 4L, SEEK_SET);
      fread( &ret_arr->end_rec, sizeof( long), 1, ifile);
      ret_arr++;
      }

   fclose( ifile);
   return( 0);
}

int extract_a10_data( const int cd_letter, const double ra_degrees,
            const double dec_degrees, const double width_degrees,
            const double ht_degrees, FILE *ofile)
{
   EXTRACT *ret_arr = (EXTRACT *)calloc( 48, sizeof( EXTRACT));
   int i, n_ret, rval = 0;
   long *buff;
   char sa10_file[20];
   FILE *sa_test;

   n_ret = find_extracts( ret_arr, (long)( ra_degrees * 360000.),
                                  (long)( (dec_degrees + 90.) * 360000.),
                                   (long)( width_degrees * 360000.),
                                   (long)( ht_degrees * 360000.));

   n_entries = 1440L;

      /* if ZONE0450.CAT is on the CD,  and is smaller than 100MB, */
      /* it's SA 1.0 or 2.0 */

   sprintf( sa10_file, "%c:\\zone0450.cat", cd_letter);
   sa_test = fopen( sa10_file, "rb");
   if( sa_test)
      {
      fseek( sa_test, 0L, SEEK_END);
      if( ftell( sa_test) < 100000000L)
         {
         n_entries = 180L;
         rval = SA10_DISK;
         }
      fclose( sa_test);
      }

   if( set_file_extents( n_ret, ret_arr))
      {
      free( ret_arr);
      return( -2);
      }

   buff = (long *)calloc( BUFFSIZE, 3 * sizeof( long));
   for( i = 0; i < n_ret; i++)
      {
      FILE *ifile;
      char filename[40];
      EXTRACT *eptr = ret_arr + i;

      sprintf( filename, "%c:zone%04d.cat", cd_letter, eptr->zone_no * 75);
      ifile = fopen( filename, "rb");
      if( ifile)
         {
         long n_to_read;
         int j, k, n_read;

         fseek( ifile, eptr->start_rec * 12L, SEEK_SET);
         n_to_read = eptr->end_rec - eptr->start_rec;
         while( n_to_read && ofile)
            {
            n_read = (( n_to_read > BUFFSIZE) ? BUFFSIZE : (int)n_to_read);
            n_read = fread( buff, 12, n_read, ifile);
            for( j = 0; j < n_read; j++)
               {
               long swapped[3], ra, spd;

               memcpy( swapped, buff + j * 3, 3 * sizeof( long));
               for( k = 0; k < 3; k++)    /* A1.0 data is "wrong-endian" */
                  {
                  char tchar, *swapptr = (char *)( swapped + k);

                  SWAP( swapptr[0], swapptr[3], tchar);
                  SWAP( swapptr[1], swapptr[2], tchar);
                  }
               ra = swapped[0];
               spd = swapped[1];

               if( spd > eptr->spd1 && spd < eptr->spd2 &&
                                     ra > eptr->ra1 && ra < eptr->ra2)
                  fwrite( buff + j * 3, 3, sizeof( long), ofile);
               }
            n_to_read -= n_read;
            }
         if( !rval)
            rval = eptr->cd_no;
         fclose( ifile);
         }
      }
   free( buff);
   free( ret_arr);
   return( rval);
}

#ifdef _WINDOWS

#include <windows.h>          /* For MessageBox( ) prototype */

int build_prompt_string( const int n_needed, const int *needed_cds,
        const char *prompt_string, const int cd_letter, char *output_prompt)
{
   char tbuff[50];
   int i;

   *tbuff = '\0';
   if( n_needed > 1)
      {
            /* If multiple CDs will be needed,  list 'em all up front: */
      for( i = 0; i < n_needed - 1; i++)
         sprintf( tbuff + strlen( tbuff), "%d, ", needed_cds[i]);
      sprintf( tbuff + strlen( tbuff), "or %d", needed_cds[i]);
      }
   if( n_needed == 1)
      sprintf( tbuff, "%d", needed_cds[0]);
   for( i = 0; prompt_string[i] != '%'; i++)
      output_prompt[i] = prompt_string[i];
   output_prompt[i] = ' ';
   strcpy( output_prompt + i + 1, tbuff);
   sprintf( output_prompt + strlen( output_prompt), prompt_string + i + 2,
                                     cd_letter);
   return( 0);
}

long windows_extract_a10_data( const int cd_letter,
            const double ra_degrees,
            const double dec_degrees, const double width_degrees,
            const double ht_degrees, FILE *ofile, const char *prompt_string)
{
   int i, needed_cds[12], n_needed;

   n_needed = find_needed_a10_cds( dec_degrees, ht_degrees, needed_cds);
   while( n_needed)
      {
      int cd_no;

      cd_no = extract_a10_data( cd_letter, ra_degrees, dec_degrees,
                                       width_degrees, ht_degrees, ofile);
      if( cd_no == SA10_DISK)              /* SA1.0 CD */
         {
/*       printf( "SA1.0 data extracted\n");        */
         return( 0);
         }
      if( cd_no > 0)
         {
/*       printf( "Data extracted from A1.0 CD #%d\n", cd_no);     */
         for( i = 0; i < n_needed; i++)
            if( cd_no == needed_cds[i])
               {
               n_needed--;
               memmove( needed_cds + i, needed_cds + i + 1,
                                         (n_needed - i) * sizeof( int));
               }
         }
      if( cd_no == -2)
         {
/*       printf( "A1.0/SA1.0 index not found!\n");       */
         return( -2);
         }
      if( n_needed)
         {
         char buff[100], title[10];

         build_prompt_string( n_needed, needed_cds, prompt_string, cd_letter,
                                                            buff);
         sprintf( title, "A%d.0", using_a20 + 1);
         if( MessageBox( NULL, buff, title, MB_OKCANCEL) == IDCANCEL)
            return( -3);
         }
      }
   return( 0);
}
#else

#include <conio.h>
#include <dos.h>

long dos_prompt_extract_a10_data( const int cd_letter,
            const double ra_degrees,
            const double dec_degrees, const double width_degrees,
            const double ht_degrees, FILE *ofile)
{
   int i, needed_cds[12], n_needed;

   n_needed = find_needed_a10_cds( dec_degrees, ht_degrees, needed_cds);
   printf( "%d A%d.0 CDs needed for this area\n", n_needed, using_a20 + 1);
   if( n_needed == 0)      /* a problem with the input area asked for */
      return( -3L);

   while( n_needed)
      {
      int cd_no = extract_a10_data( cd_letter, ra_degrees, dec_degrees,
                                       width_degrees, ht_degrees, ofile);

      if( cd_no == SA10_DISK)              /* SA1.0 CD */
         {
         printf( "SA%d.0 data extracted\n", using_a20 + 1);
         return( 0);
         }
      if( cd_no > 0)
         {
         printf( "Data extracted from A%d.0 CD #%d\n", using_a20 + 1, cd_no);
         for( i = 0; i < n_needed; i++)
            if( cd_no == needed_cds[i])
               {
               n_needed--;
               memmove( needed_cds + i, needed_cds + i + 1,
                                         (n_needed - i) * sizeof( int));
               }
         }
      if( cd_no == -2)
         {
         printf( "A%d.0/SA%d.0 index not found!\n", using_a20 + 1,
                                                    using_a20 + 1);
         return( -2);
         }
      if( n_needed > 1)
         {
            /* If multiple CDs will be needed,  list 'em all up front: */
         printf( "Please insert A%d.0 CD #", using_a20 + 1);
         for( i = 0; i < n_needed - 1; i++)
            printf( "%d, ", needed_cds[i]);
         printf( "or %d and hit Enter:\n", needed_cds[i]);
         }
      if( n_needed == 1)
         printf( "Please insert A%d.0 CD #%d and hit Enter:\n",
                               using_a20 + 1, needed_cds[0]);
      if( n_needed)
         if( getch( ) == 27)
            return( -1);
         else
            {
            struct _find_t c_file;
            char *path = "z:\\*.*";

            *path = (char)cd_letter;
            _dos_findfirst( path, _A_VOLID, &c_file);
            printf( "Volume name: %s\n", c_file.name);
            }
      }
   return( 0);
}
#endif

#ifdef MAKE_EXE

#include <conio.h>

void main( int argc, char **argv)
{
   int cd_letter = argv[5][0];
   FILE *ofile = NULL;
   double center_ra = atof( argv[1]);
   double center_dec = atof( argv[2]);
   double width = atof( argv[3]);
   double height = atof( argv[4]);

   if( argc > 6)
      ofile = fopen( argv[6], "ab");
   dos_prompt_extract_a10_data( cd_letter, center_ra, center_dec,
                                width, height, ofile);
   if( ofile)
      fclose( ofile);
   printf( "Please reinsert the Guide CD and hit any key:");
   getch( );
}
#endif
