/*
** Atari ST driver for starchart.
** Works on color systems only, compiled with Mark Williams C.
** Written by Dave Yearke (dgy@sigmast), September 1988.
** Portions of this program (c) Mark Williams Company.
** Thanks to the authors of the other starchart drivers for giving
** me some examples to work from.
*/

extern int exit_and_save;
extern char *getenv(), picturefile[];

#include <ctype.h>    /* isprint(), iscntrl(), etc. */
#include <linea.h>    /* Line A (low-level graphics) routines. */
#include <osbind.h>   /* Operating system bindings */
#include <vdibind.h>  /* The virtual device interface routines */
#include <xbios.h>    /* Extended BIOS bindings */
#include "starchrt.h" /* Starchart information */

/* Global line A variables used by vdi; MUST be included */
int contrl[12], intin[128], ptsin[128], intout[128], ptsout[128];
/* Array used by vs_clip() */
int cliparray[] = { 1, 1, 319, 199 };
/* Arrays used by v_opvwk() */
int work_in[] = { 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 2 };
int work_out[57];

#define TRUE 1
#define NULL 0

/*
** Starchart was designed for 1024x768 pixels.  These macros scale the
** image to the 320x200 Atari ST low-resolution (16-color) mode.  Also,
** the y coord is subtracted from 200 so the image isn't upside-down.
*/
#define xadjust(x) ((x) * 0.3125)
#define yadjust(y) (200 - ((y) * 0.2604167))

struct la_font *fontp; /* Line A font header. */
char line[100], linemode, *p;
char scr_wrk[1024];
int vdihandle; /* Virtual device's handle */

int CURRX, CURRY, currez, old_color[16], oldrez = 99; /* 99 is impossible! */

/*
** Settings for the palette, given as RGB, 3 bits for each color (512 total).
** I tried to use different registers for different types of objects so they
** could be set individually (for example, in DEGAS you could blink a register
** for an object you're trying to point out).
*/
int new_color[16] = { 0x000,   /* Black      - Background color */
                      0x777,   /* White      - 1st and higher mag. stars */
                      0x666,   /* White      - 2nd mag. stars */
                      0x555,   /* White      - 3rd mag. stars */
                      0x444,   /* White      - 4th mag. stars */
                      0x333,   /* White      - 5th mag. stars */
                      0x222,   /* White      - 6th and lower mag. stars */
                      0x222,   /* White      - Nebulae, Galaxies, Clusters */
                      0x007,   /* Blue       - 1st and higher mag. stars */
                      0x700,   /* Red        - 1st and higher mag. stars */
                      0x770,   /* Yellow     - Sol */
                      0x704,   /* Pink       - Inferior Planets */
                      0x404,   /* Purple     - Superior Planets */
                      0x020,   /* Dark Green - Dotted lines */
                      0x200,   /* Dark Red   - Hyphenated lines */
                      0x003 }; /* Deep Blue  - Text and borders */

/*
** These are reverse-image colors for the background and stars, used when the
** 'r' key is hit when the program pauses before exiting.  Switching these
** registers makes a .ice output image for a screen dump to a printer.
*/
int rev_color[8]  = { 0x777,   /* White      - Background color */
                      0x000,   /* Black      - 1st and higher mag. stars */
                      0x222,   /* Black      - 2nd mag. stars */
                      0x333,   /* Black      - 3rd mag. stars */
                      0x444,   /* Black      - 4th mag. stars */
                      0x555,   /* Black      - 5th mag. starag. starag. starag. starag. star                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                 /* Loop if 'r' is pressed, return for anything else */
               switch (i = Crawcin()) { /* Wait for keypress before quitting */
               case 'r': /* Reverse the color registers */
                    if (Setcolor(0, -1) == 0) { /* If normal, reverse it */
                         for (i = 0; i < 8; i++)
                              Setcolor(i, rev_color[i]);
                    } else { /* Make it normal */
                         for (i = 0; i < 8; i++)
                              Setcolor(i, new_color[i]);
                    }
                    continue; /* Restart the loop */
                    break;
               case 's': /* Save as a DEGAS picture */
                    strcpy(picturefile, "star.pi1");
                    savepic();
                    break;
               case 'S': /* Save as a NeoChrome picture */
                    strcpy(picturefile, "star.neo");
                    savepic();
                    break;
               default:
                    break;
               }
               break;
          }
     vecreset();
}

