/*============================================================================*/
/*  MPaint.c: by Kevin T. Seghetti					      */
/*  Simple paint program, main module					      */
/*============================================================================*/

/*============================================================================*/
/*  includes								      */
/*============================================================================*/

#include "equates.h"
#include <functions.h>
#include <exec/types.h>
#include <intuition/intuition.h>

#include <graphics/gfx.h>
#include "iff/jiff.h"

/*============================================================================*/
/*  external declarations						      */
/*============================================================================*/

extern void HandleMenu(),DrawContLine(),(*DrawRoutineList[])();
extern long ForeColor,BackColor,OutlineColor,DrawMode;
extern FLAG noUndo;
extern char Crd[20];
extern struct IntuiText *CoordIntuiText;
extern struct Remember *MenuRemember;

/*============================================================================*/
/*  Global Variable declarations					      */
/*============================================================================*/

struct IntuitionBase *IntuitionBase;
struct GfxBase *GfxBase;

struct Screen *MPaintScreen = NULL;
struct Window *MPaintWindow = NULL,*ToolWindow = NULL,*PaletteWindow = NULL,*HistWindow = NULL,*CoordWindow = NULL;
struct RastPort *VRastPort = NULL;
struct Menu *MPaintMenu;
struct Remember *ToolRemember,*PaletteRemember,*HistRemember,*CoordRemember;
struct ILBM_info  *info;
struct RastPort UndoRastPort;
struct BitMap UndoBitMap;

int i,CurrWindow,GridX,GridY,MaxColor,PaletteColorMode,Function_DrawMode;
SHORT mx,my,PaintWidth,PaintHeight;
FLAG Continue,SelectDown,UndoBuffer = FALSE,PropGadgDown = FALSE,Grid = FALSE,ChoosingBackColor = FALSE,PatternOn = FALSE;
UBYTE *TempPlane = 0;
UWORD *ColorPTR,CurrGadg;
ULONG Class,Code,Qualifier;
void (*DrawRoutine)();
struct Gadget *GadgetPTR;
struct TmpRas myTmpRas;
struct Gadget *OldGadget = 0;
struct PropInfo *TempProp;
struct Gadget *TempGadget;

UWORD areabuffer[250];
struct AreaInfo myAreaInfo;

USHORT areaPattern[] =
{
    0xaaaa,
    0x5555
};

/*============================================================================*/
/*  default colors							      */
/*============================================================================*/

UWORD colortable[32] =
{
    0,0xfff,0xf00,0x0f0,0x00f,0xff0,0x0ff,0xf0f,
    0x8a8,0xa88,0x080,0x008,0x888,0x808,0x088,0x880,
    0xeee,0xddd,0xccc,0xbbb,0xaaa,0x999,0x888,0x777,
    0x666,0x555,0x444,0x333,0x222,0x111,0x000,0x00f
};

/*============================================================================*/
/*  Initial Screen Definition						      */
/*============================================================================*/

struct NewScreen NewMPaintScreen =
{
    0,0,320,200,5,	    /* leftedge, topedge, width, height, depth */
    0,1,		    /* detail and block pens */
    NULL,		   /* Viewmodes */
    CUSTOMSCREEN,	    /* screen type */
    NULL,		    /* font pointer */
    (UBYTE *)"MPaint Screen", /* screen title */
    NULL,		    /* pointer to screen gadgets */
    NULL		    /* custom bitmap pointer */
};

/*============================================================================*/
/*  Return palette settings to default					      */
/*============================================================================*/

RestorePalette()
{
    int i;
    if(ColorPTR)
	for(i=0;i<32;i++)
	    ColorPTR[i] = colortable[i];
}

/*============================================================================*/
/*  free a set of bitplanes						      */
/*============================================================================*/

void
myfree_planes(bmap)
register struct BitMap *bmap;
{
    PLANEPTR plane;
    long length;
    short i;

    length = bmap->BytesPerRow * bmap->Rows;

    for (i=0; i<bmap->Depth; i++)
	if ( (plane = bmap->Planes[i]) != NULL)
	 {
	    rfree(plane, length);
	    bmap->Planes[i] = NULL;
	 }
}

