/*
 * The functions in this file negotiate with the operating system for
 * characters, and write characters in a barely buffered fashion on the display.
 * All operating systems.
 */
#include <exec/types.h>
#include <exec/exec.h>
#include <intuition/intuition.h>
#include <devices/console.h>
#include <stdio.h>
#include "keymap.h"

struct IntuitionBase *IntuitionBase;
#define INTUITION_REV 29

static struct NewWindow NewWindow = {
   0, 0,
   640, 200,
   -1, -1,
   0,
   SMART_REFRESH | ACTIVATE | WINDOWDRAG | WINDOWDEPTH | WINDOWSIZING,
   NULL,
   NULL,
   "MicroEMACS",
   NULL,
   NULL,
   100, 35,
   640, 200,
   WBENCHSCREEN
};

static struct Window *Window;
static struct IOStdReq consoleIO;
static struct MsgPort consoleMsgPort;

#define NOBUF 1024
static char obuf[NOBUF];
static int nobuf;

/*
 * This function fills in the keymap string fields
 */
static void FillIn(keytypes, keymap, n, strings)
UBYTE *keytypes;
UBYTE **keymap;
int n;
char **strings;
{
   int i;

   for (i = 0; i < n; ++i)
   {
      if (keytypes[i] & KCF_STRING)
      {
         if (*strings == NULL)
	 {  printf("too few KeyStrings\n"); exit(1); }
	 keymap[i] = *strings;
	 ++strings;
      }
   }
   if (*strings != NULL)
   {  printf("too many KeyStrings\n"); exit(1); }
}

/*
 * This function is called once to set up the terminal device streams.
 * On VMS, it translates SYS$INPUT until it finds the terminal, then assigns
 * a channel to it and sets it raw. On CPM it is a no-op.
 */
ttopen()
{
   int i;

   IntuitionBase = (struct IntuitionBase *)OpenLibrary("intuition.library",
							INTUITION_REV);
   if (IntuitionBase == NULL)
   {  printf("can't open Intuition\n"); exit(1); }
   Window = (struct Window *)OpenWindow(&NewWindow);
   if (Window == NULL)
   {  printf("can't open window\n"); exit(1); }
   consoleIO.io_Data = (APTR) Window;
   consoleIO.io_Length = sizeof(*Window);
   if (OpenDevice("console.device", 0, &consoleIO, 0) != 0)
   {  printf("can't open console\n"); exit(1); }
   consoleMsgPort.mp_Node.ln_Type = NT_MSGPORT;
   consoleMsgPort.mp_Flags = 0;
   if ((i =  AllocSignal(-1)) == -1)
   {  printf("can't AllocSignal\n"); exit(1); }
   consoleMsgPort.mp_SigBit = i;
   consoleMsgPort.mp_SigTask = (struct Task *)FindTask(NULL);
   consoleIO.io_Message.mn_ReplyPort = &consoleMsgPort;

   FillIn(HiKeyMapTypes, HiKeyMap, 40, HiStrings);
   consoleIO.io_Command = CD_SETKEYMAP;
   consoleIO.io_Data = (APTR) &KeyMap;
   consoleIO.io_Length = sizeof(KeyMap);
   DoIO(&consoleIO);

   nobuf = 0;
}

/*
 * This function gets called just before we go back home to the command
 * interpreter. On VMS it puts the terminal back in a reasonable state.
 * Another no-operation on CPM.
 */
ttclose()
{
   ttflush();
   CloseDevice(&consoleIO);
   CloseWindow(Window);
   CloseLibrary(IntuitionBase);
}

/*
 * Write a character to the display. On VMS, terminal output is buffered, and
 * we just put the characters in the big array, after checking for overflow.
 * On CPM terminal I/O unbuffered, so we just write the byte out. Ditto on
 * MS-DOS (use the very very raw console output routine).
 */
ttputc(c)
{
        if (nobuf >= NOBUF)
                ttflush();
        obuf[nobuf++] = c;
}

/*
 * Flush terminal buffer. Does real work where the terminal output is buffered
 * up. A no-operation on systems where byte at a time terminal I/O is done.
 */
ttflush()
{
   if (nobuf != 0)
   {
      register int i = nobuf;
      /* The DoIO write to the console trashes D7, so we declare
         an unnecessary register variable here to for it to be saved */
      
      consoleIO.io_Command = CMD_WRITE;
      consoleIO.io_Data = (APTR) obuf;
      consoleIO.io_Length = i;
      DoIO(&consoleIO);
      nobuf = 0;
   }
}

/*
 * Read a character from the terminal, performing no editing and doing no echo
 * at all. More complex in VMS that almost anyplace else, which figures. Very
 * simple on CPM, because the system can do exactly what you want.
 */
ttgetc()
{
   char ch;

   consoleIO.io_Command = CMD_READ;
   consoleIO.io_Data = (APTR) &ch;
   consoleIO.io_Length = 1;
   DoIO(&consoleIO);
   return((int)ch);
}
