/**********************************/
/*				  */
/* Amiga text adventure front-end */
/* Copyright 1995 David Kinder	  */
/*				  */
/**********************************/

#include <clib/asl_protos.h>
#include <clib/dos_protos.h>
#include <clib/exec_protos.h>
#include <clib/intuition_protos.h>
#include <clib/gadtools_protos.h>
#include <clib/graphics_protos.h>
#include <clib/macros.h>
#include <exec/exec.h>
#include <graphics/videocontrol.h>
#include <ctype.h>
#include <stdarg.h>
#include <string.h>
#include "amiga.h"

#define SHIFT (IEQUALIFIER_LSHIFT|IEQUALIFIER_RSHIFT)
#define HISTORY_SIZE 10
enum chars
  { CHAR_LEFT = 0x100,
    CHAR_SHIFTLEFT,
    CHAR_RIGHT,
    CHAR_SHIFTRIGHT,
    CHAR_UP,
    CHAR_DOWN };

extern struct Library *IntuitionBase, *GfxBase;
extern struct Library *GadToolsBase, *AslBase;

struct Window *TextWindow;
struct RastPort *TextRPort;
struct Menu *Menus;
struct FileRequester *FileReq;
APTR Visual;
UWORD MinWidth, MinHeight, TextLeft, TextRight, TextBorder, TextHeight;
char TextBuffer[4096];
char LineBuffer[512];
UWORD LinePos, MoreCount;
BOOL QuitExit = FALSE;
char *History[HISTORY_SIZE];

struct NewMenu NewMenus[] =
 {{ NM_TITLE,"Project",0,0,0,0 },
  { NM_ITEM,"About...","?",0,0,0 },
  { NM_ITEM,"Quit","Q",0,0,0 },
  { NM_END,0,0,0,0 }};

#define IGNORE_MAX 4
char *IgnoreStr[IGNORE_MAX] = {
"Would you like me to continue? ",
"If  you  suspend  your  Adventure  now, you will have to wait at least 30",
"minutes before continuing.",
"Is this acceptable? "
};

BOOL FlushText(void);
int GetCharacter(char c,char *buffer);
void CursorLeft(void);
void CursorBottomLeft(void);
void ScrollWindow(void);
void DrawCursor(BOOL on, char c);
void GetScreenSize(struct Screen *scr, ULONG *w, ULONG *h);
void AddToHistory(char *buffer);
int UseHistory(char *buffer, int hpos, int left);
UWORD GetTextHeight(int sub);

wbmain()
{
  main(0,0);
}

void AmigaSetup()
{
struct Screen *defscr;
ULONG width,height;

  if (IntuitionBase->lib_Version < 37) AmigaExit(1);

  if ((defscr = LockPubScreen(NULL)) == NULL) AmigaExit(1);
  GetScreenSize(defscr,&width,&height);
  if ((TextWindow = OpenWindowTags(NULL,
    WA_Left,0,
    WA_Top,defscr->BarHeight+1,
    WA_Width,width,
    WA_Height,height - defscr->BarHeight - 1,
    WA_Flags,
      WFLG_ACTIVATE|WFLG_CLOSEGADGET|WFLG_SIZEGADGET|WFLG_SIZEBBOTTOM|
      WFLG_DEPTHGADGET|WFLG_DRAGBAR|WFLG_SMART_REFRESH|WFLG_NEWLOOKMENUS,
    WA_IDCMP,IDCMP_CLOSEWINDOW|IDCMP_CHANGEWINDOW|IDCMP_VANILLAKEY|
       IDCMP_RAWKEY|IDCMP_MENUPICK,
    WA_Title,"Adventure 4+",
    WA_ScreenTitle,"Adventure",
    WA_PubScreen,defscr,
    WA_AutoAdjust,TRUE,TAG_DONE)) == NULL) AmigaExit(1);
  ScreenToFront(defscr);
  UnlockPubScreen(NULL,defscr);

  if ((Visual = GetVisualInfo(TextWindow->WScreen,TAG_DONE)) == NULL)
    AmigaExit(1);
  if ((Menus = CreateMenus(NewMenus,GTMN_NewLookMenus,TRUE,TAG_DONE))
    == NULL) AmigaExit(1);
  LayoutMenus(Menus,Visual,GTMN_NewLookMenus,TRUE,TAG_DONE);
  SetMenuStrip(TextWindow,Menus);

  if ((FileReq = AllocAslRequestTags(ASL_FileRequest,
    ASLFR_Window,TextWindow,
    ASLFR_SleepWindow,TRUE,
    ASLFR_RejectIcons,TRUE,
    ASLFR_InitialFile,"Adventure4.Save",TAG_DONE)) == NULL) AmigaExit(1);

  TextRPort = TextWindow->RPort;
  SetAPen(TextRPort,1);
  SetBPen(TextRPort,0);
  SetDrMd(TextRPort,JAM2);

  TextLeft = TextWindow->BorderLeft;
  TextRight = TextWindow->BorderRight;
  TextBorder = TextLeft+TextRight+2;
  MinWidth = TextBorder+(20*TextRPort->TxWidth);
  MinHeight = TextWindow->BorderTop+TextWindow->BorderBottom+
    (5*TextRPort->TxHeight);
  TextHeight = GetTextHeight(0)/TextRPort->TxHeight;

  CursorBottomLeft();
}

