/* Photon SlideShow, by Oren Peli.
	Copyright (c) 1988 BazboSoft! and (c) 1988 MicroIllusions. */

/* This program may be freely distributed and used for non-commercial
   purposes. Any changed versions of this program must retain the original
   copyright notices */

#include <libraries/dos.h>
#include <graphics/gfxbase.h>
#include <stdio.h>

void *OpenLibrary() , *OpenScreen() , *OpenWindow();
struct Window *MyOpenWindow();
LONG Write();

void *FindTask();

void *GetDiskObject() , *FindToolType();

void *Open() , *AllocMem();

#define MAXWIDTH	376
#define MAXHEIGHT	242
#define MAXCOLORS	32

#define MakeID(a , b , c , d) ( (a) << 24L | (b) << 16L | (c) << 8 | (d) )

#define ID_FORM MakeID('F' , 'O' , 'R' , 'M')
#define ID_ILBM MakeID('I' , 'L' , 'B' , 'M')
#define ID_BMHD MakeID('B' , 'M' , 'H' , 'D')
#define ID_CAMG MakeID('C' , 'A' , 'M' , 'G')
#define ID_CMAP MakeID('C' , 'M' , 'A' , 'P')
#define ID_BODY MakeID('B' , 'O' , 'D' , 'Y')

#define cmpByteRun1 1

#define ROUNDODDUP(a) ( ( (a) + 1) & (~1L) )

typedef struct
{
	LONG ckID , ckSize;
} Chunk;

typedef struct
{
	short w , h , x , y;
	char	nPlanes , masking , compression , pad1;
	short transparentColor;
	char	xAspect ,  yAspect;
	short pageWidth , pageHeight;
} BitMapHeader;

#define SafeRead(a , b , c) if (Read(a , b , c) == -1L) { Close(a); return(NULL); }

void *IntuitionBase;

struct GfxBase *GfxBase;

BitMapHeader bmhd;
char *bufstart;
Chunk header;

void *ReadILBM(fspec)
REGISTER char *fspec;
{
	REGISTER struct NewScreen NewScreen;
	REGISTER struct Screen *screen;
	REGISTER struct FileHandle *fp;
	REGISTER char colormap[MAXCOLORS][3] , *sourcebuf;
	REGISTER short colorcount;
	LONG id , ViewModes = 0L;

	if ( (fp = Open(fspec , MODE_OLDFILE) ) == 0L)
		return(NULL);

	SafeRead(fp , &header , (LONG) sizeof(header) );

	if (header.ckID != ID_FORM)
	{
		Close(fp);
		return(NULL);
	}

	SafeRead(fp , &id , (LONG) sizeof(id) );

	if (id != ID_ILBM)
	{
		Close(fp);
		return(NULL);
	}

	for (;;)
	{
		SafeRead(fp , &header , (LONG) sizeof(header) );

		if (header.ckID == ID_BODY)
			break;

		switch(header.ckID)
		{
			case ID_BMHD:	SafeRead(fp , &bmhd , (LONG) sizeof(bmhd) );
							break;

			case ID_CMAP:	SafeRead(fp , &colormap[0] [0] , (LONG) header.ckSize);
							colorcount = header.ckSize / 3;

							break;

			case ID_CAMG:	SafeRead(fp , &ViewModes , (LONG) header.ckSize);
							break;

			default:		Seek(fp , ROUNDODDUP(header.ckSize) , OFFSET_CURRENT);
		}
	}

	sourcebuf = bufstart = AllocMem( (LONG) header.ckSize , MEMF_PUBLIC);

	if (sourcebuf == 0L)
	{
		Close(fp);

		return(NULL);
	}

	SafeRead(fp , sourcebuf , (LONG) header.ckSize);
	Close(fp);

	NewScreen.LeftEdge = 0;
	NewScreen.TopEdge = GfxBase->NormalDisplayRows;
	NewScreen.Width = bmhd.w;
	NewScreen.Height = bmhd.h;
	NewScreen.Depth = bmhd.nPlanes;

	if (NOT (NewScreen.ViewModes = ViewModes) )
	{
		if (bmhd.w > MAXWIDTH)
			NewScreen.ViewModes |= HIRES;

		if (bmhd.h > MAXHEIGHT)
			NewScreen.ViewModes |= LACE;
	}

	if (NewScreen.ViewModes & LACE)
		NewScreen.TopEdge *= 2;

	NewScreen.Type = CUSTOMSCREEN | SCREENQUIET | SCREENBEHIND;
	NewScreen.Font = 0L;
	NewScreen.Gadgets = 0L;

	if (screen = OpenScreen(&NewScreen) )
	{
		if (NewScreen.ViewModes & HIRES)
			screen->LeftEdge += (640 - screen->Width) / 2;
		else
			screen->LeftEdge += (320 - screen->Width) / 2;

		if (NewScreen.ViewModes & LACE)
			screen->ViewPort.DyOffset += (400 - screen->Height) / 2;
		else
			screen->ViewPort.DyOffset += (200 - screen->Height) / 2;

		screen->ViewPort.DxOffset = screen->LeftEdge;

		MakeScreen(screen);

		RethinkDisplay();

		while (colorcount--)
			SetRGB4(&screen->ViewPort , (LONG) colorcount , colormap[colorcount][0] >>
					4L , colormap[colorcount][1] >> 4L , colormap[colorcount][2] >> 4L);
	}

	return(screen);
}

