#include "AK_globals.h"
#include <devices/printer.h>
#include <exec/errors.h>
#include <exec/io.h>
#include <intuition/intuition.h>

struct Window *my_window = NULL;

struct NewWindow my_new_window=
{
  10,            /* LeftEdge    x position of the window. */
  10,            /* TopEdge     y positio of the window. */
  200,           /* Width       200 pixels wide. */
  50,            /* Height      50 lines high. */
  2,             /* DetailPen   Text should be drawn with colour reg. 0 */
  1,             /* BlockPen    Blocks should be drawn with colour reg. 1 */
  NULL,          /* IDCMPFlags  No IDCMP flags. */
  SMART_REFRESH|
  SUPER_BITMAP|  /* Flags       SuperBitMap. (No refreshing necessary) */
  GIMMEZEROZERO| 
  WINDOWDRAG|    /*             Drag gadget. */
  WINDOWDEPTH|   /*             Depth arrange Gadgets. */
  WINDOWSIZING|  /*             Sizing Gadget. */
  ACTIVATE,      /*             The window should be Active when opened. */
  NULL,
  NULL,
  "Kanji print", /* Title       Title of the window. */
  NULL,          /* Screen      Connected to the Workbench Screen. */
  NULL,          /* BitMap      We will change this later. */
  20,            /* MinWidth */
  20,            /* MinHeight */
  640,         /* MaxWidth  */
  200,        /* MaxHeight */
  WBENCHSCREEN   /* Type        Connected to the Workbench Screen. */
};

struct BitMap my_bitmap;
UWORD map_width, map_height;
#define MAP_DEPTH 2

/* Declare how the printer request block look like: */
union printerIO
{
  struct IOStdReq ios;
  struct IODRPReq iodrp;
  struct IOPrtCmdReq iopc;
};

struct MsgPort *replymp = NULL;         /* pointer to reply port: */
union printerIO *printer_req = NULL;    /* pointer to printer request block */
UWORD printer_dev = TRUE;  /* Store the printer device error here: */
FILE *infile;

/* 
 * Routines 
 */
void printUsage();
void cleanup(char *astr);
int DoPrint(struct Window *wind, BOOL tall);
void PrtError( BYTE error );  /* Prints some information about printer error */

/* Dumps a RastPort to the printer: */
BYTE PrintRastPort(
  union printerIO *ioreq,
  struct RastPort *rp,
  struct ColorMap *cm,
  ULONG modes,
  UWORD source_x,
  UWORD source_y,
  UWORD source_w,
  UWORD source_h,
  LONG dest_w,
  LONG dest_h,
  UWORD special
);

void PrintASCII(struct RastPort *rp, unsigned char achr);

int _brk()
{
  if (my_window != NULL)  CloseWindow(my_window);
  cleanup(NULL);
  return 1;
}