void AmigaExit(int code)
{
int i;

  if ((TextWindow) && (QuitExit == FALSE))
  {
    AmigaPrintF("\n[Press any key.]");
    FlushText();
    GetCharacter(0,0);
  }
  for (i = 0; i < HISTORY_SIZE; i++)
    if (History[i] != NULL) FreeVec(History[i]);
  if (FileReq) FreeAslRequest(FileReq);
  if (Menus) FreeMenus(Menus);
  if (Visual) FreeVisualInfo(Visual);
  if (TextWindow) CloseWindow(TextWindow);
  exit(code);
}

void AmigaPrintF(const char *format,...)
{
va_list va;
char *current = TextBuffer;
BOOL ret = FALSE;

  va_start(va,format);
  vsprintf(TextBuffer,format,va);
  va_end(va);

  while (*current != '\0') AmigaPutChar(*current++);
}

void AmigaPutChar(char c)
{
int i;

  switch (c)
  {
    case '\t':
      for (i = 0; i < 8; i++) AmigaPutChar(' ');
      break;
    case '\n':
      if (FlushText()) break;
      if (TextRPort->cp_y < GetTextHeight(1)+TextWindow->BorderTop+
	TextRPort->TxBaseline)
      {
	SetAPen(TextRPort,1);
	Move(TextRPort,TextLeft+1,TextRPort->cp_y+TextRPort->TxHeight);
      }
      else ScrollWindow();

      MoreCount++;
      if (MoreCount >= (GetTextHeight(0)/TextRPort->TxHeight)-1)
      {
	AmigaPrintF("[MORE]");
	FlushText();
        GetCharacter(0,0);
	CursorLeft();
	AmigaPrintF("      ");
	FlushText();
	CursorLeft();
	MoreCount = 0;
      }
      break;
    default:
      if (isprint((unsigned char)c) != 0)
      {
	*(LineBuffer+LinePos) = c;
	LinePos++;
      }
      break;
  }
}

