#include <functions.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>
#include <libraries/asl.h>
#include <libraries/amigaguide.h>

#define Reihenlaenge 40
#define KartenBreite 53
#define KartenHohe   70
#define MaxZuege    500

extern UWORD *Rahmen[],*Pointer1[], *Pointer2[];
extern APTR VisualInfo;
extern struct Screen   *Scr;
extern struct Window   *Wnd;
extern struct Menu     *Menus;
extern struct TextAttr  topaz8;

long seconds,micros,sec2,mic2,MRold,MLold;
long CheatLevel,Punkte,UndoCounter,HiOff = 0,CardOverlay = 13;
BOOL MoveFlag,MBFlag,AGFlag = TRUE,VerboseFlag = TRUE,done = FALSE;
BYTE UndoStack[MaxZuege][3];
BYTE Stack[105],st[105];
BYTE PlayStack[Reihenlaenge][10];
BYTE EndStack[8],f[4];
char FileNamen[40],DirNamen[80],FileTemp[100] = "";
FILE *Datei;

struct Library  *IntuitionBase  = 0;
struct Library  *GadToolsBase   = 0;
struct Library  *GfxBase        = 0;
struct Library  *AslBase        = 0;
struct Library  *AmigaGuideBase = 0;

struct IntuiMessage  *message;
struct MsgPort       *UserPort;
struct RastPort      *RastPort;
struct Image Image = {1,1,51,66,3,NULL,7,7,NULL};
struct Image ImRah = {0,0,53,68,1,(UWORD*)&Rahmen,1,0,NULL};
struct IntuiText Text1 = {1, 0, JAM2, 0, 0, NULL, NULL, NULL};
struct NewAmigaGuide nag = {0,(UBYTE*)"Arachnid.hilfe",0,0,0,0,0,0,0,0,0,0,0};

struct EasyStruct About = {sizeof(struct EasyStruct), 0, "Autor",
        "Arachnid V1.1\nFrank Nießen, © 1993\nIdee: Ian Heath 1991 (Arachnid V1.2)", "OK"};
struct EasyStruct EndeReq = {sizeof(struct EasyStruct), 0, "Spielende",
        "Sie haben die Patience erfolgreich gelöst.\n"
        "Wollen Sie nochmal oder das Programm beenden ?", "Nochmal|Ende"};
struct EasyStruct SicherReq = {sizeof(struct EasyStruct), 0, NULL,
        "%s\nSind sie sicher ?", "Ja|Nein"};
struct EasyStruct LoadErr = {sizeof(struct EasyStruct), 0, NULL,
        "Fehler beim %s aufgetreten!\n%s", "OK"};
struct EasyStruct DoNot = {sizeof(struct EasyStruct), 0, NULL,
        "nicht Implementiert", "OK"};

void CloseArachnidWindow();
void CloseDownScreen();
void CloseSCWindow();
void CloseWin();
long Deal_Row();
void FehlerAnz(long nr);
void FehlerAus(long nr);
void Hilfe();
void KartenAnzeigen();
void KartenAusteilen();
void KartenMischen();
void MoveCard(long MR1,long ML1,long MR2,long ML2,BOOL dk);
long OpenArachnidWindow();
long OpenSCWindow();
long OpenWin();
void ReiheZeigen();
long SetupScreen();
void SucheKarte();
void Undo();

long OpenLib(void)
{
  if(!(IntuitionBase = (struct Library*)OpenLibrary("intuition.library",37)))
    return(1);
  if(!(GfxBase = (struct Library*)OpenLibrary("graphics.library",37)))
    return(2);
  if(!(GadToolsBase = (struct Library*)OpenLibrary("gadtools.library",37)))
    return(3);
  if(!(AslBase = (struct Library*)OpenLibrary("asl.library",37)))
    return(4);
  if(!(AmigaGuideBase = (struct Library*)OpenLibrary("amigaguide.library",34)))
    AGFlag = FALSE;
  return(0);
}