/*============================================================================*/
/*  cleanup: Program finished, time to free all memory and resources	      */
/*============================================================================*/

cleanup(text,err)
char *text;
int err;
{
    if(text)
	DoAutoRequest(MPaintWindow,text,NULL,"Ok");

    if(MPaintWindow)
     {
	ClearMenuStrip(MPaintWindow);
	CloseWindow(MPaintWindow);
     }

    if(ToolWindow)
	CloseToolWindow();

    if(PaletteWindow)
	ClosePaletteWindow();

    if(HistWindow)
	CloseHistWindow();

    if(CoordWindow)
	CloseCoordWindow();

    if(UndoBuffer)
     {
	myfree_planes( UndoRastPort.BitMap);
     }

    DeleteAllPages();

    CloseVScreen();

    if(GfxBase)
	CloseLibrary(GfxBase);
    if(IntuitionBase)
	CloseLibrary(IntuitionBase);

    if(MenuRemember)
	FreeRemember(&MenuRemember,(long)TRUE);

    if(TempPlane)
	FreeRaster(TempPlane,(long)PaintWidth,(long)PaintHeight);

    exit(err);
}

/*============================================================================*/
/*  Open all libraries we will use					      */
/*============================================================================*/

OpenLibrarys()
{
    IntuitionBase = (struct IntuitionBase *)OpenLibrary("intuition.library",REVISION);
    if(!IntuitionBase)
	cleanup("Cannot Open Intuition Library",ERR_NOLIB);

    GfxBase = (struct GfxBase *)OpenLibrary("graphics.library",REVISION);
    if(!GfxBase)
	cleanup("Cannot Open Graphics Library",ERR_NOLIB);
}

/*----------------------------------------------------------------------------*/

InitVScreen()
{
    MPaintScreen = OpenScreen(&NewMPaintScreen);
    if(!MPaintScreen)
	cleanup("Cannot open custom screen",5);
    MaxColor = 32;
    PaintWidth = 320;
    PaintHeight = 200;

    ColorPTR = AllocMem((long)sizeof(UWORD[32]),NULL);
    if(!ColorPTR)
	cleanup("not enough memory");
    RestorePalette();
    LoadRGB4(&MPaintScreen->ViewPort,ColorPTR,32L);
}

/*============================================================================*/

CloseVScreen()
{
    if(MPaintScreen)
	CloseScreen(MPaintScreen);

    if(ColorPTR)
	FreeMem(ColorPTR,(long)sizeof(UWORD[32]));
}

/*============================================================================*/

InitTempPlane()
{
    TempPlane = AllocRaster((long)PaintWidth,(long)PaintHeight);
    if(!TempPlane)
	cleanup("no memory for TmpRas",ERR_NOMEM);
    InitTmpRas(&myTmpRas,TempPlane,(long)(PaintWidth/8)*PaintHeight);
    VRastPort->TmpRas = &myTmpRas;

    InitArea(&myAreaInfo,areabuffer, 100L);
    VRastPort->AreaInfo=&myAreaInfo;
}

/*============================================================================*/

InitDefaults()
{
    ForeColor = 1;
    SetAPen(VRastPort,(long)ForeColor);
    DrawRoutine = DrawContLine;
    DrawMode = JAM2;
    Function_DrawMode = JAM2;
    SelectDown = FALSE;
    PaletteColorMode = COLORMODE_NORMAL;
    GridX = 10;
    GridY = 10;
}

/*============================================================================*/

InitUndoBuffer()
{
    long Depth;

    Depth = Num2Bit((long)MaxColor);

    InitRastPort(&UndoRastPort);
    UndoRastPort.Mask = (1<<Depth)-1;

    InitBitMap(&UndoBitMap,Depth,(long)PaintWidth,(long)PaintHeight);

    UndoRastPort.BitMap = &UndoBitMap;

    for (i=0; i<Depth; i++)
	if ((UndoRastPort.BitMap->Planes[i] = ralloc(PaintWidth*(PaintHeight/8))) == NULL)
	 {
	    myfree_planes( UndoRastPort.BitMap );
	    UndoBuffer = FALSE;
	    return();
	 }
    UndoBuffer = TRUE;
    noUndo = FALSE;
    SaveToUndoBuffer();
}