void
main(int argc, char **argv)             
{                                       
  unsigned char tmp[256], tmp2[256];
  int font_size = 24;
  int map_cols, map_rows, page_line, fg;
  int i, j, len, argnum;
  BOOL tall = FALSE;
  struct Preferences pref;

  argnum = 1;
  map_cols = 80;  /* printout width: number of ASCII characters */
  map_rows = 66;  /* printout height: number of ASCII characters */
  fg = 2;         /* text color */

  if (argc < 2)
    printUsage();

  /* parse flags */
  while (argnum < argc) {
    if (argv[argnum][0] != '-')  break;

    /* upcase arg */
    for (i = 0; i < strlen(argv[argnum]); i++)
      if (islower(argv[argnum][i]))
        argv[argnum][i] = toupper(argv[argnum][i]);

    if (strcmp(argv[argnum], "-FG") == 0) {
      argnum++;
      sscanf(argv[argnum], "%d", &i);
      fg = i;
    }
    else if (strcmp(argv[argnum], "-TALL") == 0) {
      tall = TRUE;
    }
    else if (argv[argnum][1] == 'W') {
      argnum++;
      sscanf(argv[argnum], "%d", &i);
      if (i < 1)  printUsage();
      map_cols = i;
    }
    else if (argv[argnum][1] == 'H') {
      argnum++;
      sscanf(argv[argnum], "%d", &i);
      if (i < 1)  printUsage();
      map_rows = i;
    }
    else 
      printUsage();
    argnum++;
  }

  /* check if at last arg, if not, an arg didn't parse right */
  if (argnum != (argc - 1))
    printUsage();

#if LATTICE
  /* set break routine */
  if (onbreak(&_brk) != 0)  printf("Can't set break trap\n");
#endif

  /* Open the Intuition Library: */
  IntuitionBase = 
    (struct IntuitionBase *) OpenLibrary("intuition.library", 0);

  if (IntuitionBase == NULL)
    exit(1); /* Could NOT open the Intuition Library! */

  /* Open the Graphics Library: */                      
  GfxBase = (struct GfxBase *) OpenLibrary("graphics.library", 0);

  if (GfxBase == NULL) {
    /* Could NOT open the Graphics Library! */
    /* Close the Intuition Library since we have opened it: */
   CloseLibrary((struct Library *) IntuitionBase);
   exit(1);
  }

  /* check preferences: need to have black and white shading */
  if (GetPrefs(&pref, sizeof(pref)) != NULL) {
    if (pref.PrintShade != SHADE_BW) {
      CloseLibrary((struct Library *) IntuitionBase);
      CloseLibrary((struct Library *) GfxBase);
      fprintf(stderr, 
              "Set your printer's graphics output to black and white.\n");
      exit(1);
    }
  }    
  else {
    CloseLibrary((struct Library *) IntuitionBase);
    CloseLibrary((struct Library *) GfxBase);
    fprintf(stderr, "Could not get preferences.\n");
    exit(1);
  }

  /* assuming ASCII is 1/2 wide as high */
  map_width = ((map_cols * font_size/2)/16) * 16;
  /* width of the bitmap must be a multiple of 16 */

  map_height = map_rows * font_size;

  InitBitMap(&my_bitmap, MAP_DEPTH, map_width, map_height);

  /* Allocate display memory for the BitMap: */
  for (i= 0; i < MAP_DEPTH; i++)
    if ((my_bitmap.Planes[i] = 
         (PLANEPTR) AllocRaster(map_width, map_height)) == NULL) {
      /* PANIC! Not enough memory */
      CloseLibrary((struct Library *) IntuitionBase);
      CloseLibrary((struct Library *) GfxBase);
      fprintf(stderr, "Not enough memory.\n");
      exit(1);
     }

  for (i= 0; i < MAP_DEPTH; i++)
    BltClear(my_bitmap.Planes[i], RASSIZE(map_width, map_height), 0);

  my_new_window.BitMap=&my_bitmap;
  my_window = (struct Window *) OpenWindow(&my_new_window);

  /* Have we opened the window succesfully? */
  if (my_window == NULL) {
    /* Could NOT open the Window! */
    CloseLibrary((struct Library *) IntuitionBase);
    CloseLibrary((struct Library *) GfxBase);
    fprintf(stderr, "Could not open window.\n");
    exit(1);
  }

  if (AK_init(my_window, font_size, FALSE) == -1) {
    /* problems with kanji lib */
    CloseWindow(my_window);
    cleanup(NULL);
    exit(1);
  }

  SetAPen(my_window->RPort, fg);
  SetDrMd(my_window->RPort, JAM1);

  /* set up printer */
  /* Get a reply port: (No name, priority 0) */
  replymp = (struct MsgPort *) CreatePort( NULL, 0 );
  if (!replymp) {
    cleanup("Could not create the reply port!");
    exit(1);
  }

  /* Create the printer request block: */
  printer_req = 
    (union printerIO *) CreateExtIO(replymp, sizeof(union printerIO));
  if (!printer_req) {
    cleanup("Not enough memory for the printer request block!");
    exit(1);
  }

  /* Open the Printer Device: */
  printer_dev = OpenDevice("printer.device", 0, printer_req, 0);
  if( printer_dev ) {
    cleanup("Could not open the Printer Device!");
    exit(1);
  }

  /* We have opened the window, and everything seems to be OK. */
  infile = fopen(argv[argnum], "r");
  if (!infile) {
    CloseWindow(my_window);
    cleanup(NULL);
    printUsage();
  }

  page_line = 0;
  Move(my_window->RPort, 0, AK_font[AK_ASCII]->tf_Baseline);

  /* print file to rastport */
  while (!feof(infile)) {
    if (page_line < map_rows) {
      if (fgets(tmp, 199, infile) == NULL)  continue;
      i = 0;
      while (tmp[i] != '\0')
        if ((tmp[i] < ' ') && (tmp[i] != 27))  {
          /* ASCII control char, but not ESC */
          if (tmp[i] == 12) {
            /* formfeed, print page and reset page */
            if (DoPrint(my_window, tall)) {
              _brk();
              exit(1);
	    }
            page_line = 0;
            Move(my_window->RPort, 0, AK_font[AK_ASCII]->tf_Baseline);
            /* my kludgy way to clear the rastport bitmap */
            SetAPen(my_window->RPort, 0);
            RectFill(my_window->RPort, 0, 0, map_width-1, map_height-1);
            SetAPen(my_window->RPort, fg);
	  }
          else 
            PrintASCII(my_window->RPort, tmp[i]);
          i++;
	}
        else {
          /* copy non-ASCII control chars to tmp string 
             (or <esc>) and print */
          j = 0;
          while ((tmp[i] >= ' ') || (tmp[i] == 27))
            tmp2[j++] = tmp[i++];
          tmp2[j] = '\0';

          len = AK_strlen(tmp2);
          AK_Text(my_window->RPort, tmp2, len);
        }
      page_line++;
    }
    else {
      /* send rastport to printer */
      if (DoPrint(my_window, tall)) {
        _brk();
        exit(1);
      }
      page_line = 0;
      Move(my_window->RPort, 0, AK_font[AK_ASCII]->tf_Baseline);
      /* my kludgy way to clear the rastport bitmap */
      SetAPen(my_window->RPort, 0);
      RectFill(my_window->RPort, 0, 0, map_width-1, map_height-1);
      SetAPen(my_window->RPort, fg);
    }
  }

  if (page_line != 0)  /* send rastport to printer */
    if (DoPrint(my_window, tall)) {
      _brk();
      exit(1);
    } 

  CloseWindow(my_window);
  cleanup(NULL);
}

