/* Spottopgm: Convert a SPOT satellite image to Portable Greymap format.
 *
 * Usage: spottopgm [-1|2|3] [Firstcol Firstline Lastcol Lastline] inputfile
 *
 * Author: Warren Toomey, 1992.
 */

#include <stdio.h>
#include "pgm.h"

/* prototypes */
int get_image ARGS((long length));
int get_imghdr ARGS((int length));
int usage ARGS((void));
/* Global variables */

FILE *spotfile;			/* The input file */
#if defined (atarist) && defined (__MBASE__)
char *linebuf;
#else
char linebuf[12000];		/* The line buffer */
#endif

long Firstline = 0,		/* The rectangle the user wants */
	 Lastline = 3000,	/* cut out of the image */
	 Firstcol = 0,
	 Lastcol = 0;
long Diff = 0;			/* Firstcol - Lastcol */
char *Bufptr;			/* Pointer into the input image */
int Color = 1;			/* Either 1, 2 or 3 */
int Colbool = 0;		/* 1 if colour */


/* Get_image extracts the pixel data from one line
 * (i.e one record) in the SPOT input file. A SPOT image
 * record has a header, data and trailer. The data lengths
 * are fixed at 3960, 5400, 8640 or 10980 bytes.
 *
 * When we arrive here we have read in 12 bytes of the record.
 * We then read in the rest of the record. We find the trailer
 * and from that determine the number of pixels on the line.
 *
 * If the image is really color i.e interleaved 3 colors, we
 * convert a line if its spectral sequence is the same as the one
 * requested by the user (i.e 1, 2 or 3). I could create a ppm file
 * but I couldn't be bothered with the rearranging of the data.
 */
int 
get_image(length)
  long length;
{
  int cnt;
  struct Linehdr		/* Each line begins with the 12 bytes */
  {				/* we have already, plus these 20 bytes */
    long linenum;		/* The line number of the record */
    short recseq;		/* The record sequence number */
    short spectseq;		/* The spectral number of the line */
    long linetime;		/* Time it was recorded (in ms). */
    long leftpixmar;		/* The pixel number of the 1st pixel */
    long rightpixmar;		/* The pixel number of the last pixel */
  } linehdr;
  long numpixels;		/* Number of pixels on the line */

				/* Get the details of this line */
  if (pm_readbiglong (spotfile, &linehdr.linenum) == -1
      || pm_readbigshort (spotfile, &linehdr.recseq) == -1
      || pm_readbigshort (spotfile, &linehdr.spectseq) == -1
      || pm_readbiglong (spotfile, &linehdr.linetime) == -1
      || pm_readbiglong (spotfile, &linehdr.leftpixmar) == -1
      | pm_readbiglong (spotfile, &linehdr.rightpixmar) == -1)
    pm_error ("EOF / read error reading line header");

				/* Now read in the line data */
  cnt = length - 20 - 88;
  cnt = fread(linebuf, 1, cnt, spotfile);

  if (!Diff)
  {
    cnt += 28;
    if (fseek (spotfile, 24, 1) == EOF)
      pm_error ("seek error");
    if (pm_readbiglong (spotfile, &numpixels) == -1)
      pm_error ("EOF / read error reading line ender");
    
    /* Determine the picture size */
    Bufptr = &linebuf[Firstcol];
    if (Lastcol == 0 || Lastcol > numpixels)
      Lastcol = numpixels;
    Diff = Lastcol - Firstcol;
				/* Print out the header */
    printf("P5\n%d %d\n255\n", Diff, Lastline - Firstline);
				/* Inform about the image size */
    if (Colbool) fprintf(stderr, "Color image, ");
    fprintf(stderr, "%d pixels wide\n", numpixels);
  }

				/* Output the line */
  if (linehdr.linenum >= Firstline && linehdr.linenum <= Lastline
	&& linehdr.spectseq == Color)
    fwrite(Bufptr, 1, Diff, stdout);
  if (linehdr.linenum > Lastline) exit(0);

#ifdef DEBUG
  fprintf(stderr,
	"Line %4d, %3d, %3d, time %4d, l/r pixmar %4d %4d len %d pixnum %d\n",
	  linehdr.linenum, linehdr.recseq, linehdr.spectseq, linehdr.linetime,
	  linehdr.leftpixmar, linehdr.rightpixmar, length, numpixels);
#endif
			/* And return the amount to seek */
  return (length - 20 - cnt);
}