/*============================================================================*/
/*  put_ea_cmap: convert IFF colormap to Amiga colormap 		      */
/*============================================================================*/

/*put_ea_cmap given an ea-type color map:
		an array of unsigned chars of form ea_cmap[] = {r, g, b, r, g, b...}
  turn it into an amiga-type color map:
		an array of unsigned short of form amiga_cmap = {0xrgb, 0xrgb, ...}
  and then tell Dale this is the colors we want for our viewport */
void
put_ea_cmap(cmap, colors)
unsigned char *cmap;
int colors;
{
unsigned short amy_cmap[MAXCOL];
int i;
unsigned char r, g, b;

if (colors > MAXCOL)    /*color clipping*/
	colors = MAXCOL;
for (i=0; i<colors; i++)
	{
	amy_cmap[i] =
		((cmap[0] & 0xf0) << 4) + (cmap[1] & 0xf0) + ((cmap[2] & 0xf0) >> 4);
	cmap += 3;
	}
LoadRGB4( &MPaintScreen->ViewPort, amy_cmap, (long)colors);
for(i=0;i<colors;i++)
 {
    ColorPTR[i] = amy_cmap[i];
 }
}

/*============================================================================*/
/*  ConvertToDecimal: this is MUCH faster than using sprintf		      */
/*============================================================================*/

ConvertToDecimal(text,num)
char *text;
int num;
{
    int temp;

    temp = '0';
    while(num>99)
     {	num -=100;
	temp+=1;
     }
    *text++ = temp;

    temp = '0';
    while(num>9)
     {	num -=10;
	temp+=1;
     }
    *text++ = temp;
    *text = num + '0';
}

/*============================================================================*/
/*  UpdateCoordinates: Print the current mouse coordinates in coord window    */
/*============================================================================*/

UpdateCoordinates()
{
    if(CoordWindow)
     {
	ConvertToDecimal(Crd,MPaintScreen->MouseX);
	Crd[3] = ',';
	ConvertToDecimal(&Crd[4],MPaintScreen->MouseY);
	Crd[7] = 0;

	PrintIText(CoordWindow->RPort,CoordIntuiText,5L,12L);
     }
}

/*============================================================================*/
/*  Dispatch:all drawing operations are called from here		      */
/*============================================================================*/

Dispatch()
{
    if(Grid)
     {
	mx = (int)(mx/GridX) * GridX;
	my = (int)(my/GridY) * GridY;
     }
	    if(CoordWindow)
		UpdateCoordinates();

    if(Class == MOUSEBUTTONS && Code == SELECTDOWN)
	SaveToUndoBuffer();
    (*DrawRoutine)();
}

/*============================================================================*/
/*  converts a proportional value to a # between 0 and 15		      */
/*============================================================================*/

USHORT PotConvert16(value)
 USHORT value;
{   return(15 - (value/(0xFFFF/15)));
}

/*============================================================================*/
/*  converts a # between 0 & 15 to a proportional value 		      */
/*============================================================================*/

USHORT ConvertToPot16(value)
 USHORT value;
{   return((15 - value) * (0xffff/15));
}

/*============================================================================*/
/*  search a GList for a particular gadget				      */
/*============================================================================*/

struct Gadget *
FindGadget(GList,GadgetID)
struct Gadget *GList;
USHORT GadgetID;
{
    while(GList->GadgetID != GadgetID && GList->NextGadget != 0)
	GList = GList->NextGadget;
    return(GList);
}

/*============================================================================*/
/*  This is where all input events are parsed				      */
/*  menu choices are handled in "menuparse.c"                                 */
/*============================================================================*/

HandleInput(message)
struct IntuiMessage *message;
{
    UWORD TempColor,NewColor;
    struct StringInfo *TempString;