void cleanup(char *astr)
{
  int i;

  fclose(infile);

  /* Deallocate the display memory. */
  for (i= 0; i < MAP_DEPTH; i++)
    if (my_bitmap.Planes[i] ) /* Deallocate this Bitplan? */
      FreeRaster(my_bitmap.Planes[i], map_width, map_height);

  /* Close the Printer Device: */ 
  if (!printer_dev)
    CloseDevice((struct IORequest *) printer_req);

  /* Deallocate the printer request block: */
  if (printer_req)
    DeleteExtIO((struct IORequest *) printer_req);

  /* Remove the replyport: */
  if (replymp)
    DeletePort(replymp);

  AK_cleanup(astr);
}

/*
 * Handle ASCII control characters to rasport
 */
void PrintASCII(struct RastPort *rp, unsigned char achr)
{
  SHORT rpy, rpx;
  UWORD fwidth, fheight;

  rpx = rp->cp_x;
  rpy = rp->cp_y;
  fwidth = AK_font[AK_ASCII]->tf_XSize;
  fheight = AK_font[AK_ASCII]->tf_YSize;

  switch (achr) {
  case 8: /* Backspace */
    Move(rp, rpx-fwidth, rpy);
    break;
  case 9: /* Tab, figure out next tab place */
    rpx = ((rpx/fwidth)/AK_tab_width + 1) * AK_tab_width * fwidth;
    Move(rp, rpx, rpy);
    break;
  case 11: /* Vertical Tab */
    Move(rp, rpx, rpy-fheight);
    break;
  case 10: /* Linefeed */
    if (AK_LFisNL)
      Move(rp, 0, rpy+fheight);
    else
      Move(rp, rpx, rpy+fheight);
    break;
  case 12: /* Carriage Return */
    Move(rp, 0, rpy);
    break;
  default:   break;
  }
}

int DoPrint(struct Window *wind, BOOL tall)
{
  BYTE error;  /* Error number: */
  struct RastPort *rast_port;    
  struct ViewPort *view_port;
  struct ColorMap *colour_map;
  ULONG dmode = 0;
  UWORD flags;
  
  rast_port = wind->RPort;
  view_port = &(wind->WScreen->ViewPort);
  colour_map = view_port->ColorMap;
  if (tall)  dmode = HIRES;

  flags = SPECIAL_FULLCOLS| /* Special printing modes. Full width,   */
          SPECIAL_ASPECT;   /* and correct aspect ratio.             */

  /* Dump RastPort to the printer: */
  error = PrintRastPort(printer_req, rast_port, colour_map,
    dmode,            /* Asume lores display mode to get square pixels. */
    0, 0, map_width, map_height, /* display height and width */
    0, 0, flags);

  if (error) {
    PrtError(error);
    return 1;
  }
  return 0;
}


/* PrtError() tells the user what went wrong. You give it the error code */
/* you received, and PrtError() will print a short description of the    */
/* problem. Useful when debugging. (Printer errors)                      */
/*                                                                       */
/* Synopsis: PrtError( error );                                          */
/*                                                                       */
/* error:    (BYTE) The error value you want to have explained.          */