void AmigaGetString(char *buffer, int len)
{
int c, end = 0, cursor = 0, hpos = -1;
UWORD left;

  if (FlushText())
  {
    strcpy(buffer,"yes\n");
    return;
  }
  MoreCount = 0;
  *buffer = '\0';
  left = TextRPort->cp_x;
  SetAPen(TextRPort,2);
  for (;;)
  {
    c = GetCharacter(*(buffer+cursor),buffer);
    switch (c)
    {
      case 0x0D:
	*(buffer+end) = '\n';
	*(buffer+end+1) = '\0';
	AmigaPutChar('\n');
	AddToHistory(buffer);
	return;
	break;
      case 0x7F:
	if ((end > 0) && (cursor < end))
	{
	  memmove(buffer+cursor,buffer+cursor+1,end-cursor+1);
	  ClipBlit(TextRPort,
	    TextRPort->cp_x+TextRPort->TxWidth,
	    TextRPort->cp_y-TextRPort->TxBaseline,
	    TextRPort,
	    TextRPort->cp_x,
	    TextRPort->cp_y-TextRPort->TxBaseline,
	    TextWindow->Width-TextRight-TextRPort->cp_x-TextRPort->TxWidth,
	    TextRPort->TxHeight,0xC0);
	  end--;
	}
	break;
      case 0x08:
	if ((end > 0) && (cursor > 0))
	{
	  memmove(buffer+cursor-1,buffer+cursor,end-cursor+1);
	  ClipBlit(TextRPort,
	    TextRPort->cp_x,
	    TextRPort->cp_y-TextRPort->TxBaseline,
	    TextRPort,
	    TextRPort->cp_x-TextRPort->TxWidth,
	    TextRPort->cp_y-TextRPort->TxBaseline,
	    TextWindow->Width-TextRight-TextRPort->cp_x-TextRPort->TxWidth,
	    TextRPort->TxHeight,0xC0);
	  Move(TextRPort,TextRPort->cp_x-TextRPort->TxWidth,
	    TextRPort->cp_y);
	  end--;
	  cursor--;
	}
	break;
      case CHAR_LEFT:
	if (cursor > 0)
	{
	  cursor--;
	  Move(TextRPort,TextRPort->cp_x-TextRPort->TxWidth,
	    TextRPort->cp_y);
	}
	break;
      case CHAR_SHIFTLEFT:
	if (cursor > 0)
	{
	  Move(TextRPort,left,TextRPort->cp_y);
	  cursor = 0;
	}
	break;
      case CHAR_RIGHT:
	if (cursor < end)
	{
	  cursor++;
	  Move(TextRPort,TextRPort->cp_x+TextRPort->TxHeight,
	    TextRPort->cp_y);
	}
	break;
      case CHAR_SHIFTRIGHT:
	if (cursor < end)
	{
	  Move(TextRPort,TextRPort->cp_x+(TextRPort->TxWidth*(end-cursor)),
	    TextRPort->cp_y);
	  cursor = end;
	}
	break;
      case CHAR_UP:
	if ((hpos < HISTORY_SIZE-1) && (History[hpos+1]))
	  cursor = end = UseHistory(buffer,++hpos,left);
	break;
      case CHAR_DOWN:
	if ((hpos > 0) && (History[hpos-1]))
	  cursor = end = UseHistory(buffer,--hpos,left);
	else
	  cursor = end = UseHistory(buffer,-1,left);
	break;
      default:
	if ((end < len-2) && (isprint((unsigned char)c) != 0) &&
	  ( c < 128) && (TextRPort->cp_x+(TextRPort->TxWidth*2) <
	  TextWindow->Width-TextRight))
	{
	  memmove(buffer+cursor+1,buffer+cursor,end-cursor+1);
	  *(buffer+cursor++) = c;
	  ClipBlit(TextRPort,
	    TextRPort->cp_x,
	    TextRPort->cp_y-TextRPort->TxBaseline,
	    TextRPort,
	    TextRPort->cp_x+TextRPort->TxWidth,
	    TextRPort->cp_y-TextRPort->TxBaseline,
	    TextWindow->Width-TextRight-TextRPort->cp_x-TextRPort->TxWidth,
	    TextRPort->TxHeight,0xC0);
	  end++;
	  AmigaPutChar(c);
	  FlushText();
	}
	break;
    }
  }
}

int AmigaSave(char *buffer)
{
  if (AslRequestTags(FileReq,
    ASLFR_TitleText,"Save Game",
    ASLFR_DoSaveMode,TRUE,TAG_DONE))
  {
    strcpy(buffer,FileReq->fr_Drawer);
    AddPart(buffer,FileReq->fr_File,256);
    return TRUE;
  }
  return FALSE;
}

int AmigaLoad(char *buffer)
{
  if (AslRequestTags(FileReq,
    ASLFR_TitleText,"Load Game",
    ASLFR_DoSaveMode,FALSE,TAG_DONE))
  {
    strcpy(buffer,FileReq->fr_Drawer);
    AddPart(buffer,FileReq->fr_File,256);
    return TRUE;
  }
  return FALSE;
}