Expand(screen , bmhd , sourcebuf)
struct Screen *screen;
BitMapHeader *bmhd;
register char *sourcebuf;
{
	register char n , *destbuf;
	register short plane , linelen , rowbytes , i;

	linelen = bmhd->w / 8;

	for (i = 0 ; i < bmhd->h ; i++)
		for (plane = 0 ; plane < bmhd->nPlanes ; plane++)
		{
			destbuf = (char *) (screen->BitMap.Planes[plane]) + linelen * i;

			if (bmhd->compression == cmpByteRun1)
			{
				rowbytes = linelen;

				while (rowbytes)
				{
					n = *sourcebuf++;

					if (n >= 0)
					{
						CopyMem(sourcebuf , destbuf , (LONG) ++n);

						rowbytes -= n;

						destbuf += n;

						sourcebuf += n;
					}

					else
					{
						n = -n + 1;

						rowbytes -= n;

						setmem(destbuf , (WORD) n , (WORD) *sourcebuf++);

						destbuf += n;
					}

				}
			}

			else
			{
				CopyMem(sourcebuf , destbuf , (LONG) linelen);

				sourcebuf += linelen;

				destbuf += linelen;
			}
		}
}

USHORT dummydata[] = { 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 };

struct Window *
load_pic(name , otherwindow)
REGISTER BYTE *name;
REGISTER struct Window *otherwindow;
{
	REGISTER struct Screen *screen;
	REGISTER struct Window *window;
	REGISTER struct NewWindow nw;

	setmem(&nw , sizeof(nw) , 0);

	if (NOT (screen = ReadILBM(name) ) )
		return(NULL);

	nw.IDCMPFlags = VANILLAKEY;
	nw.Flags = SIMPLE_REFRESH | BORDERLESS | RMBTRAP | ACTIVATE;
	nw.Screen = screen;
	nw.Type = CUSTOMSCREEN;
	nw.Height = bmhd.h;
	nw.Width = bmhd.w;

	if (NOT (window = MyOpenWindow(&nw , otherwindow) ) )
	{
		CloseScreen(screen);

		return(NULL);
	}

	SetPointer(window , dummydata , 2L , 2L , NULL , NULL);

	Expand(screen , &bmhd , bufstart);

	FreeMem(bufstart , (LONG) header.ckSize);

	return(window);
}

static BYTE str_buf[1000];

extern WORD Enable_Abort = 0;

WORD abort = 0;

main(argc , argv)
REGISTER WORD argc;
REGISTER BYTE *argv[];
{
	REGISTER WORD i = 0 , cnt = 1;
	REGISTER struct Window *win[2];
	REGISTER struct Screen *scr[2];

	REGISTER FILE *fopen() , *fp;

	REGISTER BYTE file_name[500];

	REGISTER WORD method , arg1 , arg2 , wait;

	scr[0] = scr[1] = NULL;
	win[0] = win[1] = NULL;

	IntuitionBase = OpenLibrary("intuition.library" , 0L);
	GfxBase = OpenLibrary("graphics.library" , 0L);

	if (NOT GfxBase || NOT IntuitionBase)
		goto end;

	puts("\nPhoton SlideShow, by Oren Peli.\n\
Copyright (c) 1988 BazboSoft! and (c) 1988 MicroIllusions.\n");

	if (argc == 1)
		fp = fopen("script" , "r");

	else if (argc == 2)
		fp = fopen(argv[1] , "r");
	
	else
	{
		puts("Bad args.");

		goto end;
	}

	if (NOT fp)
	{
		puts("Can't open script file ... aborting!");
		goto end;
	}

forever:
	while (fgets(str_buf , 990 , fp) )
	{
		REGISTER WORD len = strlen(str_buf) - 1;
		REGISTER WORD t , p = 0;

		for (t = 0 ; t != len ; t++)
			if (str_buf[t] == ',')
			{
				str_buf[t] = '\0';
				
				p++;
			}

		if (p != 4)
		{
			puts("Bad script file: wrong number of arguments ... aborting!");

			goto end;
		}

		p = 0;

		strncpy(file_name , str_buf , 490);

		p += strlen(&str_buf[p]);

		p++;

		method = atoi(&str_buf[p]);

		p += strlen(&str_buf[p]);

		p++;

		arg1 = atoi(&str_buf[p]);

		p += strlen(&str_buf[p]);

		p++;

		arg2 = atoi(&str_buf[p]);

		p += strlen(&str_buf[p]);

		p++;

		wait = atoi(&str_buf[p]);

		i = 1 - i;

		if (win[i])
		{
			chk_abort(win[i]);

			CloseWindowSafely(win[i]);
			CloseScreen(scr[i]);
			
			win[i] = NULL;
			scr[i] = NULL;
		}
		
		if (abort)
			goto bye;

		if (NOT (win[i] = load_pic(file_name , win[1 - i] ) ) )
		{
			puts("Can't read file or not enough memory ... aborting!");

			goto end;
		}

		scr[i] = win[i]->WScreen;

		chk_abort(win[i]);

		if (abort)
			goto bye;

		show_screen(scr[i] , win[i] , method , arg1 , arg2);

		if (wait > 0)
		{
			for (t = 0 ; t <= wait ; t++)
			{
				if (chk_abort(win[i]) )
					break;
				
				if (abort)
					goto bye;

				Delay(50L);
			}
		}

		if (abort)
			goto bye;

	}

	rewind(fp);

	goto forever;

bye:
	fclose(fp);

	i = 1 - i;

end:

	if (win[i])
	{
		CloseWindowSafely(win[i]);
		CloseScreen(scr[i]);
		
		win[i] = NULL;
		scr[i] = NULL;
	}

	i = 1 - i;
		
	if (win[i])
	{
		CloseWindowSafely(win[i]);
		CloseScreen(scr[i]);
		
		win[i] = NULL;
		scr[i] = NULL;
	}

	if (IntuitionBase);
		CloseLibrary(IntuitionBase);

	if (GfxBase)
		CloseLibrary(GfxBase);

	exit(0);
}