void PrtError( BYTE error )
{
  switch( error )
  {
    /* EXEC error messages: (defined in "exec/errors.h") */
    case IOERR_OPENFAIL:
      printf( "Could not open the printer device!\n" );
      break;

    case IOERR_ABORTED:
      printf( "The print request was aborted!\n" );
      break;

    case IOERR_NOCMD:
      printf( "Unknown Command!\n" );
      break;

    case IOERR_BADLENGTH:
      printf( "Bad length of the command - data!\n" );


    /* Printer Device errors: (defined in "devices/printer.h") */
    case PDERR_CANCEL:
      printf( "User cancelled the print request!\n" );
      break;
      
    case PDERR_NOTGRAPHICS:
      printf( "The printer does not support graphics!\n" );
      break;

    case PDERR_BADDIMENSION:
      printf( "The printer dimension is not valid!\n" );
      break;
      

    case PDERR_INTERNALMEMORY:
      printf( "Not enough memory for the internal printer functions!\n" );
      break;

    case PDERR_BUFFERMEMORY:
      printf( "Not enough memory for the print buffer!\n" );
      break;

    default:
      printf( "An unknown error was reported! Error nr: %d\n", error );
  }
}


/* PrintRastPort() helps you with printing graphics. It takes a    */
/* pointer to a RastPort, and dumps that RastPort to the printer.  */
/* Note that some printers does not support graphics. If the user  */
/* has a printer that can not handle graphics, this function will  */
/* return immediately with the error number "PDERR_NOTGRAPHICS".   */
/*                                                                 */
/* Synopsis: error = PrintRastPort( io, rp, cm, modes, sx, sy,     */
/*                                  sw, sh, dw, dh, special );     */
/*                                                                 */
/* error:   (BYTE) PrintRastPort() returns 0 if everything was OK, */
/*          else an error number is returned.                      */
/*                                                                 */
/* io:      (union printerIO *) Pointer to a printer request       */
/*          block.                                                 */
/*                                                                 */
/* rp:      (struct RastPort *) Pointer to the RastPort which      */
/*          should be printed.                                     */
/*                                                                 */
/* cm:      (struct ColorMap *) Pointer to a ColorMap structure    */
/*          which contains the colour information.                 */
/*                                                                 */
/* modes:   (ULONG) The ViewPort's display modes.The information   */
/*          is used to convert the picture which will be printed   */
/*          to the correct aspects. (On a low resolution screen    */
/*          each pixels is equally wide as tall. However, on a     */
/*          high resolution screen, each pixel is only half as     */
/*          wide as it is tall. The same applies for interlaced    */
/*          and non interlaced screens.) The printer device must   */
/*          also know if you want to print a "normal" picture, or  */
/*          a picture with one of the special display modes like   */
/*          "HAM" or "Extrahalf Brite". The following flags may    */
/*          be used:                                               */
/*                                                                 */
/*            HIRES:  Set this flag if you want to print a high    */
/*                    resolution screen. If this flag is not set,  */
/*                    the printer device assumes that you are      */
/*                    using a low resolution screen.               */
/*                                                                 */
/*            LACE:   Set this flag if you want to print an inter- */
/*                    laced picture. If this flag is not set, the  */
/*                    printer device assumes that you are using a  */
/*                    noninterlaced picture.                       */
/*                                                                 */
/*            HAM:    Set this flag if you want to print a "HAM"   */
/*                    picture.                                     */
/*                                                                 */
/*            EXTRA_HALFBRITE: Set this flag if you want to print  */
/*                    an "extra halfbrite" picture.                */
/*                                                                 */
/*            PUALPF: Set this flag if you want to print a dual    */
/*                    playfields screen.                           */
/*                                                                 */
/*          Note that the simplest way is to copy the Viewport     */
/*          structure's "modes" field. You will then not risk to   */
/*          forget one or more display flags.                      */
/*                                                                 */
/* sx:      (UWORD) X offset of the source picture.                */
/*                                                                 */
/* sy:      (UWORD) Y offset of the source picture.                */
/*                                                                 */
/* sw:      (UWORD) Width of the source picture.                   */
/*                                                                 */
/* sh:      (UWORD) Height of the source picture.                  */
/*                                                                 */
/* dw:      (LONG) Width of the printed picture.                   */
/*                                                                 */
/* dh:      (LONG) Height of the printed picture.                  */
/*                                                                 */
/* special: (UWORD) Special graphical printing modes. Here is the  */
/*          complete list of flags that may be used:               */
/*                                                                 */
/*            SPECIAL_MILCOLS:    If this flag is set the "dw"     */
/*                                parameter is in 1/1000".         */
/*                                                                 */
/*            SPECIAL_MILROWS:    If this flag is set the "dh"     */
/*                                parameter is in 1/1000".         */
/*                                                                 */
/*            SPECIAL_FULLCOLS:   Set this flag if you want the    */
/*                                width of the printed picture to  */
/*                                be as wide as possible.          */ 
/*                                                                 */
/*            SPECIAL_FULLROWS:   Set this flag if you want the    */
/*                                height of the printed picture to */
/*                                be as tall as possible.          */
/*                                                                 */
/*            SPECIAL_FRACCOLS:   If this flag is set the "dw"     */
/*                                parameter specifies a fraction   */
/*                                of the maximum width.            */ 
/*                                                                 */
/*            SPECIAL_FRACROWS:   If this flag is set the "dh"     */
/*                                parameter specifies a fraction   */
/*                                of the maximum height.           */ 
/*                                                                 */
/*            SPECIAL_CENTER:     Set this flag if you want the    */
/*                                picture to be centered on the    */
/*                                paper.                           */
/*                                                                 */
/*            SPECIAL_ASPECT:     Set this flag if you want to use */
/*                                the correct aspect ratio of the  */
/*                                picture.                         */
/*                                                                 */
/*            SPECIAL_DENSITY1:   Set this flag if you want the    */
/*                                picture to be printed with the   */
/*                                printer's lowest resolution.     */
/*                                Lowest resolution.               */
/*                                                                 */
/*            SPECIAL_DENSITY2:   Next resolution.                 */
/*                                                                 */
/*            SPECIAL_DENSITY3:   Next resolution.                 */
/*                                                                 */
/*            SPECIAL_DENSITY4:   Next resolution.                 */
/*                                                                 */
/*            SPECIAL_DENSITY5:   Next resolution.                 */
/*                                                                 */
/*            SPECIAL_DENSITY6:   Next resolution.                 */
/*                                                                 */
/*            SPECIAL_DENSITY7:   Use the printer's highest        */
/*                                resolution.                      */
/*                                                                 */
/*            SPECIAL_NOFORMFEED: Set this flag if you do not want */
/*                                that the paper is ejected after  */
/*                                each time you have printed       */
/*                                graphics.                        */
/*                                                                 */
/*            SPECIAL_TRUSTME:    Set this flag if you do not want */
/*                                the printer to reset any param-  */
/*                                eters while printing.            */

