#define FRAMECOUNT 100

#include "skeleton.h"
#include <proto/intuition.h>
#include <proto/graphics.h>
#include <graphics/gfxmacros.h>
#include <graphics/display.h>
#include <proto/exec.h>
#include <exec/libraries.h>
#include <exec/memory.h>
#include <libraries/dosextens.h>
#include <proto/req.h>


struct IntuitionBase *IntuitionBase=NULL;
struct GfxBase *GfxBase=NULL;
struct Window *Win=NULL;
struct ReBase *ReqBase=NULL;
struct RastPort *RP;
struct Screen *Scrn;
struct TmpRas TRas;
struct AreaInfo AInfo;
SHORT *AreaBuffer=NULL;
PLANEPTR RastSpace=NULL;
int AreaSize=200;

struct Process *pr=NULL;
APTR oldprocesswinptr;

/* Declare and initialize your NewScreen structure: */
struct NewScreen NewScrn=
{
  0, 0, ScrnWidth, ScrnHeight, 2,
  0, 1,
  ScrnFlags,           /* ViewModes High-resolution, Interlaced */
  CUSTOMSCREEN,    /* Type      Your own customized screen. */
  NULL,            /* Font      Default font. */
  "KFAST SCREEN",  /* Title     The screen' title. */
  NULL, NULL
};

/* Declare and initialize your NewWindow structure: */
struct NewWindow NewWin=
{
  0, 10, ScrnWidth, ScrnHeight-10,
  0, 1,
  CLOSEWINDOW|VANILLAKEY|MOUSEBUTTONS|REFRESHWINDOW,
  SIMPLE_REFRESH|WINDOWCLOSE|WINDOWDRAG|WINDOWDEPTH|WINDOWSIZING|
   ACTIVATE|RMBTRAP|GIMMEZEROZERO|REPORTMOUSE,
  NULL,          /* FirstGadget No gadgets connected to this window. */
  NULL,          /* CheckMark   Use Intuition's default CheckMark. */
  "KFAST",       /* Title Title of the window. */
  NULL,          /* Screen      Connected to the Workbench Screen. */
  NULL,          /* BitMap      No Custom BitMap. */
  20, 20, ScrnWidth, ScrnHeight,
  CUSTOMSCREEN   /* Type        Connected to the Workbench Screen. */
};

obj_ptr framehead = NULL;

void Help(void)
{
 static char str[] = "\
   Left mouse button draw line / Right mouse button ends line\n\
   While the Left button is down you can draw freehand - SLOWLY\n\n\
   SPACE/BCKSPC - scroll through ALL frames\n\
   n/p - Next/Prev ENTERED frame\n\
   f/l - First/Last frame (100 frames available)\n\n\
   I/i - Toggle IMAGE flag for the frame / set draw mode to IMAGE\n\
   S/s - Toggle SKELETON flag for the frame / set draw mode to SKELETON\n\
   O/o - Toggle OUTLINE flag for the frame / set draw mode to OUTLINE\n\
   x/X/y/Y - toggle deceleration/acceleration in x/y tweening\n\
   0-3 - change IMAGE line color / shifted these change IMAGE fill color\n\
   F - toggle Fill mode\n\n\
   TAB - Change Display Mode (All, Images, or Entered)\n\
   c/C - clear the current draw mode for a frame/clear everything everywhere\n\
   a - Animate - generate intermediate image frames\n\
   r/w - Read/Write a file\n\n\
   h - Display this page";
  
	struct TRStructure trs;
	
	trs.Text = str;
	trs.Controls = 0;
	trs.Window = 0;
	trs.MiddleText = 0;
	trs.PositiveText = 0;
	trs.NegativeText = "OK";
	trs.Title = "KFAST Help";
	trs.KeyMask = 0xFFFF;
	trs.textcolor = 2;
	trs.detailcolor = 3;
	trs.blockcolor = 2;
	trs.versionnumber = REQVERSION;
	trs.Timeout = 60;
	trs.AbortMask=0;
	trs.rfu1=0;
	TextRequest(&trs);
}

char TitleInfo[127];