void CleanUp()
{
  CloseWin();
  CloseDownScreen();
  if(AmigaGuideBase) CloseLibrary(AmigaGuideBase);
  CloseLibrary(AslBase);
  CloseLibrary(GadToolsBase);
  CloseLibrary(GfxBase);
  CloseLibrary(IntuitionBase);
}

void Load()
{
  long i,j;
  char tempstr[10];
  union {long zahl;char z[4];} temp;
  struct FileRequester *fr;

  if(!(fr = (struct FileRequester *)AllocAslRequestTags(ASL_FileRequest,
                           ASL_Hail,      "Spiel laden",
                           ASL_Dir,       DirNamen,
                           ASL_File,      FileNamen,
                           ASL_Pattern,   "#?.game",
                         /*ASL_FuncFlags, FILF_PATGAD,*/
                           ASL_Window,    Wnd,
                           TAG_DONE)))
  {
    FehlerAus(10);
    return;
  }

  if(!AslRequest(fr, NULL))
  {
    FreeAslRequest(fr);      /* Cancel */
    return;
  }
  strcpy(DirNamen, fr->rf_Dir);
  strcpy(FileNamen, fr->rf_File);
  FreeAslRequest(fr);

  strcpy(FileTemp, DirNamen);
  AddPart((UBYTE *)FileTemp, (UBYTE *)FileNamen, 100);

  if(!(Datei = fopen(FileTemp, "rb")))
  {
    EasyRequest(Wnd, &LoadErr, NULL, "Laden","Dateiname ist falsch.");
    return;
  }

  fgets(tempstr, 9, Datei);
  if(strcmp("Arachnid",tempstr))
  {
    EasyRequest(Wnd, &LoadErr, NULL, "Laden","Datei ist keine Arachniddatei.");
    return;
  }

  Stack[0] = fgetc(Datei);
  for(i=1;i<=Stack[0];i++) Stack[i] = fgetc(Datei);

  for(j=0;j<10;j++)
  {
    PlayStack[0][j] = fgetc(Datei);
    for(i=1;i<=PlayStack[0][j];i++) PlayStack[i][j] = fgetc(Datei);
  }

  for(i=0;i<8;i++) EndStack[i] = fgetc(Datei);
  for(i=0;i<105;i++) st[i] = fgetc(Datei);
  for(i=0;i<4;i++) f[i] = fgetc(Datei);

  for(i=0;i<4;i++) temp.z[i] = fgetc(Datei);   /* 4 char -> 1 long */
  UndoCounter = temp.zahl;

  for(i=0;i<UndoCounter;i++)
  {
    UndoStack[i][0] = fgetc(Datei);
    UndoStack[i][1] = fgetc(Datei);
    UndoStack[i][2] = fgetc(Datei);
  }

  for(i=0;i<4;i++) temp.z[i] = fgetc(Datei);   /* 4 char -> 1 long */
  Punkte = temp.zahl;

  fclose(Datei);

  if(UndoCounter) OnMenu(Wnd, 1);
  else OffMenu(Wnd, 1);

  KartenAnzeigen();
}

long SaveAs()
{
  char *c;
  struct FileRequester *fr;

  if(!(fr = (struct FileRequester *)AllocAslRequestTags(ASL_FileRequest,
                           ASL_Hail,      "Spiel speichern",
                           ASL_Dir,       DirNamen,
                           ASL_File,      FileNamen,
                           ASL_Pattern,   "#?.game",
                           ASL_FuncFlags, FILF_SAVE,
                           ASL_Window,    Wnd,
                           TAG_DONE)))
  {
    FehlerAus(10);
    return(10);
  }

  if(!AslRequest(fr, NULL))
  {
    FreeAslRequest(fr);      /* Cancel */
    return(-1);
  }
  strcpy(DirNamen, fr->rf_Dir);
  strcpy(FileNamen, fr->rf_File);
  FreeAslRequest(fr);

  if(c=strrchr(FileNamen, '.')) *c = 0;
  strcat(FileNamen, ".game");

  strcpy(FileTemp, DirNamen);
  AddPart((UBYTE *)FileTemp, (UBYTE *)FileNamen, 100);

  return(0);
}

