/*
 * amiga_io.c
 *
 * I/O Interface module for Ephem Amiga port
 *
 * Written by Timo Rossi <trossi@jyu.fi> 1992-05-24
 *
 */

#include <exec/types.h>
#include <intuition/intuition.h>
#include <devices/console.h>
#include <workbench/workbench.h>
#include <workbench/startup.h>

#include <clib/exec_protos.h>
#include <clib/intuition_protos.h>
#include <clib/dos_protos.h>
#include <clib/icon_protos.h>

#ifdef __SASC
extern struct ExecBase *SysBase;
extern struct DosLibrary *DOSBase;
#include <pragmas/exec_pragmas.h>
#include <pragmas/intuition_pragmas.h>
#include <pragmas/dos_pragmas.h>
#include <pragmas/icon_pragmas.h>
#include <time.h>
extern char *_TZ;
#endif

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

#include "screen.h"

extern struct MsgPort *CreatePort(int, int);
extern void DeletePort(struct MsgPort *);
extern struct IORequest *CreateExtIO(struct MsgPort *, int);
extern void DeleteExtIO(struct IORequest *);

char *wgetenv(char *);

extern struct WBStartup *WBenchMsg;

struct Library *IconBase = NULL;
struct DiskObject *ProgIcon = NULL;
struct IntuitionBase *IntuitionBase = NULL;
struct MsgPort *ConWrPort = NULL;
struct MsgPort *ConRdPort = NULL;
struct IOStdReq *ConWrReq = NULL;
struct IOStdReq *ConRdReq = NULL;
struct Device *ConsoleDevice = NULL;
struct Window *Window = NULL;
struct Screen *Screen = NULL;
UBYTE RdChar = 0;

#define STR1(x) #x
#define STR(x) STR1(x)

#define SCR_HEIGHT 204
#define TOP_OFFSET 12

/*
 * Remember to update version number here
 */
char Screen_Title[] = "Ephem 4.28";

struct TextAttr Topaz80 = {
  "topaz.font",
  8,
  FS_NORMAL,
  FPF_ROMFONT
};

struct NewScreen NewS = {
  0, 0, 640, SCR_HEIGHT, 2,	/* left, top, width, height, depth */
  0, 1,			/* detailpen, blockpen */
  HIRES,		/* viewmodes */
  CUSTOMSCREEN,		/* type */
  &Topaz80,		/* font */
  Screen_Title,		/* title */
  NULL,			/* gadgets (not used) */
  NULL			/* custombitmap */
};

struct NewWindow NewW = {
  0, 0, 640, SCR_HEIGHT,		/* left, top, width, height */
  -1, -1,			/* detailpen & blockpen from screen */
  NULL,				/* No IDCMP, all input via console.device */
  BACKDROP|BORDERLESS|ACTIVATE|SMART_REFRESH|NOCAREREFRESH,	/* flags */
  NULL,			/* firstGadget */
  NULL,			/* checkmark */
  NULL,			/* title */
  NULL,			/* screen (filled in runtime) */
  NULL,			/* bitmap */
  0, 0, 0, 0,		/* sizing limits */
  CUSTOMSCREEN		/* screen type */
};

int CleanupAmiga(void)
{
  if(ConsoleDevice)
      {
	AbortIO(ConRdReq);
	WaitIO(ConRdReq);
	CloseDevice(ConWrReq);
      }
  if(ConRdReq) DeleteExtIO(ConRdReq);
  if(ConWrReq) DeleteExtIO(ConWrReq);
  if(ConRdPort) DeletePort(ConRdPort);
  if(ConWrPort) DeletePort(ConWrPort);
  if(Window) CloseWindow(Window);
  if(Screen) CloseScreen(Screen);

  if(ProgIcon) FreeDiskObject(ProgIcon);
  if(IconBase) CloseLibrary(IconBase);
  if(IntuitionBase) CloseLibrary(IntuitionBase);

  return 0;
}

void ErrorExit(char *msg)
{
  fprintf(stderr, "%s\n", msg);
  exit(10);
}

void InitAmiga(void)
{
  char *tzname;

  onexit(CleanupAmiga);

  if((IntuitionBase = OpenLibrary("intuition.library", 0)) == NULL)
	ErrorExit("No IntuitionLib!");

  if((Screen = OpenScreen(&NewS)) == NULL)
	ErrorExit("Can't open screen!");

  NewW.Screen = Screen;

  if((Window = OpenWindow(&NewW)) == NULL)
	ErrorExit("Can't open window!");

  if((ConRdPort = CreatePort(0, 0)) == NULL
     || (ConWrPort = CreatePort(0, 0)) == NULL)
	ErrorExit("Can't create MsgPorts!");

  if((ConRdReq = (struct IOStdReq *)
     CreateExtIO(ConRdPort, sizeof(struct IOStdReq))) == NULL
      || (ConWrReq = (struct IOStdReq *)
       CreateExtIO(ConWrPort, sizeof(struct IOStdReq))) == NULL)
	ErrorExit("Can't create IORequests!");

  ConWrReq->io_Data = (APTR)Window;

  if(OpenDevice("console.device", 0, ConWrReq, 0) != 0)
	ErrorExit("Can't open console device!");

  ConsoleDevice = ConRdReq->io_Device = ConWrReq->io_Device;
  ConRdReq->io_Unit = ConWrReq->io_Unit;
  ConRdReq->io_Command = CMD_READ;
  ConRdReq->io_Data = (APTR)&RdChar;
  ConRdReq->io_Length = 1;
  SendIO(ConRdReq);

#ifdef __SASC
  if((tzname = wgetenv("TZ")) == NULL)
	tzname = "UTC-00";

  _TZ = tzname;
  tzset();
#endif
}

