/*
** $PROJECT: htdemo.datatype DataType demonstrating hypertext.datatype features
**
** $VER: dispatch.c 40.2 (11.09.97)
**
** by
**
** Stefan Ruppert , Windthorststrasse 5 , 65439 Floersheim , GERMANY
**
** (C) Copyright 1996
** All Rights Reserved !
**
** $HISTORY:
**
** 11.09.97 : 040.002 : fixed gcc warnings about vars TagItem initializations
** 08.09.96 : 040.001 : initial
*/

/*FS*//* ------------------------------- include -------------------------------- */

#include "classbase.h"
/*FE*/

/*FS*//* ------------------------------- autodoc -------------------------------- */

/****** xxx.datatype/xxx.datatype ********************************************
*
*    NAME
*
*    FUNCTION
*
*    METHODS
*
*    SEE ALSO
*        hypertext.datatype
*
******************************************************************************
*
*/
/*FE*/

/*FS*//* ------------------------------- defines -------------------------------- */

#define GAD(o)             ((struct Gadget *) (o))
#define GPL(msg)           ((struct gpLayout *) (msg))
#define SET(msg)           ((struct opSet *) (msg))

#define EOS                '\0'
#define MAXARGS            20

struct HTDemoConv
{
   ULONG htdc_NoLine;
   LONG htdc_LIndent;
   LONG htdc_BodyIndent;
};

#define BUFFER_SIZE        256

/*FE*/

/*FS*//* ------------------------------ prototypes ------------------------------ */

ULONG layout(Class *cl, Object *obj, struct gpLayout *msg);
ULONG htdtm_convattrib(Class *cl,Object *obj,struct htConvAttrib *msg);
/*FE*/

/*FS*//* ----------------------------- Preferences ------------------------------ */

/**** Prefs file ReadArgs() support
#define PREFS_TEMPLATE        "MYOPT/K,MYSWITCH/S,MYNUMBER/N"
struct Args
{
   STRPTR a_MyOpt;
   LONG a_MySwitch;
   LONG* a_MyNumber;
};
****/

#ifdef PREFS_TEMPLATE
static BOOL ReadPrefs(Class *cl,Object *obj,STRPTR file)
{
   struct ClassBase *cb = (struct ClassBase *) cl->cl_UserData;
   struct DirData *data = INST_DATA(cl,obj);
   struct RDArgs *rdargs;
   struct RDArgs *args;
   BPTR fh;

   if((fh = Open(file,MODE_OLDFILE)) != NULL)
   {
      UBYTE buf[256];

      DB(("prefs file \"%s\" opened\n",file));
      if((rdargs = (struct RDArgs *) AllocDosObject(DOS_RDARGS,NULL)) != NULL)
      {
	 while(FGets(fh,buf,sizeof(buf)) != NULL)
	 {
	    struct Args para;
	    /* skip comment and empty lines, to speed up parsing */
	    if(buf[0] == ';' || buf[0] == '\n')
	       continue;

	    rdargs->RDA_Source.CS_Buffer = buf;
	    rdargs->RDA_Source.CS_Length = strlen(buf);
	    rdargs->RDA_Source.CS_CurChr = 0;

	    memset(&para,0,sizeof(struct Args));

	    if((args = ReadArgs(PREFS_TEMPLATE,(LONG *) &para,rdargs)) != NULL)
	    {
	       DB(("args at %lx\n",args));

	       if(para.a_MyOpt != NULL)
	       {
	       }

	       if(para.a_MySwitch)
	       {
	       }

	       if(para.a_MyNumber != NULL)
	       {
	       }

	       FreeArgs(args);
	    }
	 }
	 FreeDosObject(DOS_RDARGS , rdargs);
      }
      Close(fh);
   }

   return((BOOL) (fh == NULL));
}
#endif
/*FE*/

/*FS*//* ---------------------------- notify object ----------------------------- */

ULONG NotifyAttrs(Object *o,void *ginfo,ULONG flags,ULONG tag1,...)
{
    return(DoMethod(o, OM_NOTIFY, &tag1, ginfo, flags));
}
/*FE*/

/*FS*//* -------------------------- support functions --------------------------- */

