#define NAME	 "WWTabManager"
#define REVISION "5"
#define ENDCODE_NOCTRLC
//#define DEBUG

/* Programmheader

	Name:		WWTabManager
	Author:		SDI
	Distribution:	PD
	Description:	verändert WordWorth-Tabellen
	Compileropts:	-
	Linkeropts:	-l amiga

 1.0    : erste Versuche, nicht komplett
 1.1    : Maßstab eingebaut, kleine Änderungen
 1.2   17.07.96 : Fehlenden Memory gefunden und Fehler entfernt
 1.3   15.09.96 : removed an Error, added SetCol, SetRow and Prev gadgets
 1.4   20.09.96 : added correct Datatype check (hopefully recognices all
	WOWO files !!!)
 1.5   21.09.96 : finished Datatype check - faster startup
*/

#include <pragma/exec_lib.h>
#include <pragma/dos_lib.h>
#include <pragma/intuition_lib.h>
#include <pragma/gadtools_lib.h>
#include <pragma/graphics_lib.h>
#include <intuition/gadgetclass.h>
#include <exec/memory.h>
#include "SDI_defines.h"
#include "SDI_ASM_STD_protos.h"

#ifdef DEBUG
  #include <clib/alib_protos.h>
#endif

#define PARAM	"FROM/A,TO,FACTOR/N"

struct RDArgs		*rda		= 0;
struct Library		*GadToolsBase	= 0;
struct IntuitionBase	*IntuitionBase	= 0;
struct GfxBase		*GfxBase	= 0;
struct Screen		*Scr		= 0;
APTR			VisualInfo	= 0;
struct Window		*window		= 0;
struct FileInfoBlock	*fib		= 0;
struct Gadget		*gadgList	= 0;
struct Gadget		*nextgadget	= 0;
struct Gadget		*prevgadget	= 0;
struct Gadget		*firstcolgadg	= 0;
struct Gadget		*firstrowgadg	= 0;
struct Gadget		*tablegadget	= 0;
ULONG			*buffer		= 0;
struct DataList		*data		= 0;
ULONG			filehandle	= 0;
ULONG			Faktor		= 28;
ULONG			DosVersion	= 37;
struct IntuiMessage	*IntuiMessagePtr= 0;

struct DataList {
  ULONG			*row;
  ULONG			rowsize;
  ULONG 		*column;
  ULONG			colsize;
  UWORD			number;
  struct DataList	*nextlist;
  struct DataList	*prevlist;
};

struct IffData {
  ULONG		*actpos;	// actual bufferpos
  ULONG		restsize;	// free size after 
};

STRPTR text[] =
{
"Table",
"Next",
"Prev",
"Save",
"Restart",
"SetCol",
"SetRow",
"Rows",
"Columns",
};

#define TXT_TABLE	0
#define TXT_NEXT	1
#define TXT_PREV	2
#define TXT_SAVE	3
#define TXT_RESTART	4
#define TXT_SETCOL	5
#define TXT_SETROW	6
#define TXT_ROWS	7
#define	TXT_COLUMNS	8

#define ShowNum(a)	(((a)+Faktor-1)/Faktor)
#define SaveNum(a)	((a)*Faktor)
#define MinSizeR	0x1388
#define MinSizeC	0x1F40

#define MaxR		42	// actual WW5 only takes these numbers
#define MaxC		25	// the following should be used, when WW
//#define MaxR		data->rowsize // takes care of all the table
//#define MaxC		data->colsize // entries, in WW5 51 and 26 are saved

#define GADGET		((struct Gadget *)(IntuiMessage.IAddress))

ULONG DoWindow(void);
void ActGadget(void);
struct DataList *DoGet(struct IffData);
struct IffData *FindTyp(ULONG, UBYTE, struct IffData *, struct IffData *);

#define FindType(a,b,c)	FindTyp(a,0,b,c)
#define FindFORM(a,b,c) FindTyp(a,1,b,c)

