/*
 * alock.c
 * based on several pieces of software (C) Commodore-Amiga Inc.
 *
 * written by Michael Kaiser
 *
 * This information is provided "as is"; no warranties are made.  All
 * use is at your own risk. No liability or responsibility is assumed.
 *
 * Compiles without warnings under SAS/C 6.0 :-)
 *
*/

#include <exec/types.h>
#include <exec/memory.h>
#include <exec/interrupts.h>
#include <devices/input.h>
#include <graphics/displayinfo.h>
#include <intuition/intuition.h>

#include <clib/alib_protos.h>
#include <clib/dos_protos.h>
#include <clib/exec_protos.h>
#include <clib/keymap_protos.h>
#include <clib/graphics_protos.h>
#include <clib/intuition_protos.h>
#include <math.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#include "alock.rev.h"
#include "abrush.h"
#include "alogo.h"

/* This is the function will grab all the inputs and the common pointer */

extern void Grab();
struct InputEvent *eventbuf = 0;

/* SAS/C & stuff */

extern struct IntuitionBase *IntuitionBase;

/* Password stuff */

static char NameString[] = "AmigaLock";
static char password[128] = "alock";
static int  passlen = 5;
static char passbuf[128] = "";
static int  buflen = 0;

/* A collection of routines that might be processed while the screen is locked. */

/* ------------- This shows the standard (internal) picture ------------- */

void Lock_ShowScreen(void) 

{
  struct Screen *S;
  int    done;
  char   buffer[32];
  register int i;
  int counter;
  
  if (IntuitionBase=(struct IntuitionBase *)OpenLibrary("intuition.library",37L))
    {
      if (S = OpenScreenTags(0L,
			     SA_Depth,4,
			     SA_DisplayID,HIRESLACE_KEY|DEFAULT_MONITOR_ID,
			     SA_Type,CUSTOMSCREEN,
			     SA_ShowTitle,FALSE,
			     SA_Quiet,TRUE,
			     TAG_DONE))
	{
	  
	  
	  for (i = 0; i < 16; i++)
	    {
	      SetRGB4(&S->ViewPort,(LONG)i,
		      (big_cmap[i] & 0x0F00) >> 8,
		      (big_cmap[i] & 0x00F0) >> 4,
		      (big_cmap[i] & 0x000F));
	    }
	  
	  DrawImage(&S->RastPort,&big_img,(S->Width-big_img.Width)/2,(S->Height-big_img.Height)/2);
	  
	  counter = 0;
	  done = 0;
	  while (!done)
	    {
	      if (eventbuf)
		{
		  MapRawKey(eventbuf,buffer,31,0);
		  passbuf[buflen] = buffer[0]; buflen ++; passbuf[buflen] = 0;
		  if (strncmp(passbuf,password,buflen) == 0)
		    {
		      if (strncmp(passbuf,password,passlen) == 0)
			done = 1;
		    }
		  else
		    {
		      passbuf[0] = buffer[0]; 
		      buflen = 1; 
		      passbuf[1] = 0;
		    }
		  eventbuf = 0;
		  Delay(3);
		  eventbuf = 0;
		}
	      else
		Delay(3);

	      counter ++;
	      for (i = 8; i < 16; i++)
		{
		  SetRGB4(&S->ViewPort,(LONG)i,
			  (big_cmap[(i+counter) & 0x0f] & 0x0F00) >> 8,
			  (big_cmap[(i+counter) & 0x0f] & 0x00F0) >> 4,
			  (big_cmap[(i+counter) & 0x0f] & 0x000F));
		}
	      if (counter >= 16)
		counter = 0;
	    }
	  CloseScreen(S);
	}
      CloseLibrary((struct Library *)IntuitionBase);
    }
}

/* -------------------------------- Game of life with C= logos ------------------------------- */

#define BOARD_X   12
#define BOARD_Y   10
#define SET_LIMIT 0.9

static int life_neighbours(int *board, int x, int y)

{
  register int i,j,k;

  k = 0;
  for (i = -1; i < 2; i++)
    for (j = -1; j < 2; j++)
      if ((x + i >= 0) && (x + i < BOARD_X) &&
	  (y + j >= 0) && (y + j < BOARD_Y))
	k += board[x+i + BOARD_Y * (y+j)];

  return(k);
}