/*
** Restore the screen
*/
vecreset()
{
     int i;

     v_clsvwk(vdihandle); /* Close the virtual workscreen */
     appl_exit(); /* Remove the application from AES */
     Cconws("\033H\033J"); /* Clear the screen */
     Setscreen((char *)-1, (char *)-1, oldrez); /* Set resolution */
     for (i = 0; i < 16; i++) /* Set the colors back to their saved values */
          Setcolor(i, old_color[i]);
     if (((getenv("PATH")) == 0) || (strlen(getenv("PATH")) == 0))
          linea9(); /* Show mouse pointer */
     else /* If called from a shell */
          Cursconf(1, 0); /* Show cursor */
}

/*
** Save the screen as a DEGAS or NeoChrome picture
*/
savepic()
{
     int neo = 0, file; /* Assume DEGAS file */

     if (strrchr(picturefile, '.') != NULL) { /* Extender given */
          if ((!strncmp(picturefile + strrchr(picturefile, '.'), ".neo", 4)) ||
             (!strncmp(picturefile + strrchr(picturefile, '.'), ".NEO", 4)))
               ++neo; /* Save as a NeoChrome picture */
     } else
          strcat(picturefile, ".pi1");
     if ((file = creat(picturefile, 0)) == -1)
          die("Cannot open %s.", picturefile);
     if (neo) /* NeoChrome has two extra null bytes at the beginning */
          if (write(file, &currez, 2) != 2)
               die("Write failure on file %s.", picturefile);
     if (write(file, &currez, 2) != 2) /* Write the resolution */
          die("Write failure on file %s.", picturefile);
     if (write(file, &new_color[0], 32) != 32) /* Write the palette info */
          die("Write failure on file %s.", picturefile);
     if (neo) /* NeoChrome has a 128-byte header, so we'll pad with 94 bytes */
          for (neo = 0; neo < 47; ++neo) /* Recycle the variable "neo" */
               if (write(file, &currez, 2) != 2)
                    die("Write failure on file %s.", picturefile);
     if (write(file, Physbase(), 32000) != 32000) /* Write the screen */
          die("Write failure on file %s.", picturefile);
     close(file); /* Done!  (that wasn't too bad, was it?) */
}

/*
** This function is duplicated in starchrt.c for non-ST versions, using
** the INTERACTIVE manifest constant.
*/
die(a,b)
char *a, *b;
{
     char buf[80];
     int i;

     if (oldrez != 99) /* See if we ever initialized the screen. */
         vecreset(); /* Reset the screen to something sane */
     if (!strncmp(b, "\nusage", 6)) { /* Sub in the ST's usage */
          Cconws("\n\rusage:\tstar* [ Ra Dcl Scale Title Maglim Labellim ]\n");
          Cconws("\ror\tstar* [ -r Ra -d Dcl -s Scale -t Title -m Maglim -l ");
          Cconws("Labellim -f x.str ]\n\ror\tstar* [ -c con (3 or 4 letters ");
          Cconws("chosen from con.loc) -l ... ]\n\ror\tstarst -x [ file.PI1 ");
          Cconws("| file.NEO ] ... (to exit and save screen)\n\n\r");
          Cconws("Portions of this program, copyright 1984, ");
          Cconws("Mark Williams Company");
     } else {
          sprintf(buf, a, b);
          Cconws(buf);
     }
     Cconws("\n\r"); /* Cconws does not map \n into \n\r */
     /* We want to hold the screen if invoked from the GEM desktop;
        the desktop doesn't usually set any environment variables,
        and if getenv() does find anything, it's probably a NULL string. */
     if (((getenv("PATH")) == 0) || (strlen(getenv("PATH")) == 0)) {
          Cconws("press any key to continue:  "); /* Hold screen if desktop */
          i = Crawcin(); /* Read raw character input */
     }
    exit(1);
}

/*
** Functions for manipulating text.
*/

/*
** This function sets the point size for text. (not applicable)
*/
vecsize (points)
int points;
{
}