    if(message)
     {
	Class=message->Class;
	Code=message->Code;
	Qualifier=message->Qualifier;
	mx=message->MouseX;
	my=message->MouseY;
	GadgetPTR = (struct Gadget *)message->IAddress;
	ReplyMsg(message);
     }

    if(CoordWindow)
	UpdateCoordinates();

    switch(Class)
     {	case MOUSEMOVE:
	    switch(CurrWindow)
	     {
		case WINDOW_PAINT:
		    Dispatch();
		    break;
		case WINDOW_PALETTE:
		    if(PropGadgDown)
			UpdateColor();
		    break;
	     }
	    break;
	case MOUSEBUTTONS:
	    switch(Code)
	     {	case  SELECTDOWN:
		    SelectDown = TRUE;
		    Dispatch();
		    break;
		case SELECTUP:
		    SelectDown = FALSE;
		    Dispatch();
		    if(Function_DrawMode == DRAWMODE_CYCLE)
			UpdatePropGadgets();
		    break;
	     }
	    break;
	case MENUPICK:
	    HandleMenu(Code);
	    break;
	case CLOSEWINDOW:
	    if(CurrWindow == WINDOW_TOOLS)
		CloseToolWindow();
	    else if(CurrWindow == WINDOW_PALETTE)
		ClosePaletteWindow();
	    else if(CurrWindow == WINDOW_HIST)
		CloseHistWindow();
	    if(CurrWindow == WINDOW_COORD)
		CloseCoordWindow();
	    break;
	case GADGETDOWN:
	    if(CurrWindow == WINDOW_PALETTE)
		if(GadgetPTR->SpecialInfo)
		 {
		    TempProp = (struct PropInfo *)GadgetPTR->SpecialInfo;
		    CurrGadg = GadgetPTR->GadgetID;
		    PropGadgDown = TRUE;
		 }
	    break;
	case GADGETUP:
	    if(CurrWindow == WINDOW_TOOLS)
	     {
		if(GadgetPTR->GadgetID >= TOOL_GADG_START)
		    DrawRoutine = DrawRoutineList[GadgetPTR->GadgetID-TOOL_GADG_START];
		else if(GadgetPTR->GadgetID < 32 /*&& ToolWindow*/)
		 {  if(ChoosingBackColor)
		     {
			SetBackColor((long)GadgetPTR->GadgetID);
		     }
		    else
			SetForeColor((long)GadgetPTR->GadgetID);
		 }
		else if(GadgetPTR->GadgetID == TOOL_GADG_UNDO)
		    DoUndo();
		else if(GadgetPTR->GadgetID == TOOL_GADG_BACK)
		    ChoosingBackColor = GadgetPTR->Flags & SELECTED;
		else if(GadgetPTR->GadgetID == TOOL_GADG_PATTERN)
		 {
		    if(GadgetPTR->Flags & SELECTED)
		     {
			PatternOn = TRUE;
			SetAfPt(VRastPort,&areaPattern[0],1L);
		     }
		    else
		     {
			PatternOn = FALSE;
			SetAfPt(VRastPort,0L,0L);
		     }
		 }
	     }
	    if(CurrWindow == WINDOW_COORD)
	     {
		switch(GadgetPTR->GadgetID)
		 {
		    case COORD_GADG_GRID:
			if(GadgetPTR->Flags & SELECTED)
			    Grid = TRUE;
			else Grid = FALSE;
			break;
		    case COORD_GADG_GRIDX:
			TempString = (struct StringInfo *)GadgetPTR->SpecialInfo;
			if(TempString->LongInt < 1 || TempString->LongInt > PaintWidth)
			    DoAutoRequest(MPaintWindow,"Invalid Entry, Try again",NULL,"Ok");
			else GridX = TempString->LongInt;
			break;
		    case COORD_GADG_GRIDY:
			TempString = (struct StringInfo *)GadgetPTR->SpecialInfo;
			if(TempString->LongInt < 1 || TempString->LongInt > PaintWidth)
			    DoAutoRequest(MPaintWindow,"Invalid Entry, Try again",NULL,"Ok");
			else GridY = TempString->LongInt;
			break;
		 }
	     }
	    if(CurrWindow == WINDOW_PALETTE)
	     {
		PropGadgDown = FALSE;
		if(GadgetPTR->GadgetID >= PALETTE_GADG_START && GadgetPTR->GadgetID <= PALETTE_GADG_PROP_BLUE)
		 {
		    SetScreenTitle("MPaint");
		    PaletteColorMode = COLORMODE_NORMAL;
		    TempColor = ColorPTR[ForeColor];
		    switch(GadgetPTR->GadgetID)
		     {
			case PALETTE_GADG_RED_PLUS2:
			    for(i=0;i<MaxColor;++i)
				ColorPTR[i] = Palette_IncRed(ColorPTR[i]);
			    UpdatePropGadgets();
			    break;
			case PALETTE_GADG_GREEN_PLUS2:
			    for(i=0;i<MaxColor;++i)
				ColorPTR[i] = Palette_IncGreen(ColorPTR[i]);
			    UpdatePropGadgets();
			    break;
			case PALETTE_GADG_BLUE_PLUS2:
			    for(i=0;i<MaxColor;++i)
				ColorPTR[i] = Palette_IncBlue(ColorPTR[i]);
			    UpdatePropGadgets();
			    break;
			case PALETTE_GADG_RED_MINUS2:
			    for(i=0;i<MaxColor;++i)
				ColorPTR[i] = Palette_DecRed(ColorPTR[i]);
			    UpdatePropGadgets();
			    break;
			case PALETTE_GADG_GREEN_MINUS2:
			    for(i=0;i<MaxColor;++i)
				ColorPTR[i] = Palette_DecGreen(ColorPTR[i]);
			    UpdatePropGadgets();
			    break;
			case PALETTE_GADG_BLUE_MINUS2:
			    for(i=0;i<MaxColor;++i)
				ColorPTR[i] = Palette_DecBlue(ColorPTR[i]);
			    UpdatePropGadgets();
			    break;
			case PALETTE_GADG_PROP_RED:
			    TempProp = (struct PropInfo *)GadgetPTR->SpecialInfo;
			    ColorPTR[ForeColor] = Palette_SetRed(ColorPTR[ForeColor],PotConvert16(TempProp->VertPot));
			    UpdatePropGadgets();
			    break;
			case PALETTE_GADG_PROP_GREEN:
			    TempProp = (struct PropInfo *)GadgetPTR->SpecialInfo;
			    ColorPTR[ForeColor] = Palette_SetGreen(ColorPTR[ForeColor],PotConvert16(TempProp->VertPot));
			    UpdatePropGadgets();
			    break;
			case PALETTE_GADG_PROP_BLUE:
			    TempProp = (struct PropInfo *)GadgetPTR->SpecialInfo;
			    ColorPTR[ForeColor] = Palette_SetBlue(ColorPTR[ForeColor],PotConvert16(TempProp->VertPot));
			    UpdatePropGadgets();
			    break;
		     }
		    LoadRGB4(&MPaintScreen->ViewPort,ColorPTR,MaxColor);
		 }
		else if(GadgetPTR->GadgetID < 32 && PaletteWindow)
		    switch(PaletteColorMode)
		     {
			case COLORMODE_NORMAL:
			    SetForeColor((long)GadgetPTR->GadgetID);
			    break;
			case COLORMODE_COPY:
			    ColorPTR[GadgetPTR->GadgetID] = ColorPTR[ForeColor];
			    SetScreenTitle("MPaint");
			    PaletteColorMode = COLORMODE_NORMAL;
			    LoadRGB4(&MPaintScreen->ViewPort,ColorPTR,MaxColor);
			    break;
			case COLORMODE_EXCHNG:
			    TempColor = ColorPTR[ForeColor];
			    ColorPTR[ForeColor]= ColorPTR[GadgetPTR->GadgetID];
			    ColorPTR[GadgetPTR->GadgetID] = TempColor;
			    SetScreenTitle("MPaint");
			    PaletteColorMode = COLORMODE_NORMAL;
			    LoadRGB4(&MPaintScreen->ViewPort,ColorPTR,MaxColor);
			    break;
		     }
		else if(GadgetPTR->GadgetID == PALETTE_GADG_COPY)
		 {
		    SetScreenTitle("Choose color to copy to");
		    PaletteColorMode = COLORMODE_COPY;
		 }
		else if(GadgetPTR->GadgetID == PALETTE_GADG_EXCHNG)
		 {
		    SetScreenTitle("Choose color to exchange with");
		    PaletteColorMode = COLORMODE_EXCHNG;
		 }
	     }
	    break;
     }
}