chk_abort(win)
REGISTER struct Window *win;
{
	REGISTER struct IntuiMessage *msg , *GetMsg();

	REGISTER WORD code , was_pause = 0 , pausing = 0;

forever:

	if (msg = GetMsg(win->UserPort) )
	{
		code = msg->Code;
		
		ReplyMsg(msg);
		
		if (code == '' || code == 'q' || code == 'Q')
		{
			abort = 1;
			
			return(was_pause);
		}
		
		was_pause = 1;

		pausing = NOT pausing;

		if (pausing)
			WaitPort(win->UserPort);

		else
			return(was_pause);
	}

	else
		return(was_pause);

	goto forever;
}

show_screen(scr , win , method , arg1 , arg2)
REGISTER struct Screen *scr;
REGISTER struct Window *win;
REGISTER WORD method , arg1 , arg2;
{
	switch(method)
	{
		case 1:	scrollup(scr , win , arg1);
				break;

		default:
				scrollup(scr , win , -1);
				break;
	}
}

scrollup(scr , win , jump)
REGISTER struct Screen *scr;
REGISTER struct Window *win;
REGISTER WORD jump;
{
	REGISTER WORD i , is_lace = (NOT (NOT (scr->ViewPort.Modes & LACE) ) ) + 1;

	REGISTER WORD prev_pri = SetTaskPri(FindTask(NULL) , 9L);

	if (jump < 1)
		MoveScreen(scr , 0L , -9999L);

	ScreenToFront(scr);

	while (scr->TopEdge)
	{
		MoveScreen(scr , 0L , - (LONG) jump * is_lace);
		
		chk_abort(win);
		
		if (abort)
			return;
	}

	SetTaskPri(FindTask(NULL) , (LONG) prev_pri);
}


struct Window *
MyOpenWindow(newwin , otherwin)
REGISTER struct NewWindow *newwin;
REGISTER struct Window *otherwin;
{
	REGISTER struct Window *t_win;

	if (NOT otherwin)
		return(OpenWindow(newwin) );

	newwin->IDCMPFlags = NULL;

	if (NOT (t_win = OpenWindow(newwin) ) )
		return(NULL);

	t_win->UserPort = otherwin->UserPort;

	ModifyIDCMP(t_win , otherwin->IDCMPFlags);

	return(t_win);
}

CloseWindowSafely(p_wind)
REGISTER struct Window *p_wind;
{
	REGISTER struct IntuiMessage *msg , *succ;
	REGISTER struct Window *window = p_wind;
	REGISTER struct MsgPort *mp = (struct MsgPort *) window->UserPort;

	Forbid();

	msg = (struct IntuiMessage *) mp->mp_MsgList.lh_Head;

	while (succ = (struct IntuiMessage *) msg->ExecMessage.mn_Node.ln_Succ)
	{
		if (msg->IDCMPWindow == window)
		{
			Remove(msg);

			ReplyMsg(msg);
		}

		msg = succ;
	}

	window->UserPort = NULL;

	ModifyIDCMP(window , NULL);

	Permit();

	CloseWindow(window);
}