/*
** This function puts text on the screen.
*/
vecsyms (x, y, s)
int x,y;
char *s;
{
     register char *ptr;
     register unsigned int tmp;
 
     if (*s == '\0') /* Don't bother if the string is empty */
          return;   /* (A null char can make linea8() crash!) */
     fontp = la_init.li_a1[0];  /* 6x6 system font */
     FBASE = fontp->font_data; /* Pointer to start of font form */
     FWIDTH = fontp->font_width; /* Width of font form */
     DELY = fontp->font_height; /* Height of character in font */

     DSTX = xadjust(x);
     if ((DSTY = yadjust(y)) > 2)
          DSTY -= 2; /* This vertically centers the text on the line */
     if (DSTY > 191)
          DSTY = 191; /* Make sure the bottom line shows up */
     ptr = s;
     do { /* Output the characters until the end of the string is hit */
          if (!iscntrl(*ptr)) {
               tmp = *ptr - fontp->font_low_ade; /* Find the character data */
               SRCX = fontp->font_char_off[tmp]; /* within the font data */
               DELX = fontp->font_char_off[tmp + 1] - SRCX;
               /* Check screen boundaries */
               if ((DSTX > 0) && (DSTX < 311) && (DSTY > 0))
                    if (isspace(*ptr)) {
                         WMODE = 1; /* Writing mode = transparent */
                         linea8();
                         WMODE = 0; /* Writing mode = replace */
                    } else 
                         linea8(); /* Line A trap for text blit routine */
          }
     } while (*++ptr != '\0');
}

/*
** This function puts text on the screen using the greek alphabet.
** (Well, not yet, but someday ... :-))
*/
vecsymsgk(s, x, y)
char *s;
{
     vecsyms(s, x, y);
}

/*
** Functions for drawing points and lines.
*/

/*
** Change the current position of x and y.
*/
vecmove (x, y)
int x,y;
{
     CURRX = x;
     CURRY = y;
}

/*
** Move to a new location (x1, y1) and draw a solid line to (x2, y2).
*/
vecmovedraw (x1, y1, x2, y2)
int x1, y1, x2, y2;
{
     vecmove(x1, y1);
     vecdraw(x2, y2);
}

/*
** Draw a solid line to (x,y)
*/
vecdraw (x, y)
int x,y;
{
     if (linemode != 'S') {
          linemode = 'S';
          COLBIT0 = 1; /* Set the bit plane for line drawing (color 15) */
          COLBIT1 = 1;
          COLBIT2 = 1;
          COLBIT3 = 1;
     }
     X1 = xadjust(CURRX);
     Y1 = yadjust(CURRY);
     X2 = xadjust(CURRX = x);
     Y2 = yadjust(CURRY = y);
     linea3(); /* Line blit routine */
}

/*
** Draw a dotted line (dark green on the ST) to (x,y)
*/
vecdrawdot(x, y)
{
     if (linemode != 'D') {
          linemode = 'D';
          COLBIT0 = 1; /* Set the bit plane for line drawing (color 13) */
          COLBIT1 = 0;
          COLBIT2 = 1;
          COLBIT3 = 1;
     }
     X1 = xadjust(CURRX);
     Y1 = yadjust(CURRY);
     X2 = xadjust(CURRX = x);
     Y2 = yadjust(CURRY = y);
     linea3();
}

/*
** Draw a hyphenated line (dark red on the ST) to (x,y)
*/
vecdrawhyph(x, y)
{
     if (linemode != 'H') {
          linemode = 'H';
          COLBIT0 = 0; /* Set the bit plane for line drawing (color 14) */
          COLBIT1 = 1;
          COLBIT2 = 1;
          COLBIT3 = 1;
     }
     X1 = xadjust(CURRX);
     Y1 = yadjust(CURRY);
     X2 = xadjust(CURRX = x);
     Y2 = yadjust(CURRY = y);
     linea3();
}

/*
** Draw a horizontal line.
*/
drawlen (x, y, dx, dy, len)
int x, y, dx, dy, len;
{
     if (linemode != 'S') {
          linemode = 'S';
          COLBIT0 = 1; /* Set the bit plane for line drawing (color 15) */
          COLBIT1 = 1;
          COLBIT2 = 1;
          COLBIT3 = 1;
     }
     X1 = xadjust(x + dx);
     Y1 = yadjust(y + dy);
     X2 = xadjust(CURRX = x + dx + len - 1);
     Y2 = yadjust(CURRY = y + dy);
     linea3();
}

/*
** Functions for astronomical objects.
*/

/*
** Draw the sun or a planet
*/
drawPlan(x, y, mag, type, color)
int x, y, mag, type;
char *color;
{
     if (type == 'S')
          INTIN[0] = 10; /* Yellow for the Sun */
     else if ((type == 'M') || (type == 'V'))
          INTIN[0] = 11; /* Pink for inferior planets (Mercury or Venus) */
     else
          INTIN[0] = 12; /* Purple for superior planets (m, J, s, U, or N) */
     PTSIN[0] = xadjust(CURRX = x);
     PTSIN[1] = yadjust(CURRY = y);
     if (linea2() == 0) /* See if there's anything there already */
          linea1(); /* Put a pixel on the screen */
}