/* The image header tells us if the image is in monochrome or color, and
 * if the latter, if the input colors are interleaved. If interleaved
 * color, lines are interleaved R, G, B, R, G, B etc. Technically, some
 * interleaving of infra-red, visible and ultra-violet.
 *
 * In the description field below,
 *	element 0 == P --> monochrome
 *	element 0 == X --> color
 *	element 9 == S --> sequential (i.e only one color here)
 *	element 9 == I --> interleaved (1 or more colors)
 */
int 
get_imghdr(length)
  int length;
{
  struct Imghdr
  {
    long linewidth;
    char dummy1[36];
    char description[16];	/* Type of image */
  } header;

  if (pm_readbiglong (spotfile, &header.linewidth) == -1)
    pm_error ("EOF / read error reading header");
#ifdef DEBUG
  if (fread (header.dummy1, 1, 36, spotfile) != 36)
    pm_error ("EOF / read error reading header");
#else
  if (fseek (spotfile, 36, 1) == EOF)
    pm_error ("seek error");
#endif
  if (fread (header.description, 1, 16, spotfile) != 16)
    pm_error ("EOF / read error reading header");

				/* Determine mono or colour */
  if (header.description[0] == 'X' && header.description[9] == 'S')
    Colbool = 1;
  else Colbool = 0;

#ifdef DEBUG
  fprintf(stderr, "Dummy str is >%s<\n", header.dummy1);
  fprintf(stderr, "Imghdr str is >%s<, col %d\n", header.description, Colbool);
#endif
				/* Return the amount to fseek */
  return (length - 56);
}

usage()
{
  fprintf(stderr,
"Usage: spottopgm [-1|2|3] [Firstcol Firstline Lastcol Lastline] input_file\n");
  exit(1);
}

int
main(argc, argv)
  int argc;
  char *argv[];

{
  struct Record				/* A SPOT image is broken up into */
  {					/* records with the following fields */
    long record;			/* The record number (1, 2, 3...) */
    unsigned char sub1;			/* Record sub type 1 */
    unsigned char type;			/* The record type */
    unsigned char sub2;			/* Record sub type 2 */
    unsigned char sub3;			/* Record sub type 3 */
    long length;			/* Record length in bytes */
  } arecord;

  pgm_init( &argc, argv );
   
  switch (argc)
  {
    case 7:
      Color= -(atoi(argv[1]));		/* Get the color to extract */
      argv++;
    case 6:
      Firstcol = atoi(argv[1]);		/* Get the rectangle to extract */
      Firstline = atoi(argv[2]);
      Lastcol = atoi(argv[3]);
      Lastline = atoi(argv[4]);
      argv += 4;
      goto openfile;			/* Yuk, a goto! */
    case 3:
      Color= -(atoi(argv[1]));		/* Get the color to extract */
      argv++;
    case 2:
openfile:
      spotfile = fopen(argv[1], "r");	/* Open the input file */
      if (spotfile == NULL) { perror("fopen"); exit(1); }
      break;
    default:
      usage();
  }

#if defined (atarist) && defined (__MBASE__)
  linebuf = (char *) pm_allocrow (12000, sizeof (char));
#endif

  while (1)				/* Get a record */
  {
    if (pm_readbiglong (spotfile, &arecord.record) == -1)
      break;
    arecord.sub1 = fgetc (spotfile);
    arecord.type = fgetc (spotfile);
    arecord.sub2 = fgetc (spotfile);
    arecord.sub3 = fgetc (spotfile);
    if (pm_readbiglong (spotfile, &arecord.length) == -1)
      pm_error ("EOF / read error reading a record");

    arecord.length -= 12;	/* Subtract header size as well */
    if (arecord.type == 0355 && arecord.sub1 == 0355)
      arecord.length = get_image(arecord.length);
    else if (arecord.type == 0300 && arecord.sub1 == 077)
      arecord.length = get_imghdr(arecord.length);
#ifdef DEBUG
    else
      fprintf(stderr, "Rcrd %3d, type %03o, stype %03o %03o %03o, length %d\n",
	      arecord.record, arecord.type, arecord.sub1, arecord.sub2,
	      (int) arecord.sub3 & 0xff, arecord.length);
#endif
				/* Seek to next record */
    fseek(spotfile, arecord.length, 1);
  }
  exit (0);
}