void Save()
{
  long i,j;
  char anz;
  union {long zahl;char z[4];} temp;

  if(!strlen(FileTemp)) if(SaveAs()) return;

  if(!(Datei = fopen(FileTemp, "wb")))
  {
    EasyRequest(Wnd, &LoadErr, NULL, "Speichern","Datei läßt sich nicht öffnen.");
    return;
  }

  fputs("Arachnid", Datei);

  anz = Stack[0];
  putc(anz, Datei);
  for(i=1;i<=anz;i++) putc(Stack[i], Datei);

  for(j=0;j<10;j++)
  {
    anz = PlayStack[0][j];
    putc(anz, Datei);
    for(i=1;i<=anz;i++) putc(PlayStack[i][j], Datei);
  }

  for(i=0;i<8;i++) putc(EndStack[i], Datei);
  for(i=0;i<105;i++) putc(st[i], Datei);
  for(i=0;i<4;i++) putc(f[i], Datei);

  temp.zahl = UndoCounter;
  for(i=0;i<4;i++) putc(temp.z[i], Datei);

  for(i=0;i<UndoCounter;i++)
  {
    putc(UndoStack[i][0], Datei);
    putc(UndoStack[i][1], Datei);
    putc(UndoStack[i][2], Datei);
  }

  temp.zahl = Punkte;
  for(i=0;i<4;i++) putc(temp.z[i], Datei);

  fclose(Datei);
}

BOOL MenuAction(long select)
{
  long i,j,flags,menu,item,subit;

  menu = MENUNUM(select);
  item = ITEMNUM(select);
  subit = SUBNUM(select);
  ClearPointer(Wnd);
  switch(menu)
  {
    case 0:
      switch(item)
      {
        case 0: if(EasyRequest(Wnd, &SicherReq, NULL,
                    "Hiermit starten Sie ein neues Spiel!"))
                  KartenMischen();
                break;
        case 1: Load();
                break;
        case 2: SaveAs();
        case 3: Save();
                break;
        case 5: if(EasyRequest(Wnd, &SicherReq, NULL, "Programmende!"))
                  return(TRUE);
      } break;
    case 1:
      switch(item)
      {
        case 0: Undo();
                break;
        case 1: if(UndoCounter&&(EasyRequest(Wnd, &SicherReq, NULL,
                     "Alle Züge werden zurückgenommen!")))
                {
                  for(i=1;i<105;i++) Stack[i] = st[i];
                  KartenAusteilen();
                }
                break;
        case 3: FehlerAnz(Deal_Row());
                break;
      } break;
    case 2:
      switch(item)
      {
        case 0: ReiheZeigen();
                break;
        case 1: SucheKarte();
                break;
        case 3:
        case 4:
        case 5: break;
        case 6: EasyRequest(Wnd, &DoNot, NULL, NULL);
                break;
      } break;
    case 3:
      switch(item)
      {
        case 0: EasyRequest(Wnd, &About, NULL, NULL);
                break;
        case 1: Hilfe();
      }
  }
  MoveFlag = FALSE;
  seconds = 0;

  for(i=0;i<3;i++)
  {
    j = FULLMENUNUM(2, 4, i);
    flags = ((struct MenuItem *)ItemAddress(Menus, j))->Flags;
    if(flags & CHECKED) CheatLevel = i;
  }
  j = FULLMENUNUM(2, 5, 0);
  flags = ((struct MenuItem *)ItemAddress(Menus, j))->Flags;
  VerboseFlag = (flags & CHECKED);
  j = FULLMENUNUM(2, 3, 0);
  flags = ((struct MenuItem *)ItemAddress(Menus, j))->Flags;
  i = (flags & CHECKED) ? -80 : 0;
  if(i != HiOff)
  {
    HiOff = i;
    EraseRect(RastPort, 0, 0, 639, 511);
    KartenAnzeigen();
  }

  return(FALSE);
}