/*
 * Write a NUL-terminated string to console.device
 */
void ConWrite(struct IOStdReq *req, char *str)
{
  ConWrReq->io_Command = CMD_WRITE;
  ConWrReq->io_Data = (APTR)str;
  ConWrReq->io_Length = -1;
  DoIO(ConWrReq);
}

/*
 * Read a character from console.device
 *
 * This assumes that the given IORequest has already been sent
 * to console.device with command CMD_READ
 *
 */
int ConReadCh(struct IOStdReq *req)
{
  int c;
  WaitIO(req);

  c = *((UBYTE *)req->io_Data);

  SendIO(req);
  return c;
}

/*
 * Move cursor to row, col
 */
void c_pos(int r, int c)
{
  static char buf[16];

  sprintf(buf, "\233%d;%dH", r, c);
  ConWrite(ConWrReq, buf);
}

/*
 * Erase entire screen
 */
void c_erase(void)
{
  if(ConsoleDevice == NULL)
	InitAmiga();

/* clear screen, set top offset, disable scroll, cursor home */
  ConWrite(ConWrReq, "\233H" "\2332J" "\233" STR(TOP_OFFSET) "y" "\233>1l");
}

/*
 * Erase to end of line
 */
void c_eol(void)
{
  ConWrite(ConWrReq, "\233K");
}

int chk_char(void)
{
  return (CheckIO(ConRdReq) ? 0 : -1);
}

int read_char(void)
{
  int c;

  c = ConReadCh(ConRdReq);
  if(c == 155) /* '\233' doesn't work! */
     {
	c = ConReadCh(ConRdReq);
	switch(c)
	  {
	     case 'A':
		return 'k';	/* up */

	     case 'B':
		return 'j';	/* down */

	     case 'C':
		return 'l';	/* right */

	     case 'D':
		return 'h';	/* left */

	     default:
	        while(c <= '9') c = ConReadCh(ConRdReq);
		return c;
	  }
     }
  return c;
}

/*
 * TTY cleanup, not needed here
 */
void byetty(void)
{
}

/*
 * write a string to the console
 */
void cwrite(char *str)
{
  ConWrite(ConWrReq, str);
}

/*
 * write a character to the console
 */
void cputchar(int c)
{
  char bb[2];

  bb[0] = c;
  bb[1] = '\0';
  ConWrite(ConWrReq, bb);
}

#ifdef __SASC
/*
 * SAS/C 5.10 does not have sleep()
 */
void sleep(int n)
{
  if(n) Delay(50 * n);
}
#endif

/*
 * This is from original io.c, with small modifications
 */

/* read up to max chars into buf, with cannonization.
 * add trailing '\0' (buf is really max+1 chars long).
 * return count of chars read (not counting '\0').
 * assume cursor is already positioned as desired.
 * if type END when n==0 then return -1.
 */
read_line (buf, max)
char buf[];
int max;
{
	static char erase[] = "\b \b";
	int n, c;
	int done;

#ifdef UNIX
	if (!ttysetup) setuptty();
#endif

	for (done = 0, n = 0; !done; )
	    switch (c = read_char()) {	/* does not echo */
	    case cntrl('h'):	/* backspace or */
	    case 0177:		/* delete are each char erase */
		if (n > 0) {
		    cwrite(erase);
		    n -= 1;
		}
		break;
	    case cntrl('u'):		/* line erase */
		while (n > 0) {
		    cwrite(erase);
		    n -= 1;
		}
		break;
	    case '\r':	/* EOL */
		done++;
		break;
	    default:			/* echo and store, if ok */
		if (n == 0 && c == END)
		    return (-1);
		if (n >= max)
		    cputchar (cntrl('g'));
		else if (c >= ' ') {
		    cputchar (c);
		    buf[n++] = c;
		}
	    }

	buf[n] = '\0';
	return (n);
}

/*
 * getenv that also searches tooltypes if the program was started from WB
 *
 */
char *wgetenv(char *name)
{
  BPTR lock;
  char *s;

  if(WBenchMsg)
     {
	if(IconBase == NULL)
	    {
	       if((IconBase = OpenLibrary("icon.library", 0)) == NULL)
		  goto do_getenv;
	    }
	lock = CurrentDir(WBenchMsg->sm_ArgList[0].wa_Lock);
	ProgIcon = GetDiskObject(WBenchMsg->sm_ArgList[0].wa_Name);
	CurrentDir(lock);
	if(ProgIcon)
	   {
	      if((s = FindToolType(ProgIcon->do_ToolTypes, name)) != NULL)
		 return s;
	   }
     }
do_getenv:
  return getenv(name);
}