void main(void)
{
  struct {
	STRPTR from;
	STRPTR to;
	ULONG  *factor;
  } args = {0, 0, 0};
  struct IntuiMessage	IntuiMessage;

  if(!(rda = ReadArgs(PARAM, (LONG *) &args, 0)))
    End(RETURN_FAIL);

  if(args.factor && *args.factor)
    Faktor = *args.factor;

  if(
  !(filehandle = Open(args.from, MODE_OLDFILE)) ||
  !(fib = (struct FileInfoBlock *) AllocDosObject(DOS_FIB,0)) ||
  !ExamineFH(filehandle, fib) ||
  !(buffer = (ULONG *) AllocMem(fib->fib_Size, MEMF_ANY)) ||
  (Read(filehandle, buffer, fib->fib_Size) != fib->fib_Size) ||
  !(IntuitionBase = (struct IntuitionBase *) OpenLibrary("intuition.library", 37)) ||
  !(GadToolsBase = OpenLibrary("gadtools.library", 37)) ||
  !(GfxBase = (struct GfxBase *) OpenLibrary("graphics.library",37)) ||
  !(Scr = LockPubScreen(0)) ||
  !(VisualInfo = GetVisualInfoA(Scr, 0)))
    End(RETURN_FAIL);

  {
    struct IffData d = {buffer + 3, buffer[1]-4};

    if(*buffer != 'FORM' ||
    buffer[1] != fib->fib_Size - 8 ||
    buffer[2] != 'WOWO' ||
    !(data = DoGet(d)))
    {
      SetIoErr(ERROR_SEEK_ERROR);
      End(RETURN_FAIL);
    }
  }

  if(DoWindow())
    End(RETURN_FAIL);

  ActGadget();

  ScreenToFront(Scr);

  do
  {
    if(Wait((1L<<window->UserPort->mp_SigBit)|SIGBREAKF_CTRL_C)
    == SIGBREAKF_CTRL_C)
      IntuiMessage.Class = IDCMP_CLOSEWINDOW;
    else
      while((IntuiMessagePtr = GT_GetIMsg(window->UserPort)))
      {
        CopyMem(IntuiMessagePtr, &IntuiMessage, sizeof(struct IntuiMessage));
        GT_ReplyIMsg(IntuiMessagePtr);
        if(GADGET->GadgetID >= 200)
        {
	  ULONG a = MinSizeC;
	  if(GADGET->GadgetID >= 300)
	  {
	    args.factor = data->row+GADGET->GadgetID-300; a = MinSizeR;
	  }
	  else
            args.factor = data->column+GADGET->GadgetID-200;

          *args.factor = SaveNum(((struct StringInfo *) GADGET->SpecialInfo)
	  ->LongInt);
          ((struct StringInfo *) GADGET->SpecialInfo)->BufferPos = 0;
	  if(*args.factor < a)
	  {
	    *args.factor = a;
	    GT_SetGadgetAttrs(GADGET, window, 0, GTIN_Number, ShowNum(a),
	    TAG_DONE);
 	  }
	  RefreshGList(GADGET,window,0,1);
        }
        else if(GADGET->GadgetID == 100 + TXT_SAVE)
        {
          Close(filehandle);
          if(!(filehandle = Open(args.to ? args.to : args.from, MODE_NEWFILE)))
            End(RETURN_FAIL);
          Write(filehandle,buffer,fib->fib_Size);
	  End(RETURN_OK);
        }
        else
        {
	  struct Gadget *g;
	  ULONG i;

          switch(GADGET->GadgetID)
          {
          case 100 + TXT_RESTART:
            while(data->prevlist)
              data = data->prevlist;
	    Seek(filehandle,0,OFFSET_BEGINNING);
	    Read(filehandle,buffer,fib->fib_Size);
	    ActGadget();
            break;
          case 100 + TXT_NEXT:
            if(data->nextlist)
            {
              data = data->nextlist;
	      ActGadget();
            }
            else
              continue;
            break;
          case 100 + TXT_PREV:
            if(data->prevlist)
            {
              data = data->prevlist;
	      ActGadget();
            }
            else
              continue;
            break;
          case 100 + TXT_SETCOL:
	    for(i = 1; i < MaxC; ++i)
              data->column[i] = data->column[0];
	    break;
	  case 100 + TXT_SETROW:
	    for(i = 1; i < MaxR; ++i)
              data->row[i] = data->row[0];
            break;
          }
          g = firstcolgadg;
	  for(i = 0; i < MaxC; ++i)
          {
            GT_SetGadgetAttrs(g, window, 0, GTIN_Number, ShowNum(data->column[i]), TAG_DONE);
	    g = g->NextGadget;
          }
	  g = firstrowgadg;
	  for(i = 0; i < MaxR; ++i)
          {
            GT_SetGadgetAttrs(g, window, 0, GTIN_Number, ShowNum(data->row[i]), TAG_DONE);
	    g = g->NextGadget;
          }
        }
      }
  }
  while(IntuiMessage.Class != IDCMP_CLOSEWINDOW);

  End(RETURN_OK);
}

