#include <stdlib.h>
#include <stdio.h>
#include <string.h>

#include "prtrans.h"

#if !defined(__MAIN_C__)
#define main	main_prdisk
#endif

#if defined(__MAIN_C__)
unsigned char buffer[256];
#else
extern unsigned char buffer[];
#endif

#define SECTORS     10

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

int
main(int argc, char **argv)
{
  FILE *file;
  int device = 8;
  int byte, i, writeflag = FALSE;
  char **parameters = argv;
  char *localfile = NULL;
  char *prg = "prdisk.prg"; /* loader program file name */
  int sectorcount = 0;
  baseaddr = portaddr[DEFAULT_PORT];

  while (*++parameters && **parameters == '-') { /* check for options */
    if (parameters[0][1] == '-') { /* "--" ends options */
      parameters++;
      break;
    }

    switch (parameters[0][1]) {
    case 'd':
      device = strtoul (*++parameters, NULL, 0);

      if (device > 15) {
	fprintf (stderr, "%s: Illegal device number specified.\n", *argv);
	return 1;
      }

      break;

    case 'r':
      writeflag = FALSE;
      break;
    case 'w':
      writeflag = TRUE;
      break;

    case 'l':
      prg = *++parameters;
      break;

    case 'p':
      baseaddr = strtoul (*++parameters, NULL, 16);

      if (baseaddr > 3) {
	fprintf (stderr, "%s: The printer port number must be between 0 and 3.\n",
			 *argv);
	return 1;
      }

      baseaddr = portaddr[baseaddr];
      break;

    case '?':
    case 'h':
    Usage:
      fprintf (stderr, "%s: Copies a file to or from a disk accessible "
		       " from a remote computer.\n\n", *argv);
      fprintf (stderr, "Usage: %s [options] [filename]\n",
		       *argv);
      fprintf (stderr, "Options:\n\t"
			 "-d device\t"
			     "Specify the device number (0-15)\n\t"
			 "-r\t"
			     "Specify read operation (default)\n\t"
			 "-w\t"
			     "Specify write operation\n\t"
			 "-l filename\t"
			     "Specify the filename of the client program\n\t"
			 "-p port\t"
			     "Specify the printer port (0 to 3)\n");
      return 1;

    default:
      fprintf (stderr, "%s: Illegal option `%s'.\n", *argv, *parameters);
      goto Usage;
    }
  }

  if (*parameters) {
    localfile = *parameters++;

    if (!(file = fopen(localfile, writeflag ? "rb" : "wb"))) {
      fprintf (stderr, "%s: Could not open the file `%s'.\n", *argv,
	       localfile);
      return 1;
    }
  }
  else
    file = writeflag ? stdin : stdout;

  {
    int addr;

    FILE *f = fopen(prg, "r");
    if (f == NULL) {
      fprintf (stderr, "%s: Cannot find %s to download\n", *argv, prg);
      return 1;
    }

    addr = fgetc(f);
    addr |= fgetc(f) << 8;
    fclose(f);

#if defined(__MAIN_C__)
    {
      char cmd[80];
      sprintf(cmd, "prload -j %x %s", addr, prg);

      fprintf(stderr, "%s\n", cmd);
      system(cmd);
    }
#else
    {
      char saddr[10];
      sprintf(saddr, "%x", addr);
      vmain(main_prload, 4, "prload", "-j", saddr, prg, NULL);
    }
#endif
  }

  if (prinit()) {
    fprintf (stderr, "%s: Could not get the I/O permissions.\n", *argv);
    return 1;
  }

  if (wait_input ()) {
    fprintf (stderr, "%s: not ready to transfer\n", *argv);
    return 6;
  }
  else
    fprintf (stderr, "%s: ok to transfer\n", *argv);

  output (writeflag ? 2 : 1); /* specify function: write or read a disk */
  output (device); /* specify device number */

  if (wait_input ()) {
    fprintf (stderr, "%s: I/O error on remote side\n", *argv);
    fclose (file);
    output (0);
    prclose ();
    return 7;
  }

  if (writeflag) { /* loop to write all disk blocks */
    while (!feof (file)) {

      sectorcount++;
      if (sectorcount % SECTORS == 0) {
	fprintf(stderr, " sector %d\r", sectorcount);
	fflush(stderr);
      }

      if (256 != fread (buffer + 1, 1, 256, file)) {
	fprintf (stderr, "%s: Could not read all of the input file.\n", *argv);
	break;
      }
      /* calculate the checksum */
      for (*buffer = i = 0; i < 256; *buffer += buffer[++i]);

      output (0); /* notify start of block */

      for (;;) {
	send (buffer, 257);

	if ((byte = wait_input ()) != 0x81)
	  break;

	fprintf(stderr, "%s: checksum error, retrying\n", *argv);
      }

      if (byte != 0x80) {
	fprintf (stderr, "%s: remote side aborted the transmission "
			 "with error code %u.\n", *argv, input ());
	goto abort;
      }
    }

    output (1); /* end the transfer */
  }
  else { /* loop to get all disk blocks */
    for (;;) {
      for (;;) {
	int chksum;

	sectorcount++;
	if (sectorcount % SECTORS == 0) {
	  fprintf(stderr, " sector %d\r", sectorcount);
	  fflush(stderr);
	}

	if ((byte = wait_input ())) {
	  if (byte == 66) /* illegal track or sector (end of disk) */
	    break;

	  fprintf (stderr, "%s: disk error %u\n", *argv, byte);
	abort:
	  fclose (file);
	  output (0);	/* terminate remote side */
	  prclose ();
	  return 7;
	}

	receive(buffer, 257);

	for (chksum = i = 0; i < 256; chksum += buffer[++i]);

	if ((chksum & 0xFF) == buffer[0]) {
	  output(0x80);
	  break;
	}
	fprintf(stderr, "checksum error "
			"(received %02x, calculated %02x), retrying\n",
		buffer[0], (chksum & 0xFF));
	output(0x81);
      }

      if (byte == 66)
	break; /* end of disk */

      fwrite (buffer + 1, 1, 256, file);
    }
  }

  fclose (file);

  output (0);	/* terminate remote side */
  prclose ();
  return 0;
}