static void life_run(int *old_board, int *new_board)

{
  register int i,j,k;

  for (i = 0; i < BOARD_X; i++)
    for (j = 0; j < BOARD_Y; j++)
      {
	k = life_neighbours(old_board,i,j);
	if (k <= 1)
	  new_board[i + BOARD_Y * j] = old_board[i + BOARD_Y * j];
	else
	  if (k <= 2)
	    new_board[i + BOARD_Y * j] = 1;
	  else
	    new_board[i + BOARD_Y * j] = 0;
      }
}

static void life_display(struct Screen *screen, 
			 struct Image *pos_img, 
			 struct Image *neg_img, 
			 int *board)

{
  int x,y;

  for (x = 0; x < BOARD_X; x++)
    for (y = 0; y < BOARD_Y; y++)
      if (board[x + BOARD_Y * y])
	DrawImage(&screen->RastPort,pos_img,
		  x * pos_img->Width,
		  y * pos_img->Height);
      else
	DrawImage(&screen->RastPort,neg_img,
		  x * neg_img->Width,
		  y * neg_img->Height);
}


void Lock_ShowLife(void) 

{
  struct Screen *S;
  int    done;

  char   buffer[32];
  register int i;
  int counter;

  int old_board[BOARD_X * BOARD_Y];
  int new_board[BOARD_X * BOARD_Y];

  counter = 0;
  for (i = 0; i < BOARD_X * BOARD_Y; i++)
    {
      old_board[i] = 0;
      if (drand48() > SET_LIMIT)
	{
	  old_board[i] = 1;
	  counter ++;
	}
    }
  
  while (counter < (BOARD_X * BOARD_Y)/16)
    {
      for (i = 0; i < BOARD_X * BOARD_Y; i++)
	{
	  if (old_board[i])
	    if (drand48() > SET_LIMIT)
	      {
		old_board[i] = 1;
		counter ++;
	      }
	}
    }
  
  if (IntuitionBase=(struct IntuitionBase *)OpenLibrary("intuition.library",37L))
    {
      if (S = OpenScreenTags(0L,
			     SA_Depth,2,
			     SA_DisplayID,HIRESLACE_KEY|DEFAULT_MONITOR_ID,
			     SA_Type,CUSTOMSCREEN,
			     SA_ShowTitle,FALSE,
			     SA_Quiet,TRUE,
			     TAG_DONE))
	{
	  
	  
	  for (i = 0; i < 4; i++)
	    {
	      SetRGB4(&S->ViewPort,(LONG)i,
		      (pos_cmap[i] & 0x0F00) >> 8,
		      (pos_cmap[i] & 0x00F0) >> 4,
		      (pos_cmap[i] & 0x000F));
	    }
	  
	  life_display(S,&pos_img,&neg_img,old_board);

	  counter = 0;
	  done = 0;
	  while (!done)
	    {
	      if (eventbuf)
		{
		  MapRawKey(eventbuf,buffer,31,0);
		  passbuf[buflen] = buffer[0]; buflen ++; passbuf[buflen] = 0;
		  if (strncmp(passbuf,password,buflen) == 0)
		    {
		      if (strncmp(passbuf,password,passlen) == 0)
			done = 1;
		    }
		  else
		    {
		      passbuf[0] = buffer[0]; 
		      buflen = 1; 
		      passbuf[1] = 0;
		    }
		  eventbuf = 0;
		  Delay(3);
		  eventbuf = 0;
		}
	      else
		Delay(3);

	      counter ++;
	      if ((counter % 20) == 0)
		{
		  life_run(old_board,new_board);
		  for (i = 0; i < BOARD_X * BOARD_Y; i++)
		    old_board[i] = new_board[i];
		  life_display(S,&pos_img,&neg_img,old_board);
		  counter = 0;
		}
	    }
	  CloseScreen(S);
	}
      CloseLibrary((struct Library *)IntuitionBase);
    }
}

/* ------------------------------- Load a picture & display it --------------------------------- */