void ActGadget(void)
{
  GT_SetGadgetAttrs(nextgadget, window, 0, GA_Disabled,
  (ULONG) !data->nextlist, TAG_DONE);
  GT_SetGadgetAttrs(prevgadget, window, 0, GA_Disabled,
  (ULONG) !data->prevlist, TAG_DONE);
  GT_SetGadgetAttrs(tablegadget, window, 0, GTNM_Number, data->number);
}

struct DataList *DoGet(struct IffData iffdat)
{
  struct DataList *data = 0;
  struct IffData gtid;

  while(FindFORM('GTID', &iffdat, &gtid))
  {
    struct IffData gtip;
    while(FindFORM('GTIP', &gtid, &gtip))
    {
      struct IffData oa09;
      while(FindFORM('OA09', &gtip, &oa09))
      {
        struct IffData rhed, ched;
        if(
        (FindFORM('AXES', &oa09, &ched)) &&
        (FindFORM('RHED', &ched, &rhed)) &&
        (FindType('SIZE', &rhed, &rhed)) &&
        (FindFORM('CHED', &ched, &ched)) &&
        (FindType('SIZE', &ched, &ched)))
        {
          struct DataList *dat;

          if(!(dat = (struct DataList *) AllocMem(sizeof(struct DataList),
          MEMF_ANY|MEMF_CLEAR)))
            End(RETURN_FAIL);
	  dat->row = rhed.actpos;
	  dat->rowsize = rhed.restsize >> 2;
	  dat->column = ched.actpos;
	  dat->colsize = ched.restsize >> 2;
          dat->prevlist = data;
          if(data)
          {
            data->nextlist = dat;
            dat->number = data->number + 1;
          }
          else
            dat->number = 1;
          data = dat;
        }
      }
    }
  }

  while(data->prevlist)
    data = data->prevlist;

  return data;
}

struct IffData *FindTyp(ULONG ID, UBYTE form, struct IffData *dat, struct IffData *sub)
{
  ULONG size;
  ULONG *ptr;

#ifdef DEBUG
  Printf("FindTyp: scan for %s %.4s\n", (form ? "FORM" : "CHUNK"), &ID);
#endif

  while(dat->restsize)
  {
#ifdef DEBUG
  size = '    ';
  Printf("FindTyp: actual: %.4s %.4s, restsize (%ld)\n",
  dat->actpos, (*dat->actpos == 'FORM' ? dat->actpos + 2 : &size),
  dat->restsize);
#endif

    ptr = dat->actpos;
    size = ((ptr[1] + 1) & ~1);

    dat->restsize -= size + 8;
    (STRPTR) dat->actpos += size + 8;

    if((form && *ptr == 'FORM' && ptr[2] == ID) || (!form && *ptr == ID))
    {
      ptr += 2;
      if(form)
      { ++ptr; size -= 4; }
      sub->actpos = ptr;
      sub->restsize = size;

      return sub;
    }
  }
// if dat and sub are same, only sub is valid return result

#ifdef DEBUG
  Printf("FindTyp: didn't find %.4s,\n", &ID);
#endif
  return 0;
}