BYTE PrintRastPort(
  union printerIO *ioreq,
  struct RastPort *rp,
  struct ColorMap *cm,
  ULONG modes,
  UWORD source_x,
  UWORD source_y,
  UWORD source_w,
  UWORD source_h,
  LONG dest_w,
  LONG dest_h,
  UWORD special
)
{
  /* We want to dump a RastPort to the printer: */
  ioreq->iodrp.io_Command = PRD_DUMPRPORT;

  /* Set a pointer to the RastPort structure: */
  ioreq->iodrp.io_RastPort = rp;

  /* Set a pointer to the ColorMap structure: */
  ioreq->iodrp.io_ColorMap = cm;

  /* Set the "display" modes: */
  ioreq->iodrp.io_Modes = modes;

  /* X position of the source: */
  ioreq->iodrp.io_SrcX = source_x;

  /* Y position of the source: */
  ioreq->iodrp.io_SrcY = source_y;

  /* Width of the source: */
  ioreq->iodrp.io_SrcWidth = source_w;

  /* Height of the source: */
  ioreq->iodrp.io_SrcHeight = source_h;

  /* The width of the printed picture: */
  ioreq->iodrp.io_DestCols = dest_w;

  /* The height of the printed picture: */
  ioreq->iodrp.io_DestRows = dest_h;

  /* Set the special printing commands: */
  ioreq->iodrp.io_Special = special;

  /* Do our request, and return 0 if everything is OK, else */
  /* return an error number: (This is a task sleep.)        */
  return( (BYTE) DoIO((struct IORequest *) ioreq));
}

void printUsage()
{
  printf("\nUsage: Kanji_print [-TALL] [-FG num] [-W num] [-H num] <file>\n");
  printf("\tPrints file with kanji to printer.\n\n");
  printf("\t-TALL\tPixels are twice as high as they are wide.\n");
  printf("\t-FG num\tColor of text (should be black or dark color).\n");
  printf("\t-W num\tWidth of page in ASCII characters.\n");
  printf("\t-H num\tHeight of page in ASCII characters.\n");
  exit(1);
}