static STRPTR getstring(STRPTR *str,ULONG *len,ULONG max)
{
   STRPTR ptr = *str;
   STRPTR end = ptr + max;

   while(*ptr == ' ' || *ptr == '\t' || *ptr == ',')
      ptr++;

   if(*ptr == '"')
   {
      *str = ++ptr;
      while(ptr < end && *ptr != '"')
	 ptr++;
      *len = ptr - *str;
   } else
   {
      *str = ptr;
      while(ptr < end && *ptr != ' ' && *ptr != '\t' && *ptr != ',' && *ptr != '}' && *ptr != '\n')
	 ptr++;
      *len = ptr - *str;
   }

   return ptr;
}
/*FE*/

/*FS*//* --------------------------- class dispatcher --------------------------- */

ClassCall ULONG dispatcher(REGA0 Class *cl GREG(a0),REGA2 Object *obj GREG(a2),REGA1 Msg msg GREG(a1))
{
   struct ClassBase *cb = (struct ClassBase *) cl->cl_UserData;
   ULONG retval = 0;

   switch(msg->MethodID)
   {
   case OM_NEW:
      {
	 Object *newobj;

	 if((newobj = (Object *) DoSuperMethodA(cl,obj,msg)) != NULL)
	 {
	    DB(("After OM_NEW DoSuperMethodA()\n"));

	    /* initialize the instance data here */

#ifdef PREFS_TEMPLATE
	    if(PROC(FindTask(NULL)->pr_HomeDir == NULL || ReadPrefs(cl,newobj,"PROGDIR:Prefs/DataTypes/xxx.prefs"))
	       ReadPrefs(cl,newobj,"ENV:DataTypes/xxx.prefs");
#endif

	    retval = (ULONG) newobj;
	 }
      }
      break;
   case OM_DISPOSE:
      /* don't need to free any resource, if you use only the SuperClass
       * memory pool (HTDTA_Pool).
       */
      retval = DoSuperMethodA(cl,obj,msg);
      break;
   case OM_UPDATE:
      if(DoMethod(obj, ICM_CHECKLOOP))
	 break;
   case OM_SET:
      /* Pass the attributes to the hypertext class and force a refresh
       * if we need it */
      if((retval = DoSuperMethodA (cl, obj, msg)) && (OCLASS (obj) == cl))
      {
	 struct RastPort *rp;

	 if((rp = ObtainGIRPort(SET(msg)->ops_GInfo)) != NULL)
	 {
	    /* send only a update message GREDRAW_UPDATE */
	    DoMethod(obj,GM_RENDER,SET(msg)->ops_GInfo,rp,GREDRAW_UPDATE);

	    ReleaseGIRPort (rp);
	 }

	 retval = 0;
      }
      break;
   case GM_LAYOUT:
      /* Tell everyone that we are busy doing things */
      NotifyAttrs (obj, GPL(msg)->gpl_GInfo, NULL,
			GA_ID,       GAD(obj)->GadgetID,
			DTA_Busy,    TRUE,
			TAG_DONE);

      /* NOTE: this is different from current known datatypes implementations
       * but its necessary! The hypertext.datatype depends on it.
       * So only do a AsyncLayout, when the SuperClass returns a zero.
       */
      if((retval = DoSuperMethodA (cl, obj, msg)) == 0)
      {
	 /* We need to do this one asynchronously */
	 retval += DoAsyncLayout (obj, (struct gpLayout *) msg);
      }
      break;
   case DTM_PROCLAYOUT:
      /* Tell everyone that we are busy doing things */
      NotifyAttrs (obj, GPL(msg)->gpl_GInfo, NULL,
			GA_ID,       GAD(obj)->GadgetID,
			DTA_Busy,    TRUE,
			TAG_DONE);

      /* Let the super-class partake and then fall through to our layout method */
      DoSuperMethodA (cl, obj, msg);
   case DTM_ASYNCLAYOUT:
      /* Layout the text */
      retval = layout(cl, obj, GPL(msg));
      break;
   case HTDTM_CONVATTRIB:
      retval = htdtm_convattrib(cl,obj,(struct htConvAttrib *) msg);
      break;
   default:
      retval = DoSuperMethodA(cl,obj,msg);
   }

   return retval;
}
/*FE*/

/*FS*//* -------------------------------- layout -------------------------------- */

ULONG layout(Class *cl, Object *obj, struct gpLayout *msg)
{
/*FS*//*"Data"*/
enum
{
   CMD_TITLE,
   CMD_PREV,
   CMD_NEXT,
   CMD_TOC,
   CMD_MAX
};

static const STRPTR cmds[] =
{
   "title",
   "prev",
   "next",
   "toc",
   NULL
};
/*FE*/

   struct ClassBase *cb = (struct ClassBase *) cl->cl_UserData;
   APTR pool;
   ULONG maxwidth = 0;
   ULONG total    = 0;
   ULONG th = 0,tv = 0;

   /* attributes obtained from super-class */
   struct IBox *domain;
   struct TextFont *font;
   STRPTR title;
   STRPTR buffer;
   ULONG bufferlen;

   DB(("htdemo layout , obj : %lx!\n",obj));

   /* Get all the attributes that we are going to need for a successful layout */
   if(GetDTAttrs(obj,
		 DTA_TextFont,      (ULONG) &font,
		 DTA_ObjName,       (ULONG) &title,
		 DTA_Domain,        (ULONG) &domain,
		 DTA_TopHoriz,      (ULONG) &th,
		 DTA_TopVert,       (ULONG) &tv,
		 HTDTA_Buffer,      (ULONG) &buffer,
		 HTDTA_BufferLen,   (ULONG) &bufferlen,
		 HTDTA_Pool,        (ULONG) &pool,
		 TAG_DONE) == 8 && pool != NULL && buffer != NULL)
   {
      ULONG hunit,vunit;
      ULONG hvis,vvis;

      BOOL abort = FALSE;
      struct HTSegInfo *seginfo;

      hunit = font->tf_XSize;
      vunit = font->tf_YSize;
      hvis  = domain->Width  / hunit;
      vvis  = domain->Height / vunit;

      DB(("before OBTAIN object\n"));
      /* lock datatype object */
      DoMethod(obj,HTDTM_OBTAIN);


      DB(("before AllocSegInfo()\n"));
      /* We only need to perform layout if this is the initial layout call. We
       * need also a SegInfo structure !
       */
      if(msg->gpl_Initial && (seginfo = AllocSegInfo(HTDTSIA_Pool,      (ULONG) pool,
						     HTDTSIA_Object,    (ULONG) obj,
						     HTDTSIA_GadgetInfo,(ULONG) msg->gpl_GInfo,
						     TAG_DONE)) != NULL)
      {
	 struct HTConvInfo *convinfo;
	 struct TagItem tags[6];
	 struct HTDemoConv htdconv = {0};
	 STRPTR end = buffer + bufferlen;
	 STRPTR ptr = buffer + 8;
	 STRPTR seg,endseg;

	 while(ptr < end && *ptr == '@' && *(ptr + 1) != '{')
	 {
	    STRPTR const *cmd = cmds;
	    STRPTR name = ++ptr;
	    LONG len;
	    LONG i = 0;

	    while(ptr < end && *ptr != ' ' && *ptr != '\t')
	       ptr++;
	    len = ptr - name;

	    while(*cmd != NULL)
	    {
	       if(!Strnicmp(*cmd,name,len) && len == strlen(*cmd))
	       {
		  STRPTR begin = ptr;
		  ULONG len;
		  UBYTE chr;

		  ptr = getstring(&begin,&len,end-ptr);
		  chr = begin[len];
		  begin[len] = '\0';

		  switch(i)
		  {
		  case CMD_TITLE:
		     SetAttrs(obj,DTA_Title,(ULONG) begin,TAG_DONE);
		     break;
		  case CMD_NEXT:
		     SetAttrs(obj,HTDTA_Next,(ULONG) begin,TAG_DONE);
		     break;
		  case CMD_PREV:
		     SetAttrs(obj,HTDTA_Previous,(ULONG) begin,TAG_DONE);
		     break;
		  case CMD_TOC:
		     SetAttrs(obj,HTDTA_Contents,(ULONG) begin,TAG_DONE);
		     break;
		  }
		  begin[len] = chr;
		  break;
	       }
	       i++;
	       cmd++;
	    }
	    while(ptr < end && *ptr != '\n')
	       ptr++;
	    ptr++;
	 }

	 /* AllocConvInfoA() set up defaults */
	 tags[0].ti_Tag  = HTDTSA_LayoutFlags;
	 tags[1].ti_Tag  = HTDTSA_FgPen;
	 tags[2].ti_Tag  = HTDTSA_BgPen;
	 tags[3].ti_Tag  = HTDTSA_Style;
	 tags[4].ti_Tag  = HTDTSA_TextFont;
	 tags[5].ti_Tag  = TAG_DONE;

	 if((convinfo = AllocConvInfo(seginfo,tags,
				      HTDTCIA_UserData,(ULONG) &htdconv,
				      TAG_DONE)) != NULL)
	 {
	    seg = ptr;
	    while(ptr < end)
	    {
	       /* are we aborted ? */
	       if((abort = (SetSignal(0,SIGBREAKF_CTRL_C) & SIGBREAKF_CTRL_C)))
		  break;

	       if(*ptr == '#')
	       {
		  while(ptr < end && *ptr != '\n')
		     ptr++;
		  seg = ++ptr;
		  continue;
	       }

	       endseg = ptr;
	       /* parse commands of format @name{args} */
	       if(*ptr == '@')
	       {
		  STRPTR tmp = ptr;

		  if(seg < endseg)
		  {
		     AllocSegment(seginfo,HTST_NORMAL,
				  HTDTSA_Text,(ULONG) seg,
				  HTDTSA_TextLen,endseg-seg,
				  TAG_MORE,(ULONG) &tags[0]);
		     seg = endseg;
		  }

		  if((ptr = ConvertAttributes(convinfo,ptr,end-ptr)) == tmp)
		  {
		     switch(IoErr())
		     {
		     case ERROR_NOT_IMPLEMENTED:
			break;
		     }
		  } else
		  {
		     seg = ptr;
		     endseg = ptr;
		  }
	       }

	       if(*ptr == '\n')
	       {
		  if(htdconv.htdc_NoLine)
		  {
		     if(seg < endseg)
			AllocSegment(seginfo,HTST_NORMAL,
				     HTDTSA_Text,(ULONG) seg,
				     HTDTSA_TextLen,endseg - seg,
				     TAG_MORE,(ULONG) &tags[0]);
		     seg = ptr + 1;
		     htdconv.htdc_NoLine = FALSE;
		  } else
		  {
		     AllocSegment(seginfo,HTST_NORMAL,
				  HTDTSA_Text,(ULONG) seg,
				  HTDTSA_TextLen,endseg - seg,
				  HTDTSA_Flags,HTSF_LF,
				  TAG_MORE,(ULONG) &tags[0]);
		     seg = ptr + 1;
		  }
	       }

	       ptr++;
	    }
	    FreeConvInfo(convinfo);
	 }
	 FreeSegInfo(seginfo);
      }

      /* these values must be set explicitly
       * possible error in datatypesclass ?
       */
      {
	 struct DTSpecialInfo *si = (struct DTSpecialInfo *) GAD(obj)->SpecialInfo;

	 si->si_VisVert  = vvis;
	 si->si_TotVert  = total;

	 si->si_VisHoriz = hvis;
	 si->si_TotHoriz = maxwidth;
	 si->si_VertUnit = vunit;
	 si->si_HorizUnit= hunit;
      }

      if(abort == FALSE)
      {
	 /* layout the linked list */
	 total = DoMethod(obj,HTDTM_LAYOUT,msg->gpl_GInfo,NULL,msg->gpl_Initial);

	 GetAttr(DTA_TotalHoriz,obj,&maxwidth);
      }

      /* release (hypertext) datatype object */
      DoMethod(obj,HTDTM_RELEASE);

      DB(("after release()\n"));

      /* Were we aborted? */
      if(abort == FALSE)
      {
	 /* Not aborted, so tell the world of our newest attributes,
	  *
	  * NOTE: you have to pass the DTA_TopHoriz and DTA_TopVert
	  *       here. The hypertext.datatype depends on it.
	  */

	 NotifyAttrs(obj, msg->gpl_GInfo, 0,
			  GA_ID,                   GAD(obj)->GadgetID,

			  DTA_VisibleVert,         vvis,
			  DTA_TotalVert,           total,
			  DTA_NominalVert,         25 * vunit,
			  DTA_VertUnit,            vunit,

			  DTA_VisibleHoriz,        hvis,
			  DTA_TotalHoriz,          maxwidth,
			  DTA_NominalHoriz,        60 * hunit,
			  DTA_HorizUnit,           hunit,

			  DTA_TopHoriz,            th,
			  DTA_TopVert,             tv,

			  DTA_Title,               title,
			  DTA_Busy,                FALSE,
			  DTA_Sync,                TRUE,
			  TAG_DONE);
      }
   }

   return total;
}
/*FE*/

/*FS*//* -------------------------- convert attributes -------------------------- */

ULONG htdtm_convattrib(Class *cl,Object *obj,struct htConvAttrib *msg)
{
/*FS*//*"Data"*/
enum
{
   TDT_LINK,
   TDT_RX,
   TDT_RXS,
   TDT_SYSTEM,
   TDT_QUIT,
   TDT_OBJECT,
   TDT_NOLINE,
   TDT_LINE,
   TDT_SEPARATOR,
   TDT_LINDENT,
   TDT_PARI,
   TDT_PAR,
   TDT_MAX
};

static const STRPTR cmds[] =
{
   "link",     /* text,path} */
   "rx",       /* text,command } */
   "rxs",      /* text,string } */
   "system",   /* command} */
   "quit",     /* text} */
   "object",   /* file} */
   "noline",   /* } */
   "line",     /* } */
   "separator",/* } */
   "lindent",  /* num} */
   "pari",     /* num} */
   "par",
   NULL
};
/*FE*/
   struct ClassBase *cb = (struct ClassBase *) cl->cl_UserData;
   struct HTSegInfo *seginfo = msg->htca_SegInfo;
   struct HTDemoConv *conv = msg->htca_UserData;
   const STRPTR *cmd = cmds;
   ULONG rc = FALSE;
   LONG len;
   ULONG tdtype = 0;

   if(*msg->htca_Keyword == '"')
   {
      STRPTR text = msg->htca_Keyword + 1;
      ULONG textlen = msg->htca_KeywordLen - 2;

      STRPTR ptr = msg->htca_Arguments;
      STRPTR end = ptr + msg->htca_ArgumentsLen;
      STRPTR keyword = ptr;
      ULONG keylen;
      LONG i;

      while(ptr < end && *ptr != ' ' && *ptr != '\t')
	 ptr++;
      keylen = ptr - keyword;

      while(ptr < end && (*ptr == ' ' || *ptr == '\t'))
	 ptr++;

      for(i = 0; i < TDT_OBJECT ; i++)
	 if(!Strnicmp(keyword,cmds[i],keylen) && strlen(cmds[i]) == keylen)
	 {
	    if(i == TDT_QUIT)
	    {
	       AllocSegment(seginfo,HTST_NORMAL,
			    HTDTSA_Text,(ULONG) text,
			    HTDTSA_TextLen,textlen,
			    HTDTSA_Flags,HTSF_LINK,
			    HTDTSA_LinkType,HTLKT_QUIT,
			    (msg->htca_TagList != NULL) ? TAG_MORE : TAG_DONE,(ULONG) msg->htca_TagList);
	    } else
	    {
	       STRPTR data = ptr;
	       ULONG datalen;
	       UBYTE chr;

	       getstring(&data,&datalen,end-ptr);
	       chr = data[datalen];
	       data[datalen] = '\0';

	       AllocSegment(seginfo,HTST_NORMAL,
			    HTDTSA_Text,(ULONG) text,
			    HTDTSA_TextLen,textlen,
			    HTDTSA_Flags,HTSF_LINK,
			    HTDTSA_LinkType,tdtype - TDT_LINK + 1,
			    HTDTSA_LinkData,(ULONG) data,
			    HTDTSA_AllocLink,TRUE,
			    (msg->htca_TagList != NULL) ? TAG_MORE : TAG_DONE,(ULONG) msg->htca_TagList);

	       data[datalen] = chr;
	    }
	    rc = TRUE;
	    break;
	 }
   } else
   {

      while(*cmd != NULL)
      {
	 if(!Strnicmp(msg->htca_Keyword,*cmd,msg->htca_KeywordLen) && strlen(*cmd) == msg->htca_KeywordLen)
	 {
	    DB(("found : %s\n",*cmd));

	    rc = TRUE;

	    switch(tdtype)
	    {
	    case TDT_OBJECT:
	       {
		  UBYTE buf[256];

		  len = msg->htca_ArgumentsLen;
		  if(len > sizeof(buf)-1)
		     len = sizeof(buf)-1;
		  strncpy(buf,msg->htca_Arguments,len);
		  buf[len] = '\0';

		  DB(("object name : \"%s\"\n",buf));
		  AllocSegment(seginfo,HTST_DTOBJECT,
				       HTDTSA_Name,(ULONG) buf,
				       (msg->htca_TagList != NULL) ? TAG_MORE : TAG_DONE,(ULONG) msg->htca_TagList);

	       }
	       break;
	    case TDT_LINK:
	    case TDT_RX:
	    case TDT_RXS:
	    case TDT_SYSTEM:
	       {
		  STRPTR ptr  = msg->htca_Arguments;
		  STRPTR end  = ptr + msg->htca_ArgumentsLen;
		  STRPTR text = ptr;
		  STRPTR data;
		  ULONG textlen,datalen;
		  UBYTE chr;

		  ptr = getstring(&text,&textlen,end-ptr);

		  data = ++ptr;
		  getstring(&data,&datalen,end-ptr);
		  chr = data[datalen];
		  data[datalen] = '\0';

		  AllocSegment(seginfo,HTST_NORMAL,
			       HTDTSA_Text,(ULONG) text,
			       HTDTSA_TextLen,textlen,
			       HTDTSA_Flags,HTSF_LINK,
			       HTDTSA_LinkType,tdtype - TDT_LINK + 1,
			       HTDTSA_LinkData,(ULONG) data,
			       HTDTSA_AllocLink,TRUE,
			       (msg->htca_TagList != NULL) ? TAG_MORE : TAG_DONE,(ULONG) msg->htca_TagList);

		  data[datalen] = chr;

		  DB(("link(%ld)\n",tdtype - TDT_LINK + 1));
	       }
	       break;
	    case TDT_QUIT:
	       {
		  STRPTR ptr  = msg->htca_Arguments;
		  STRPTR end  = ptr + msg->htca_ArgumentsLen;
		  STRPTR text = ptr;
		  ULONG textlen;

		  getstring(&text,&textlen,end-ptr);

		  AllocSegment(seginfo,HTST_NORMAL,
				       HTDTSA_Text,(ULONG) text,
				       HTDTSA_TextLen,textlen,
				       HTDTSA_Flags,HTSF_LINK,
				       HTDTSA_LinkType,HTLKT_QUIT,
				       (msg->htca_TagList != NULL) ? TAG_MORE : TAG_DONE,(ULONG) msg->htca_TagList);

	       }
	       break;
	    case TDT_NOLINE:
	       conv->htdc_NoLine = TRUE;
	       break;
	    case TDT_LINE:
	       AllocSegment(seginfo,HTST_NORMAL,
				    HTDTSA_Text,(ULONG) "",
				    HTDTSA_TextLen,0,
				    HTDTSA_Flags,HTSF_LF,
				    TAG_DONE);
	       break;
	    case TDT_SEPARATOR:
	       AllocSegment(seginfo,HTST_SEPARATOR,
				    HTDTSA_Height,2,
				    TAG_DONE);
	       break;
	    case TDT_LINDENT:
	       StrToLong(msg->htca_Arguments,&conv->htdc_LIndent);
	       AllocSegment(seginfo,HTST_STARTPARAGRAPH,
				    HTDTSA_ParaIndent,conv->htdc_LIndent,
				    HTDTSA_BodyIndent,conv->htdc_BodyIndent,
				    TAG_DONE);
	       break;
	    case TDT_PARI:
	       StrToLong(msg->htca_Arguments,&conv->htdc_BodyIndent);
	       break;
	    case TDT_PAR:
	       AllocSegment(seginfo,HTST_ENDPARAGRAPH,
				    TAG_DONE);
	       break;
	    default:
	       rc = FALSE;
	    }
	    break;
	 }
	 tdtype++;
	 cmd++;
      }
   }

   return rc;
}
/*FE*/