#define		i	winheight

ULONG DoWindow(void)
{
  UWORD	winwidth;
  UWORD winheight;
  struct Gadget		*g, *a, *b;
  struct NewGadget	ng = {Scr->WBorLeft + 5,
			Scr->WBorTop + Scr->Font->ta_YSize + 1 + 5,
			0, Scr->Font->ta_YSize+5, 0,
			Scr->Font, 0, 0, VisualInfo, 0};

  if(!(g = CreateContext(&gadgList)))
    return 1;

  for(i = TXT_NEXT; i <= TXT_SETROW; ++i)
  {
    if((winwidth = TextLength(&Scr->RastPort, text[i], SDI_strlen(text[i])))
    > ng.ng_Width)
      ng.ng_Width = winwidth;
  }
  ng.ng_Width += 10;

  for(i = TXT_NEXT; i <= TXT_SETROW; ++i)
  {
    ng.ng_GadgetText = text[i]; ng.ng_GadgetID = 100 + i;
    if(!(g = CreateGadgetA(BUTTON_KIND, g, &ng, 0)))
      return 2;
    if(i == TXT_NEXT)
      nextgadget = g;
    else if(i == TXT_PREV)
      prevgadget = g;
    ng.ng_LeftEdge += ng.ng_Width + 5;
  }
  ng.ng_LeftEdge += TextLength(&Scr->RastPort, text[TXT_TABLE],
  SDI_strlen(text[TXT_TABLE])) + 10;
  ng.ng_Width = TextLength(&Scr->RastPort, "88888", 5) + 15;
  ng.ng_GadgetText = text[TXT_TABLE];
  ng.ng_GadgetID = 100 + TXT_TABLE;

  if(!(tablegadget = g = CreateGadget(NUMBER_KIND, g, &ng,
  GTNM_Justification, GTJ_CENTER, GTNM_Border, TRUE, GTNM_Clipped, TRUE,
  TAG_DONE)))
    return 3;

  winwidth = ng.ng_LeftEdge + ng.ng_Width + 5 + Scr->WBorRight;
  ng.ng_GadgetText = 0;

  ng.ng_TopEdge += ng.ng_Height + 5;	// Place for the Text
  ng.ng_LeftEdge = Scr->WBorLeft + 5;
  ng.ng_GadgetID = 100 + TXT_COLUMNS;
  i = ng.ng_Width; ng.ng_Width = (i<<1) + 5;
  if(!(a = g = CreateGadget(TEXT_KIND, g, &ng, GTTX_Border, TRUE,
  GTTX_Justification, GTJ_CENTER, GTTX_Clipped, TRUE, TAG_DONE)))
    return 3;
  ng.ng_LeftEdge += ng.ng_Width + 5;
  ng.ng_Width = i;

  for(i = 200; i < 200+MaxC; ++i)
  {
    if((i%10)==8)
    {
      ng.ng_TopEdge += ng.ng_Height + 5;
      ng.ng_LeftEdge = Scr->WBorLeft + 5;
    }
    ng.ng_GadgetID = i;
    if(!(g = CreateGadget(INTEGER_KIND, g, &ng, GTIN_MaxChars, 5,
    STRINGA_Justification, GACT_STRINGRIGHT, STRINGA_ReplaceMode, TRUE,
    GTIN_Number, ShowNum(data->column[i-200]), TAG_DONE)))
      return 3;
    if(i==200)
      firstcolgadg = g;
    ng.ng_LeftEdge += ng.ng_Width + 5;
  }

  ng.ng_TopEdge += ng.ng_Height + 5;	// Place for the Text
  ng.ng_LeftEdge = Scr->WBorLeft + 5;
  ng.ng_GadgetID = 100 + TXT_ROWS;
  i = ng.ng_Width; ng.ng_Width = (i<<1) + 5;
  if(!(b = g = CreateGadget(TEXT_KIND, g, &ng, GTTX_Border, TRUE,
  GTTX_Justification, GTJ_CENTER, GTTX_Clipped, TRUE, TAG_DONE)))
    return 3;
  ng.ng_LeftEdge += ng.ng_Width + 5;
  ng.ng_Width = i;

  for(i = 300; i < 300+MaxR; ++i)
  {
    if((i%10)==8)
    {
      ng.ng_TopEdge += ng.ng_Height + 5;
      ng.ng_LeftEdge = Scr->WBorLeft + 5;
    }
    ng.ng_GadgetID = i;
    if(!(g = CreateGadget(INTEGER_KIND, g, &ng, GTIN_MaxChars, 5,
    STRINGA_Justification, GACT_STRINGRIGHT, STRINGA_ReplaceMode, TRUE,
    GTIN_Number, ShowNum(data->row[i-300]), TAG_DONE)))
      return 3;
    if(i==300)
      firstrowgadg = g;
    ng.ng_LeftEdge += ng.ng_Width + 5;
  }

  if((i = ((ng.ng_Width + 5)*10) + Scr->WBorLeft + 5 + Scr->WBorRight)
  > winwidth)
    winwidth = i;

  winheight = ng.ng_TopEdge + ng.ng_Height + 5 + Scr->WBorBottom;

  if(winwidth > Scr->Width || winheight > Scr->Height)
    return 4;

  if(!(window = OpenWindowTags(0,
	WA_Left,	(Scr->Width-winwidth >> 1),
	WA_Top,		(Scr->Height-winheight >> 1),
	WA_Width,	winwidth,
	WA_Height,	winheight,
	WA_IDCMP,	IDCMP_GADGETUP|IDCMP_CLOSEWINDOW,
	WA_Flags,	WFLG_DRAGBAR|WFLG_DEPTHGADGET|WFLG_CLOSEGADGET|
			WFLG_SMART_REFRESH,
	WA_Gadgets,	gadgList,
	WA_ScreenTitle,	version + 6,
	WA_Title,	version + 6,
	WA_AutoAdjust,	TRUE,
        WA_Activate,	TRUE,
	WA_PubScreen,	Scr,
	TAG_DONE)))
    return 5;

  GT_SetGadgetAttrs(a,window,0, GTTX_Text, (ULONG) text[TXT_COLUMNS],
  TAG_DONE);
  GT_SetGadgetAttrs(b,window,0, GTTX_Text, (ULONG) text[TXT_ROWS],
  TAG_DONE);

  return 0;
}

void end(void)
{
  if(data)
  {
    struct DataList *d;
    while(data->prevlist)
      data = data->prevlist;
    while(data)
    {
      d = data;
      data = d->nextlist;
      FreeMem(d, sizeof(struct DataList));
    }
  }
  if(window)	 	CloseWindow(window);
  if(gadgList)		FreeGadgets(gadgList);
  if(VisualInfo)	FreeVisualInfo(VisualInfo);
  if(Scr)		UnlockPubScreen(0, Scr);
  if(GfxBase)		CloseLibrary((struct Library *) GfxBase);
  if(GadToolsBase)	CloseLibrary(GadToolsBase);
  if(IntuitionBase)	CloseLibrary((struct Library *) IntuitionBase);
  if(buffer)		FreeMem(buffer, fib->fib_Size);
  if(fib)		FreeDosObject(DOS_FIB, fib);
  if(filehandle)	Close(filehandle);
  if(rda)		FreeArgs(rda);
}
