#include <stdlib.h>
#include <stdio.h>
#include <strings.h>

#include <exec/types.h>
#include <exec/memory.h>
#include <dos/dos.h>
#include <dos/rdargs.h>
#include <egs/egsutil.h>
#include <egs/ownall.h>

#include <proto/exec.h>
#include <proto/dos.h>
#include <egs/proto/egsutil.h>
#include "ArtEffect:Developer/picotalk.h"

const char ver[] = {"$VER: Mosaic 1.1 (21.01.95)"};

struct Library *EGBBase;
struct Library *EGSUtilBase;
struct Library *EGSIntuiBase;

struct EI_Window *window;
struct MsgPort *myport;

enum gadgets { GAD_WSIZE=100, GAD_HSIZE, GAD_START };

BOOL SendMsg(struct Message *msg)
{
	struct MsgPort *port;
	BOOL success;

	msg->mn_Node.ln_Type = NT_MESSAGE;
	msg->mn_ReplyPort = myport;
	msg->mn_Length = sizeof(struct PicoMsg);

	Forbid();
	if (port = FindPort("PicoTalk")){
		PutMsg(port, msg);
		WaitPort(myport);
		msg=GetMsg(myport);
		success = TRUE;
	} else
		success = FALSE;

	Permit();
	return success;
}

int ExecuteOp(struct PicoMsg *msg, int wval, int hval)
{
	struct E_EBitMap *bitmap;
	ULONG winid;

	msg->command = PTALK_OBTAIN_CURRENT;
	msg->arg.winid = 0;
	msg->res.alloc.bitmap = NULL;
	SendMsg((struct Message *)msg);
	winid = msg->res.alloc.winid;
	bitmap = msg->res.alloc.bitmap;
	if (bitmap){
		int x,y,sx,sy;
		int xval,yval;

		for (y=0; y<bitmap->Height; y+=hval){
			for (x=0; x<bitmap->Width; x+=wval){
				ULONG red,green,blue;
				UBYTE *data;

				red=green=blue=0;
				xval = ((x+wval)<bitmap->Width) ? wval : (bitmap->Width-x);
				yval = ((y+hval)<bitmap->Height) ? hval : (bitmap->Height-y);

				// evaluate color
				for (sy=0; sy<yval; sy++){
					data = bitmap->Typekey.PixelMap.Planes.Dest;
					data += x*4 + (y+sy)*bitmap->BytesPerRow;

					for (sx=0; sx<xval; sx++){
						red += *data++;
						green += *data++;
						blue += *data;
						data += 2;
					}
				}
				red /= xval*yval; green /= xval*yval; blue /= xval*yval;

				// write color;
				for (sy=0; sy<yval; sy++){
					data = bitmap->Typekey.PixelMap.Planes.Dest;
					data += x*4 + (y+sy)*bitmap->BytesPerRow;

					for (sx=0; sx<xval; sx++){
						*data++ = red;
						*data++ = green;
						*data = blue;
						// skip alpha channel: alpha channel is also used for your calculations
						// you may also change the alpha channel, and afterwards your result will be mixed accordingly.
						// you define the visibility 255: your value, 0: old value
						data += 2;
					}
				}									
			}
			// allways has to be used (does the automatic alpha channel support)
			msg->command = PTALK_UPDATE_WINDOW;
			msg->arg.update.winid = winid;
			msg->arg.update.left = 0;
			msg->arg.update.top = y;
			msg->arg.update.width = bitmap->Width;
			msg->arg.update.height = yval;
			SendMsg((struct Message *)msg);
		}
		msg->command = PTALK_RELEASE_WINDOW;
		msg->arg.winid = winid;
		SendMsg((struct Message *)msg);

		return 0;
	} else
		return -30;
}