void ButtonAction(long code)
{
  long MR,ML;

  if((code != SELECTDOWN)&&(code != MIDDLEDOWN)) return;

  MR = message->MouseX;
  ML = message->MouseY;
  MR = ((MR % 62)<10) ? -1 : MR/62;
  if(MR>9) MR = -1;
  if(MR == -1) return;
  ML = (ML < 80+HiOff) ? -1 : (ML-80-HiOff)/CardOverlay;
  if(DoubleClick(seconds,micros,message->Seconds,message->Micros))
  {
    if(!((code == SELECTDOWN) == MBFlag)) return;
    ClearPointer(Wnd);
    MoveFlag = FALSE;
    seconds = 0;
    if(MRold == MR) MoveCard(MR,ML,MR,ML,TRUE);
    else            MoveCard(MRold,MLold,MR,ML,FALSE);
  }
  else
  {
    if(MoveFlag)
    {
      if((!(code == SELECTDOWN) == MBFlag)) return;
      ClearPointer(Wnd);
      seconds = 0;
      MoveCard(MRold,MLold,MR,ML,FALSE);
    }
    else
    {
      MBFlag = (code == SELECTDOWN);
      seconds = message->Seconds;
      micros = message->Micros;
      if(MBFlag) SetPointer(Wnd, (UWORD*)Pointer1, 11, 16, -1, 0);
      else       SetPointer(Wnd, (UWORD*)Pointer2, 11, 16, -1, 0);
    }
    MoveFlag = !MoveFlag;
  }
  MRold = MR;
  MLold = ML;
}

long main()
{
  long i;
  long class,code,select;
  time_t t1;
  BPTR lock;

  srand(time(&t1));

  /*************** Resourcen öffnen ***************/
  if(i = OpenLib())
  {
    CleanUp();
    FehlerAus(i);
    return(10);
  }

  if(i = SetupScreen())
  {
    CleanUp();
    FehlerAus(i);
    return(10);
  }
  nag.nag_Screen = Scr;

  if(i = OpenWin())
  {
    CleanUp();
    FehlerAus(i);
    return(10);
  }

  if(!(lock = Lock(nag.nag_Name, ACCESS_READ))) AGFlag = FALSE;
  else UnLock(lock);

  UserPort = Wnd->UserPort;
  RastPort = Wnd->RPort;

  KartenMischen();

  /*************** Benutzereingabe holen **********/
  while (!(done))
  {
    Wait(1L << UserPort->mp_SigBit);
    while(message = (struct IntuiMessage *) GT_GetIMsg(UserPort))
    {
      class = message->Class;
      code  = message->Code;
      switch(class)
      {
        case IDCMP_MENUPICK: for(select=code;
                                 select != MENUNULL;
                                 select = (ItemAddress(Menus,select))->NextSelect)
                               done = MenuAction(select);
                             break;
        case IDCMP_MOUSEBUTTONS: ButtonAction(code);
                                 break;
        case IDCMP_RAWKEY:
        case IDCMP_VANILLAKEY: if((code == 't')||(code == 'T'))
                               {
                                  ButtonAction(MIDDLEDOWN); /* dirty hack; replace MMB */
                                  break;
                               }
                               ClearPointer(Wnd);
                               if((code == 'u')||(code == 'U')) Undo();
                               if((code == 'o')||(code == 'O')) SucheKarte();
                               if((code == 'd')||(code == 'D')) ReiheZeigen();
                               if((code == 'h')||(code == 'H')||(code == 95)) Hilfe(); /* Help-taste */
                               MoveFlag = FALSE;
                               seconds = 0;
      }
      GT_ReplyIMsg(message);
    }
  }

  CleanUp();
  return(0);
}

#ifdef __MAXON__
#include <workbench/startup.h>

void wbmain(struct WBStartup *ws)
{
  BPTR oldlock;

  oldlock = CurrentDir(ws->sm_ArgList[0].wa_Lock);
  main();
  CurrentDir(oldlock);
}
#endif