/*
** Draw a star.
*/
drawStar(x, y, mag, type, color)
int x, y, mag, type;
char *color;
{
     if (mag > 6)
          INTIN[0] = 6;
     else if (mag < 1)
          INTIN[0] = 1;
     else
          INTIN[0] = mag; /* No conversion necessary, direct map into palette */
     if (mag < 2) /* Brightest stars will be in color if possible */
          if (color != NULL)
               if ((color[0] == 'O') || (color[0] == 'B'))
                    INTIN[0] = 8; /* Color register 8 holds blue */
               else if ((color[0] == 'K') || (color[0] == 'M'))
                    INTIN[0] = 9; /* Color register 9 holds red */
     PTSIN[0] = xadjust(CURRX = x);
     PTSIN[1] = yadjust(CURRY = y);
     if (mag < 1)
          drawBigStar(x, y); /* Bright stars clobber anything under them */
     else if (linea2() == 0) /* See if there's anything there already */
               linea1();
}

/*
** Draw a bright star as a cross shape.
*/
drawBigStar(x, y)
int x, y;
{
     linea1(); /* x, y */
     --PTSIN[0];
     linea1(); /* x-1, y */
     ++PTSIN[0];
     --PTSIN[1];
     linea1(); /* x, y-1 */
     PTSIN[1] += 2;
     linea1(); /* x, y+1 */
     ++PTSIN[0];
     --PTSIN[1];
     linea1(); /* x+1, y */
}

/*
** Draw a nebula. ('type' = 'D' for diffuse, 'P' for planetary)
*/
drawNebu(x, y, mag, type, color)
int x, y, mag, type;
char *color;
{
     /* This isn't as clever as cstar() in starimag.c, but it's fast */
     INTIN[0] = 7; /* Make 'em faint, with this shape: */
     PTSIN[0] = xadjust(CURRX = x) - 1; /*         **       */
     PTSIN[1] = yadjust(CURRY = y);     /*        *  *      */
     linea1(); /* x-1, y                           **       */
     ++PTSIN[1];
     linea1(); /* x-1, y+1 */
     ++PTSIN[0];
     ++PTSIN[1];
     linea1(); /* x, y+2 */
     PTSIN[1] -= 3;
     linea1(); /* x, y-1 */
     ++PTSIN[0];
     ++PTSIN[1];
     linea1(); /* x+1, y */
     ++PTSIN[1];
     linea1(); /* x+1, y+1 */
}

/*
** Draw a galaxy. ('type' = 'P' for sphere, 'S' for spiral)
*/
drawGalx(x, y, mag, type, color)
int x, y, mag, type;
char *color;
{
     int i;

     CURRX = x;
     CURRY = y;
     INTIN[0] = 7; /* Make 'em faint, with this shape: */
     PTSIN[0] = xadjust(CURRX = x) - 2; /*        **  *   */
     PTSIN[1] = yadjust(CURRY = y);     /*      ******    */
     for (i = 0; i < 6; ++i) {          /*     *  **      */
          PTSIN[0] += 1;
          linea1(); /* (x-2)+i, y */
     }
     ++PTSIN[0];
     --PTSIN[1];
     linea1(); /* x+4, y-1 */
     PTSIN[0] -= 3;
     linea1(); /* x+1, y-1 */
     --PTSIN[0];
     linea1(); /* x, y-1 */
     PTSIN[1] += 2;
     linea1(); /* x, y+1 */
     ++PTSIN[0];
     linea1(); /* x+1, y+1 */
     PTSIN[0] -= 4;
     linea1(); /* x-3, y+1 */
}

/*
** Draw a cluster. ('type' = 'G' for glob., 'O' for open, 'C' for Galactic)
*/
drawClus(x, y, mag, type, color)
int x, y, mag, type;
char *color;
{
     INTIN[0] = 7; /* Make 'em faint, with this shape: */
     PTSIN[0] = xadjust(CURRX = x);     /*         * *      */
     PTSIN[1] = yadjust(CURRY = y);     /*        * * *     */
     linea1(); /* x, y                             * *      */
     PTSIN[0] -= 2;
     linea1(); /* x-2, y */
     ++PTSIN[0];
     --PTSIN[1];
     linea1(); /* x-1, y-1 */
     PTSIN[0] += 2;
     linea1(); /* x+1, y-1 */
     PTSIN[1] += 2;
     linea1(); /* x+1, y+1 */
     PTSIN[0] -= 2;
     linea1(); /* x-1, y+1 */
     PTSIN[0] += 3;
     --PTSIN[1];
     linea1(); /* x+2, y */
}