BOOL FlushText()
{
char *ptr;
int line,i;

  if (LinePos > 0)
  {
    for (i = 0; i < IGNORE_MAX; i++)
    {
      if (strncmp(LineBuffer,IgnoreStr[i],strlen(IgnoreStr[i])) == 0)
      {
	LinePos = 0;
	return TRUE;
      }
    }
    ptr = LineBuffer;
    line = (TextWindow->Width-TextBorder)/TextRPort->TxWidth;
    if (LinePos > line)
    {
      while (LinePos > line)
      {
	Text(TextRPort,ptr,line);
	ptr += line;
	LinePos -= line;
	line = (TextWindow->Width-TextBorder)/TextRPort->TxWidth;
	ScrollWindow();
      }
    }
    if (LinePos > 0) Text(TextRPort,ptr,LinePos);
    LinePos = 0;
  }
  return FALSE;
}

int GetCharacter(char c,char *buffer)
{
struct IntuiMessage *imsg;
ULONG class;
UWORD code, qualifier, xpos;

static struct EasyStruct req =
  { sizeof(struct EasyStruct),0,"Adventure",
    "Amiga Adventure 4+ (660 points) v1.1\n\n"
    "Adventure was originally written by Will Crowther\n"
    "and Don Woods. This 660 point version was written\n"
    "by Mike Arnautov, based on versions written by\n"
    "Peter Luckett, Jack Pike and David Platt. It has\n"
    "been ported to the Amiga by David Kinder.","Continue" };

  DrawCursor(TRUE,c);
  for (;;)
  {
    while (imsg = (struct IntuiMessage *)GetMsg(TextWindow->UserPort))
    {
      class = imsg->Class;
      code = imsg->Code;
      qualifier = imsg->Qualifier;
      ReplyMsg((struct Message *)imsg);
      switch (class)
      {
	case IDCMP_VANILLAKEY:
	  DrawCursor(FALSE,c);
	  return code;
	  break;
	case IDCMP_RAWKEY:
	  switch (code)
	  {
	    case 0x4C:
	      DrawCursor(FALSE,c);
	      return CHAR_UP;
	      break;
	    case 0x4D:
	      DrawCursor(FALSE,c);
	      return CHAR_DOWN;
	      break;
	    case 0x4E:
	      DrawCursor(FALSE,c);
	      return (qualifier & SHIFT) ? CHAR_SHIFTRIGHT : CHAR_RIGHT;
	      break;
	    case 0x4F:
	      DrawCursor(FALSE,c);
	      return (qualifier & SHIFT) ? CHAR_SHIFTLEFT : CHAR_LEFT;
	      break;
	  }
	  break;
	case IDCMP_CLOSEWINDOW:
	  QuitExit = TRUE;
	  AmigaExit(0);
	  break;
	case IDCMP_MENUPICK:
	  if (MENUNUM(code) == 0)
	  {
	    switch (ITEMNUM(code))
 	    {
	      case 0:
		EasyRequest(TextWindow,&req,NULL);
		break;
	      case 1:
		QuitExit = TRUE;
		AmigaExit(0);
		break;
	    }
	  }
	  break;
	case IDCMP_CHANGEWINDOW:
	  if ((GetTextHeight(0)/TextRPort->TxHeight) < TextHeight)
	  {
	    if (GetTextHeight(1)+TextWindow->BorderTop+TextRPort->TxBaseline
	      < TextRPort->cp_y)
	    {
	      xpos = TextRPort->cp_x;
	      SetAPen(TextRPort,0);
	      RectFill(TextRPort,
		TextLeft,
		TextWindow->BorderTop+GetTextHeight(1),
		TextWindow->Width-TextRight-1,
		TextWindow->Height-TextWindow->BorderBottom-1);
	      CursorBottomLeft();
	      if (buffer)
	      {
		SetAPen(TextRPort,1);
		AmigaPutChar('>');
		FlushText();
		SetAPen(TextRPort,2);
		AmigaPrintF("%s",buffer);
		FlushText();
	      }
	      SetAPen(TextRPort,2);
	      Move(TextRPort,xpos,TextRPort->cp_y);
	      DrawCursor(TRUE,c);
	    }
	  }
	  TextHeight = GetTextHeight(0)/TextRPort->TxHeight;
	  break;
      }
    }
    WaitPort(TextWindow->UserPort);
  }
}