/*============================================================================*/

HandlePaintWindowInput()
{
    struct IntuiMessage *message;
    CurrWindow = WINDOW_PAINT;
    while ((message = (struct IntuiMessage *)GetMsg(MPaintWindow->UserPort)) != NULL)
     {
	if(message->Class == MOUSEMOVE)
	 {
	    while(message->Class == MOUSEMOVE)
	     {
		mx=message->MouseX;
		my=message->MouseY;
		ReplyMsg(message);

		message = (struct IntuiMessage *)GetMsg(MPaintWindow->UserPort);
		if(message == NULL)
		 {
		    Class = MOUSEMOVE;
		    HandleInput(NULL);
		    break;
		 }
	     }
	 }
	HandleInput(message);
     }
}

/*============================================================================*/
/*  Main entry point of program 					      */
/*  First will initialize everything, then will load file if name specified,  */
/*  then enter main loop						      */
/*============================================================================*/

main(argc,argv)
int argc;
char *argv[];
{
    int x,y,color,Result;
    struct IntuiMessage *message;
    long WaitBits;

    MenuRemember = NULL;

    OpenLibrarys();
    InitVScreen();
    InitMenus();
    InitWindow();
    InitUndoBuffer();

    VRastPort = MPaintWindow->RPort;

    InitTempPlane();                            /* for area and flood operations */
    InitDefaults();                             /* setup default configuration */

    if(argc > 1)                                /* if command line filename,*/
	MIFFLoad(argv[1]);                      /* load it                  */

    Continue = TRUE;
    while(Continue)
     {
	WaitBits = (1L<<MPaintWindow->UserPort->mp_SigBit);
	if(ToolWindow)
	    WaitBits |=(1L<<ToolWindow->UserPort->mp_SigBit);
	if(PaletteWindow)
	    WaitBits |=(1L<<PaletteWindow->UserPort->mp_SigBit);
	if(HistWindow)
	    WaitBits |=(1L<<HistWindow->UserPort->mp_SigBit);
	if(CoordWindow)
	    WaitBits |=(1L<<CoordWindow->UserPort->mp_SigBit);

	Wait(WaitBits);

	HandlePaintWindowInput();
	if(ToolWindow)
	    while ((message = (struct IntuiMessage *)GetMsg(ToolWindow->UserPort)) != NULL)
	     {
		CurrWindow = WINDOW_TOOLS;
		HandleInput(message);
		if(!ToolWindow)
		    break;
	     }
	if(PaletteWindow)
	    while ((message = (struct IntuiMessage *)GetMsg(PaletteWindow->UserPort)) != NULL)
	     {
		CurrWindow = WINDOW_PALETTE;
		HandleInput(message);
		if(!PaletteWindow)
		    break;
	     }
	if(HistWindow)
	    while ((message = (struct IntuiMessage *)GetMsg(HistWindow->UserPort)) != NULL)
	     {
		CurrWindow = WINDOW_HIST;
		HandleInput(message);
		if(!HistWindow)
		    break;
	     }
	if(CoordWindow)
	    while ((message = (struct IntuiMessage *)GetMsg(CoordWindow->UserPort)) != NULL)
	     {
		CurrWindow = WINDOW_COORD;
		HandleInput(message);
		if(!CoordWindow)
		    break;
	     }
     }
    cleanup(NULL,0);                /* user quit, time to cleanup */
}

/*============================================================================*/
/*  end of MPaint.c							      */
/*============================================================================*/