void Attributes(int display, int draw,int entry,int ilaw,int linec,int fillc)
{
	char displaytext[13],drawtext[13],entrytext[4],ilawtext[5],colortext[50];

	if(display==ENTRY)
		strcpy(displaytext,"Entered");
	else if(display==IMAGE)
		strcpy(displaytext,"Images");
	else
		strcpy(displaytext,"All");

	colortext[0] = 0;
			
	if((draw&IMAGE)==IMAGE)
	{
		if((draw&FILL)!=0)
		{
			strcpy(drawtext,"Image w/Fill");
			sprintf(colortext," LC:%d FC:%d",linec,fillc);
		}
		else
		{
			strcpy(drawtext,"Image");
			sprintf(colortext," LC:%d",linec);
		}
	}
	else if(draw==SKELETON)
		strcpy(drawtext,"Skeleton");
	else if(draw==XOUTLINE)
		strcpy(drawtext,"Outline");
	else
		strcpy(drawtext,"???");
	
	strcpy(entrytext,"   ");	
	if((entry&IMAGE)!=0)
		entrytext[0] = 'I';
	if((entry&SKELETON)!=0)
		entrytext[1] = 'S';
	if((entry&XOUTLINE)!=0)
		entrytext[2] = 'O';
		
	strcpy(ilawtext,"    ");
	if((ilaw&XACCEL)!=0)
		ilawtext[0]='X';
	if((ilaw&XDECEL)!=0)
		ilawtext[1]='x';
	if((ilaw&YACCEL)!=0)
		ilawtext[2]='Y';
	if((ilaw&YDECEL)!=0)
		ilawtext[3]='y';
	
	
	sprintf(TitleInfo,"DISPLAY:%s DRAWMODE:%s%s ENTERED:%s ILAW:%s",
		displaytext,drawtext,colortext,entrytext,ilawtext);
		
	SetWindowTitles(Win,TitleInfo,(char *)-1);
}

BOOL init(void)
{
  pr = (struct Process *)FindTask(NULL);
  if(pr==NULL)
  	return(FALSE);

  oldprocesswinptr = pr->pr_WindowPtr;

  IntuitionBase = (struct IntuitionBase *)
    OpenLibrary( "intuition.library", 0 );
  
  if( IntuitionBase == NULL )
    return(FALSE);

  GfxBase = (struct GfxBase *)
    OpenLibrary( "graphics.library", 0 );
  
  if( GfxBase == NULL )
    return(FALSE);

  ReqBase = (struct ReqBase *)
    OpenLibrary( "req.library", REQVERSION );
    
  if( ReqBase == NULL )
    return(FALSE);

  Scrn = OpenScreen(&NewScrn);
  if(Scrn==NULL)
  	return(FALSE);

  NewWin.Screen = Scrn;

  Win = (struct Window *) OpenWindow( &NewWin );
  
  if(Win == NULL)
    return(FALSE);

  pr->pr_WindowPtr = (APTR)Win;

  RP = Win->RPort;

  AreaSize = AreaSize + MEM_BLOCKSIZE - (AreaSize%MEM_BLOCKSIZE);
  AreaBuffer = (SHORT *)malloc(5*AreaSize);
  if(AreaBuffer==NULL) return(FALSE);

  RastSpace = (PLANEPTR)AllocRaster(ScrnWidth,ScrnHeight);
  if(RastSpace==NULL) return(FALSE);
  
  InitArea(&AInfo,AreaBuffer,AreaSize);
  RP->AreaInfo = &AInfo;
  RP->TmpRas=(struct TmpRas *)InitTmpRas(&TRas,RastSpace,RASSIZE(ScrnWidth,ScrnHeight));

  BNDRYOFF( RP );

  new();

  return(TRUE);
}

void fini(void)
{
 /* not necessary with Lattice managing the Pool - and slow too
  if(framehead!=NULL)
  {
	new();
  	free(framehead,sizeof(struct object));
  }
  */
  
  if(AreaBuffer!=NULL)
  	free(AreaBuffer,5*AreaSize);
	
  if(RastSpace!=NULL)
    FreeRaster( RastSpace, ScrnWidth, ScrnHeight );
	
  if(pr!=NULL)
  	pr->pr_WindowPtr = oldprocesswinptr;
	
  if(Win!=NULL)
	CloseWindow( Win );

  if(Scrn!=NULL)
	CloseScreen( Scrn );

  if(IntuitionBase!=NULL)
	CloseLibrary( IntuitionBase );

  if(GfxBase!=NULL)
	CloseLibrary( GfxBase );

  if(ReqBase!=NULL)
	CloseLibrary( ReqBase );

  exit(0);  
}