main(int argc, char **argv)
{
	struct PicoMsg *msg;
	EB_GadContext con;
	struct EI_Screen *screen;
	ULONG appid;
	ULONG sigbits;
	BOOL loop=TRUE;

	EGBBase = OpenLibrary("egsgadbox.library",7);
	EGSUtilBase = OpenLibrary("egsutil.library",7);
	EGSIntuiBase = OpenLibrary("egsintui.library",7);
	myport=CreateMsgPort();
	msg=AllocVec(sizeof(struct PicoMsg), MEMF_PUBLIC|MEMF_CLEAR);

	if (msg && myport && EGSIntuiBase && EGSUtilBase && EGBBase){
		msg->command = PTALK_NOTIFY;
		msg->arg.notify.ApplicationPort = myport;
		msg->arg.notify.ApplicationName = "MOSAIC";
		if (SendMsg((struct Message *)msg)){
			appid = msg->res.notify.ApplicationId;
			screen = msg->res.notify.screen;

			if (con = EB_CreateGadContext(NULL,NULL,-1,-1)){
				if (!ET_CreateWindow(con,NULL,
					ET_CreateVertiBox(con,
						ET_CreateVertiTable(con,
							ET_CreateHorizBox(con,
								EB_CreateText(con,"Element-Width"),
								EB_NewMinWidth(ET_CreateValueGad(con,1,256,GAD_WSIZE,TAG_DONE),128),
								ET_DONE),
							ET_CreateHorizBox(con,
								EB_CreateText(con,"Element-Height"),
								EB_NewMinWidth(ET_CreateValueGad(con,1,256,GAD_HSIZE,TAG_DONE),128),
								ET_DONE),
							ET_DONE),
						EB_CreateVertiFill(con,EB_FILL_ALL,-1),
						EB_CreateTextAction(con, "_Modify Picture", GAD_START, EB_FILL_ALL),
						ET_DONE),
					ETW_SysGadgets, EI_WINDOWCLOSE|EI_WINDOWFRONT|EI_WINDOWDRAG,
					ETW_Title, "Mosaic",
					ETW_Flags, EI_WINDOWACTIVE|EI_SIZEBRIGHT|EI_SMART_REFRESH|EI_WINDOWCENTER,
					ETW_IDCMP, EI_iGADGETDOWN|EI_iCLOSEWINDOW,
					ETW_AutoResize, TRUE,
					ETW_PubScreen, screen,
					TAG_DONE)){

					int wval,hval;

					window = EB_OpenGadWindow(con);
					ET_ModifyValueGad(window,(struct ET_ValueGad *)EB_GCFindGadget(con,GAD_WSIZE),2);
					ET_ModifyValueGad(window,(struct ET_ValueGad *)EB_GCFindGadget(con,GAD_HSIZE),2);
					wval = hval = 2;

					sigbits = (1<<window->UserPort->mp_SigBit)|(1<<myport->mp_SigBit);
					do {
						union {
							struct Message *msg;
							struct EI_EIntuiMsg *intui;
						} execmsg;

						Wait(sigbits);

						{ struct PicoMsg *msg2;
						while (msg2 = (struct PicoMsg *)GetMsg(myport)){
							switch (msg2->command){
							case PTALK_APPLICATION_QUIT:
								loop=FALSE;
								break;

							case PTALK_APPLICATION_CMD:
								{
								struct RDArgs *rdargs;
								long *size[2];

								// parse rexx command
								if (rdargs = AllocDosObject(DOS_RDARGS, NULL)){
									rdargs->RDA_Source.CS_Buffer = msg2->arg.cmd.CommandString;
									rdargs->RDA_Source.CS_Length = strlen(msg2->arg.cmd.CommandString);
									rdargs->RDA_Source.CS_CurChr = 0;
									rdargs->RDA_DAList = NULL;
									rdargs->RDA_Buffer = NULL;
									rdargs->RDA_Flags = RDAF_NOPROMPT;

									if (ReadArgs("XSIZE/N/A,YSIZE/N/A", (long *)&size, rdargs)){
										if ((*size[0] >= 1) && (*size[0] <= 256) && (*size[1] >= 1) && (*size[1] <= 256))
											msg2->res.cmd.error = ExecuteOp(msg, *size[0], *size[1]);
										else
											msg2->res.cmd.error = -30;
									} else
										msg2->res.cmd.error = -20;

									FreeArgs( rdargs );
									FreeDosObject(DOS_RDARGS, rdargs);
								}
								}
								break;
							}
							ReplyMsg(&msg2->msg);
						}
						}
						while (execmsg.msg = GetMsg(window->UserPort)){
							switch (execmsg.intui->Class){
							case EI_iGADGETDOWN:
								switch (((struct EI_Gadget *)execmsg.intui->IAddress)->GadgetID){
								case GAD_WSIZE:
									wval = ((struct ET_ValueGad *)execmsg.intui->IAddress)->Value;
									break;
								case GAD_HSIZE:
									hval = ((struct ET_ValueGad *)execmsg.intui->IAddress)->Value;
									break;
								case GAD_START:
									ExecuteOp(msg, wval,hval);
									break;
								}
								break;
							case EI_iCLOSEWINDOW:
								loop=FALSE;
								break;
							}
							ReplyMsg(execmsg.msg);
						}
					} while (loop);
					EB_CloseGadWindow(con);
				}
				EB_DeleteGadContext(con);
			}
			msg->command = PTALK_LEAVE;
			msg->arg.leave.ApplicationId = appid;
			SendMsg((struct Message *)msg);
		}
	}
	if (msg) FreeVec(msg);
	if (myport) DeleteMsgPort(myport);
	if (EGBBase) CloseLibrary(EGBBase);
	if (EGSUtilBase) CloseLibrary(EGSUtilBase);
	if (EGSIntuiBase) CloseLibrary(EGSIntuiBase);
}