void CursorLeft()
{
  Move(TextRPort,TextLeft+1,TextRPort->cp_y);
}

void CursorBottomLeft()
{
  Move(TextRPort,
       TextLeft+1,
       GetTextHeight(1)+TextWindow->BorderTop+TextRPort->TxBaseline);
}

void ScrollWindow()
{
  WaitTOF();
  ClipBlit(TextRPort,
	   TextLeft,
	   TextWindow->BorderTop+TextRPort->TxHeight,
	   TextRPort,
	   TextLeft,
	   TextWindow->BorderTop,
	   TextWindow->Width-TextLeft-TextRight,
	   GetTextHeight(1),
	   0xC0);
  SetAPen(TextRPort,0);
  RectFill(TextRPort,
	   TextLeft,
	   TextWindow->BorderTop+GetTextHeight(1),
	   TextWindow->Width-TextRight-1,
	   TextWindow->BorderTop+GetTextHeight(0)-1);
  SetAPen(TextRPort,1);
  CursorBottomLeft();
}

void DrawCursor(BOOL on, char c)
{
WORD xpos;
BYTE fgpen;

  if (c == 0) c = ' ';
  xpos = TextRPort->cp_x;
  fgpen = TextRPort->FgPen;
  SetAPen(TextRPort,on ? 1 : 2);
  SetBPen(TextRPort,on ? 3 : 0);
  Text(TextRPort,&c,1);
  SetAPen(TextRPort,fgpen);
  SetBPen(TextRPort,0);
  Move(TextRPort,xpos,TextRPort->cp_y);

  if (on)
    WindowLimits(TextWindow,
      MAX(xpos+TextRPort->TxWidth+TextRight+1,MinWidth),MinHeight,-1,-1);
  else
    WindowLimits(TextWindow,TextWindow->Width,TextWindow->Height,
      TextWindow->Width,TextWindow->Height);
}

void GetScreenSize(struct Screen *scr, ULONG *w, ULONG *h)
{
struct TagItem vti[] =
  { VTAG_VIEWPORTEXTRA_GET,NULL,
    VTAG_END_CM,NULL };
struct ViewPortExtra *vpe;

  *w = scr->Width;
  *h = scr->Height;
  if (scr->ViewPort.ColorMap)
  {
    if (VideoControl(scr->ViewPort.ColorMap,vti) == 0)
    {
      vpe = (struct ViewPortExtra *)vti[0].ti_Data;
      *w = vpe->DisplayClip.MaxX - vpe->DisplayClip.MinX + 1;
      *h = vpe->DisplayClip.MaxY - vpe->DisplayClip.MinY + 1;
    }
  }
}

void AddToHistory(char *buffer)
{
char *hist;
int i;

  if (strcmp(buffer,"\n") == 0) return;
  if ((hist = AllocVec(strlen(buffer),MEMF_CLEAR)) == NULL) return;
  strncpy(hist,buffer,strlen(buffer)-1);
  if (History[HISTORY_SIZE-1] != NULL) FreeVec(History[HISTORY_SIZE-1]);
  for (i = HISTORY_SIZE-1; i > 0; i--) History[i] = History[i-1];
  History[0] = hist;
}

int UseHistory(char *buffer, int hpos, int left)
{
  strcpy(buffer,(hpos > -1) ? History[hpos] : "");
  SetAPen(TextRPort,0);
  RectFill(TextRPort,
    left,
    TextRPort->cp_y-TextRPort->TxBaseline,
    TextWindow->Width-TextRight-1,
    TextRPort->cp_y-TextRPort->TxBaseline+TextRPort->TxHeight-1);
  SetAPen(TextRPort,2);
  Move(TextRPort,left,TextRPort->cp_y);
  Text(TextRPort,buffer,strlen(buffer));
  return strlen(buffer);
}

UWORD GetTextHeight(int sub)
{
  return
    (((TextWindow->Height-TextWindow->BorderTop-TextWindow->BorderBottom)
    /TextRPort->TxHeight)-sub)*TextRPort->TxHeight;
}