void main(void)
{
  BOOL close_me, refresh, newline, adjust, fixc;
  int i,drawmode,linec,fillc;
  struct IntuiMessage IMsg,*Msg;
  obj_ptr currentframe,firstframe, lastframe, temp;
  line_ptr lastline=NULL;

  if(init()!=TRUE) fini();

  firstframe = makeobject();
  firstframe->entry = IMAGE;
  lastframe = makeobject();
  lastframe->entry = IMAGE;
  addobject(firstframe,lastframe,FRAMECOUNT);
  addchain(framehead,firstframe);
  lastframe = locateframe(lastframe);
  firstframe = locateframe(firstframe);
	
  currentframe = firstframe;

  /* We have opened the window, and everything seems to be OK. */

  Help();

  close_me = FALSE;
  refresh = FALSE;
  newline = TRUE;
  drawmode = IMAGE;
  adjust = FALSE;
  fixc = FALSE;
  linec = 1;
  fillc = 3;

  Attributes(dispmode,drawmode,currentframe->framedown->entry,
             currentframe->framedown->ilaw,linec,fillc);
  Attributes(dispmode,drawmode,currentframe->framedown->entry,
             currentframe->framedown->ilaw,linec,fillc);

  /* Stay in the while loop until the user has selected the Close window */
  /* gadget: */
  while( close_me == FALSE )
  {
    /* Wait until we have recieved a message: */
    Wait( 1 << Win->UserPort->mp_SigBit );

    /* As long as we can collect messages successfully we stay in the */
    /* while-loop: */
    while(Msg = (struct IntuiMessage *) GetMsg(Win->UserPort))
    {
      /* After we have successfully collected the message we can read */
      /* it, and save any important values which we maybe want to check */
      /* later: */
      memcpy((char *)&IMsg,(char *)Msg,sizeof(struct IntuiMessage));

      IMsg.MouseY -= 12; /* correct for GimmeZeroZero */
      
      /* After we have read it we reply as fast as possible: */
      /* REMEMBER! Do never try to read a message after you have replied! */
      /* (Some other process has maybe changed it.) */
      ReplyMsg( (struct Message *)Msg );

      /* Check which IDCMP flag was sent: */
      switch( IMsg.Class )
      {
        case CLOSEWINDOW:  /* The user selected the Close window gadget! */
               close_me=TRUE;
               break;
        
	case MOUSEMOVE:
		if(lastline==NULL)
			break;
		i = lastline->number - 1;
		if(i>0)
		{
			if(
			  (lastline->pts->p[i][1]-lastline->pts->p[i-1][1])*
			  (IMsg.MouseX-lastline->pts->p[i-1][0])-
			  (lastline->pts->p[i][0]-lastline->pts->p[i-1][0])*
			  (IMsg.MouseY-lastline->pts->p[i-1][1])==0)
			{
				lastline->pts->p[i][0] = IMsg.MouseX;
				lastline->pts->p[i][1] = IMsg.MouseY;
			}
			else
			{
				addpoint(lastline,IMsg.MouseX,IMsg.MouseY);
			}
		}
		else
		{
			addpoint(lastline,IMsg.MouseX,IMsg.MouseY);
		}
		
		DrawObject(lastline,currentframe->framedown->offset[0],
			   currentframe->framedown->offset[1]);
 		break;
        case MOUSEBUTTONS: /* The user pressed/released a mouse button. */
               /* We shall now check wich button, and if it was pressed */
               /* or released: */
               switch( IMsg.Code )
               {
                 case SELECTDOWN: /* Left button pressed. */
                       if(((currentframe->framedown->entry&IMAGE)!=0) && ((drawmode&IMAGE)!=0))
		       {
			ModifyIDCMP(Win,NewWin.IDCMPFlags|MOUSEMOVE);

			if(currentframe->framedown->image==NULL)
			{
				lastline = addpoint(NULL,IMsg.MouseX,IMsg.MouseY);
				currentframe->framedown->image = lastline;
				if((drawmode&FILL)!=0)
					lastline->fillc = fillc;
				lastline->linec = linec;
				newline = FALSE;
			}
			else if(newline==TRUE)
			{
				lastline = addpoint(NULL,IMsg.MouseX,IMsg.MouseY);
				appendline(currentframe->framedown->image,lastline);
				if((drawmode&FILL)!=0)
					lastline->fillc = fillc;
				lastline->linec = linec;
				newline=FALSE;
			}
			else
			{
				lastline = appendpoint(currentframe->framedown->image,
					IMsg.MouseX,IMsg.MouseY);
			}

		       }
                       else if(((currentframe->framedown->entry&SKELETON)!=0) && ((drawmode&SKELETON)!=0))
		       {
			lastline = 
				addpoint(currentframe->framedown->skeleton,IMsg.MouseX,IMsg.MouseY);
			if(currentframe->framedown->skeleton==NULL)
			{
				lastline->linec = 2;
				currentframe->framedown->skeleton = lastline;
			}
		       }
                       else if(((currentframe->framedown->entry&XOUTLINE)!=0) && ((drawmode&XOUTLINE)!=0))
		       {
			lastline = 
				addpoint(currentframe->framedown->outline,IMsg.MouseX,IMsg.MouseY);
			if(currentframe->framedown->outline==NULL)
			{
				lastline->linec = 3;
				currentframe->framedown->outline= lastline;
			}	
		       }
		       break;
		 case SELECTUP:
		 	if((drawmode&IMAGE)!=0)
		 		ModifyIDCMP(Win,NewWin.IDCMPFlags);
			lastline = NULL;
			if((drawmode&FILL)!=0)
			{
				EraseFrame(currentframe);
			}
			refresh = TRUE;
		 	break;
                 case MENUDOWN:   /* Right button pressed. */
		 	newline = TRUE;
			
                        break;
               }
               break;
        case VANILLAKEY:     /* The user pressed/released a key! */
               /* Print out the translated keycode (as dec. and hex.): */

	       switch(IMsg.Code)
	       {
		 case 'f':
		 	EraseFrame(currentframe);
			currentframe = firstframe;
			refresh = TRUE;
			newline = TRUE;
			break;
		 case 'l':
		 	EraseFrame(currentframe);
			currentframe = lastframe;
			refresh = TRUE;
			newline = TRUE;
			break;
		 case 'n':
	 		EraseFrame(currentframe);
			currentframe = findtype(currentframe->framedown->next,0,&i);
		 	if(currentframe==NULL)
				currentframe = firstframe;
			else
				currentframe = locateframe(currentframe);
			refresh = TRUE;
			newline = TRUE;
			break;
		 case 'p':
	 		EraseFrame(currentframe);
			currentframe = findtypeback(currentframe->framedown->prev,0,&i);
		 	if(currentframe==NULL)
				currentframe = lastframe;
			else
				currentframe = locateframe(currentframe);
			refresh = TRUE;
			newline = TRUE;
			break;
		 case 'I':
		 	if(currentframe!=firstframe)
				currentframe->framedown->entry^=IMAGE;
			adjust = TRUE;
			newline = TRUE;
			break;
		 case 'i':
		 	drawmode = IMAGE;
			adjust = TRUE;
			newline = TRUE;
			break;
		 case 'S':
		 	currentframe->framedown->entry^=SKELETON;
			adjust = TRUE;
			break;
		 case 's':
		 	drawmode = SKELETON;
			adjust = TRUE;
			break;
		 case 'O':
		 	currentframe->framedown->entry^=XOUTLINE;
			adjust = TRUE;
			newline = TRUE;
			break;
		 case 'o':
		 	drawmode = XOUTLINE;
			adjust = TRUE;
			break;
		 case 'c':
		 	EraseFrame(currentframe);
			if((drawmode&IMAGE)!=0)
			{
				deleteline(currentframe->framedown->image);
				currentframe->framedown->image = NULL;
			}
			else if((drawmode&SKELETON)!=0)
			{
				deleteline(currentframe->framedown->skeleton);
				currentframe->framedown->skeleton = NULL;
			}
			else if((drawmode&XOUTLINE)!=0)
			{
				deleteline(currentframe->framedown->outline);
				currentframe->framedown->outline = NULL;
			}
			lastline = NULL;
			refresh = TRUE;
			break;
		 case 'C':
		 	EraseFrame(currentframe);
			new();
			firstframe = makeobject();
			firstframe->entry = IMAGE;
			lastframe = makeobject();
			lastframe->entry = IMAGE;
			addobject(firstframe,lastframe,FRAMECOUNT);
			addchain(framehead,firstframe);
			lastframe = locateframe(lastframe);
			firstframe = locateframe(firstframe);
			currentframe = firstframe;
			adjust = TRUE;
			break;
		 case ' ':
	 		EraseFrame(currentframe);
		 	if(currentframe->next!=NULL)
				currentframe = currentframe->next;
			else
				currentframe = firstframe;
			refresh = TRUE;
			newline = TRUE;
			break;
		 case   8:/* backspace */
	 		EraseFrame(currentframe);
		 	if(currentframe->prev!=NULL)
				currentframe = currentframe->prev;
			else
				currentframe = lastframe;
			refresh = TRUE;
			newline = TRUE;
			break;
		case 'a':
		 	EraseFrame(currentframe);
			animate(firstframe->framedown);
			refresh = TRUE;
			break;
		case 'x':
			currentframe->framedown->ilaw^=XDECEL;
			adjust = TRUE;
			break;
		case 'X':
			currentframe->framedown->ilaw^=XACCEL;
			adjust = TRUE;
			break;
		case 'y':
			currentframe->framedown->ilaw^=YDECEL;
			adjust = TRUE;
			break;
		case 'Y':
			currentframe->framedown->ilaw^=YACCEL;
			adjust = TRUE;
			break;
		case 'w':
			if(filerequest()!=FALSE)
				save();
			break;
		case 'r':
			EraseFrame(currentframe);
			if(filerequest()!=FALSE)
			{
				load();
			}
			currentframe = framehead;
			firstframe = framehead;
			lastframe = framehead;
			while(lastframe->next!=NULL)
				lastframe = lastframe->next;
			refresh = TRUE;
			newline = TRUE;
			break;
		case 'h':
			Help();
			break;
		case '\t':
			EraseFrame(currentframe);
			if(dispmode==IMAGE)
				dispmode = ENTRY;
			else if(dispmode==ENTRY)
				dispmode = ALL;
			else
				dispmode = IMAGE;
			refresh=TRUE;
			break;
		case 'F':
			drawmode ^=FILL;
			fixc = TRUE;
			break;
		case '0':
		case '1':
		case '2':
		case '3':
			linec = IMsg.Code - '0';
			fixc = TRUE;
			break;
		case ')':
			fillc = 0;
			fixc = TRUE;
			break;
		case '!':
			fillc = 1;
			fixc = TRUE;
			break;
		case '@':
			fillc = 2;
			fixc = TRUE;
			break;
		case '#':
			fillc = 3;
			fixc = TRUE;
			break;
		case '>':
			temp = currentframe;
			do
			{
				EraseFrame(temp);
				temp = temp->next;
				if(temp==NULL)
					temp = firstframe;
				DrawFrame(temp);
			}
			while(currentframe!=temp);
			break;
		default:
			break;
	       }
               
               break;
	case REFRESHWINDOW:
		BeginRefresh(Win);
		DrawFrame(currentframe);
		EndRefresh(Win,TRUE);	
		break;
      }

      if(fixc==TRUE)
      {
		if(((drawmode&IMAGE)!=0) && (newline==FALSE))
		{
			lastline = currentframe->framedown->image;
			if(lastline!=NULL)
			{
				while(lastline->next!=NULL)
					lastline = lastline->next;
			
				lastline->linec = linec;
				if((drawmode&FILL)!=0)
					lastline->fillc = fillc;
				else
					lastline->fillc = 0;
				EraseFrame(currentframe);
				
				refresh = TRUE;
			}
		}
		adjust = TRUE;
		fixc = FALSE;
      }      
      if(adjust==TRUE  || refresh==TRUE)
      {
      	Attributes(dispmode,drawmode,currentframe->framedown->entry,
             currentframe->framedown->ilaw,linec,fillc);
	adjust = FALSE;
      }     
      if(refresh==TRUE)
      {
	DrawFrame(currentframe);
	
	refresh = FALSE;
      }
    }
  }

  fini();
}