void Lock_DoAction(char *actionname)

{
  int    done;
  char   buffer[32];
 
  done = system(actionname);

  while (!done)
    {
      if (eventbuf)
	{
	  MapRawKey(eventbuf,buffer,31,0);
	  passbuf[buflen] = buffer[0]; buflen ++; passbuf[buflen] = 0;
	  if (strncmp(passbuf,password,buflen) == 0)
	    {
	      if (strncmp(passbuf,password,passlen) == 0)
		done = 1;
	    }
	  else
	    {
	      passbuf[0] = buffer[0]; 
	      buflen = 1; 
	      passbuf[1] = 0;
	    }
	  eventbuf = 0;
	  Delay(3);
	  eventbuf = 0;
	}
      else
	Delay(3);
    }
}

/* The main procedure */

int main(int argc, char **argv)

{
  char            **ttypes;
  char             *param;
  char              actionname[512] = " ";
  struct IOStdReq  *inputReqBlk;
  struct MsgPort   *inputPort;
  struct Interrupt *inputHandler;
  int               result = 1;  /* bad result */
  int               mode   = 0;  /* INTERNAL   */

  if (argc > 0)
    {
      printf("%s (%s) by Michael Kaiser.\n",VERS,DATE);
      printf("Usage: %s PASSWORD=<password> ACTION=INTERNAL|LIFE|<command string>\n",argv[0]);
    }

  ttypes = ArgArrayInit( argc, argv );
  if (ttypes)
    {
      param = ArgString(ttypes,"PASSWORD","alock");
      if (param)
	strcpy(password,param);
      
      param = ArgString(ttypes,"ACTION","INTERNAL");
      if (param)
	{
	  if (strcmp(param,"INTERNAL") == 0)
	    mode = 0;
	  else
	    if (strcmp(param,"LIFE") == 0)
	      mode = 1;
	    else
	      {
		strcpy(actionname,param);
		mode = 2;
	      }
	}
    }

  ArgArrayDone();

  if (inputPort=CreatePort(NULL,NULL))
    {
      if (inputHandler=AllocMem(sizeof(struct Interrupt),
				MEMF_PUBLIC|MEMF_CLEAR))
        {
	  if (inputReqBlk=(struct IOStdReq *)CreateExtIO(inputPort,
							 sizeof(struct IOStdReq)))
            {
	      if (!OpenDevice("input.device",NULL,
			      (struct IORequest *)inputReqBlk,NULL))
                {
		  inputHandler->is_Code=Grab;
		  inputHandler->is_Data=NULL;
		  inputHandler->is_Node.ln_Pri=100;
		  inputHandler->is_Node.ln_Name=NameString;
		  inputReqBlk->io_Data=(APTR)inputHandler;
		  inputReqBlk->io_Command=IND_ADDHANDLER;
		  DoIO((struct IORequest *)inputReqBlk);
		  
		  result = 0;
		  
		  switch(mode) {
		  case 0:
		    Lock_ShowScreen();
		    break;

		  case 1:
		    Lock_ShowLife();
		    break;

		  case 2:
		    Lock_DoAction(actionname);
		    break;

		  default:
		    break;
		  }

		  inputReqBlk->io_Data=(APTR)inputHandler;
		  inputReqBlk->io_Command=IND_REMHANDLER;
		  DoIO((struct IORequest *)inputReqBlk);
		  
		  CloseDevice((struct IORequest *)inputReqBlk);
		  if (IntuitionBase=(struct IntuitionBase *)OpenLibrary("intuition.library",0L))
		    {
		      DisplayBeep(0L);
		      CloseLibrary((struct Library*)IntuitionBase);
		    }
                }
	      else
                printf("Error: Could not open input.device\n");
	      
	      DeleteExtIO((struct IORequest *)inputReqBlk);
            }
	  else
            printf("Error: Could not create I/O request\n");
	  
	  FreeMem(inputHandler,sizeof(struct Interrupt));
        }
      else
        printf("Error: Could not allocate interrupt struct memory\n");
      
      DeletePort(inputPort);
    }
  else
    printf("Error: Could not create message port\n");
  
  return(result);
}
