/* Struct-Saver
 *
 * (c) 1992 Thies Wellpott
 *
 * HISTORY
 *
 * 0.90  03.07.92
 *   first working test version finished
 * 1.00  04.07.92
 *   struct-saving improved (some internal fixes); now uses NULL instead
 *   of (APTR)(NULL); GUI finished
 * 1.01  04.07.92
 *   menu shortcuts didn`t work (because of always active string gadget),
 *   bug fixed
 * 1.10  04.07.92
 *   line_length feature added, expanded saving added
 * 1.20  05.07.92
 *   define-saving added; some bugs fixed; internal improvements
 *
 *
 * TODO
 *
 * - nicer names in source (?)
 * - config option for source-look
 *
 */

#include "Struct-Saver.h"

#define MY_IDCMP (IDCMP_GADGETUP | IDCMP_CLOSEWINDOW | IDCMP_MENUPICK |\
	    IDCMP_REFRESHWINDOW | IDCMP_ACTIVEWINDOW)

#define GDG_FNAME 1
#define GDG_WAITTIME 2
#define GDG_LINELEN 3
#define GDG_USE_DEFINES 10
#define GDG_EXPANDED 11
#define GDG_GADGETS 20
#define GDG_MENUS 21

/********** externals **********/

extern void free_lists(void);
extern void save_menu(struct Menu *), save_gadget(struct Gadget *);

/********** Variablen, Daten **********/

/* "$VER: " für DOS-Befehl Version */
char version[] = "$VER: Struct-Saver V"VERSION" ("DATE")";
char title_normal[] = "Struct-Saver V"VERSION;

struct IntuitionBase *IntuitionBase = NULL;
struct Library *GadToolsBase = NULL;

static struct NewWindow newmain_wd =
{
   0,0, 0,0, -1,-1, MY_IDCMP,
   WFLG_DRAGBAR | WFLG_CLOSEGADGET | WFLG_DEPTHGADGET |
      WFLG_SIMPLE_REFRESH | WFLG_ACTIVATE,
   NULL, NULL, title_normal,
   NULL, NULL, 0,0, 0,0, WBENCHSCREEN
};

struct Window *main_wd = NULL;
static APTR vinfo = NULL;
static struct Gadget *gad_list = NULL, *fname_gdg, *time_gdg, *linelen_gdg;
static struct TextAttr ta = {"topaz.font", 8, 0x00, 0x00};

struct Menu *menu = NULL;
static struct NewMenu menu_data[] =
{
  {NM_TITLE, "Project", NULL, 0, ~0, NULL},
   {NM_ITEM, "Save configuration", NULL, 0, ~0, NULL},
   {NM_ITEM, NM_BARLABEL,	   NULL, 0, ~0, NULL},
   {NM_ITEM, "About...",           NULL, 0, ~0, NULL},
   {NM_ITEM, NM_BARLABEL,	   NULL, 0, ~0, NULL},
   {NM_ITEM, "Quit",               "Q", 0, ~0, NULL},
  {NM_END, NULL, NULL, 0, ~0, NULL},
};

#define MENU_SAVE_CONFIG FULLMENUNUM(0,0,NOSUB)
#define MENU_ABOUT FULLMENUNUM(0,2,NOSUB)
#define MENU_QUIT FULLMENUNUM(0,4,NOSUB)

struct config config =			  /* default config */
{
   50,30, 5, 75, TRUE, FALSE
};

FILE *fhd;
static struct WBStartup *wbmsg = NULL;
BOOL ende = FALSE; /* globales Flag; wenn TRUE, dann wird d. Prg. beendet */

static struct EasyStruct AboutES =
   {sizeof(struct EasyStruct), 0, "About...",
   "Struct-Saver V"VERSION" ("DATE")\n© 1992 Thies Wellpott", "Ok"};

/********** Routinen **********/

void close_all(char *s, int err)
/* alles schließen, freigeben und Programm beenden */
{
   if (main_wd)
   {
      ClearMenuStrip(main_wd);
      CloseWindow(main_wd);
   }
   if (menu) FreeMenus(menu);
   if (gad_list) FreeGadgets(gad_list);
   if (vinfo) FreeVisualInfo(vinfo);

   if (GadToolsBase)    CloseLibrary(GadToolsBase);
   if (IntuitionBase)   CloseLibrary((struct Library *)IntuitionBase);

   if (s)                              /* wenn ein String angegeben ist, */
   {
      FILE *out;

      /* da stdout nicht definiert ist, wird hier ein Window geöffnet,
       * auf dem der String ausgegeben wird */
      if (wbmsg)
	 out = fopen("CON:20/20/260/100/Struct-Saver error", "w");
      else
	 out = fopen("*", "w");

      if (out)
      {
	 fputs(s, out);                /* diesen ausgeben */
	 fputs("!\n", out);            /* "!" + LF anhängen */
	 if (wbmsg)
	 {
	    fflush(out);    /* wenn von WB gestartet, File-Puffer leeren */
	    Delay(150);     /* und 3s warten */
	 }
	 fclose(out);
      } /* if (out) */
   } /* if (s) */

   exit(err);                          /* Programm beenden */
}


static void load_config(void)
/* loads config file, beeps screen if it can`t read the file */
{
   BPTR f;
   struct config cfg;

   if (f = Open("S:Struct-Saver.config", MODE_OLDFILE))
   {
      if (Read(f, &cfg, sizeof(struct config)) == sizeof(struct config))
	 config = cfg;		 /* loading ok, copy into real struct */
      else
	 DisplayBeep(NULL);
      Close(f);
   }
   else
      DisplayBeep(NULL);
}


static void save_config(void)
/* saves config file, displays error message if it can`t write the file */
{
   BPTR f;

   TITLE("Saving configuration...");
   config.wd_x = main_wd->LeftEdge;	     /* get window position */
   config.wd_y = main_wd->TopEdge;

   if (f = Open("S:Struct-Saver.config", MODE_NEWFILE))
   {
      if (Write(f, &config, sizeof(struct config)) == sizeof(struct config))
	 TITLE("Saving done.");
      else
      {
	 TITLE("Can`t write config-file!");
	 DisplayBeep(NULL);
      }
      Close(f);
   }
   else
   {
      TITLE("Can`t open config-file!");
      DisplayBeep(NULL);
   }
}


static void do_it(int what)
{
   int i;
   char s[20];
   struct Gadget *gad;
   struct Menu *m;
   ULONG lock;

   ModifyIDCMP(main_wd, NULL);               /* disable all input */
   for (i = config.wait_time; i > 0; i--)
   {
      sprintf(s, "Time left: %ds", i);       /* stop watch */
      TITLE(s);
      Delay(50);
   }

   if (fhd = fopen(((struct StringInfo *)fname_gdg->SpecialInfo)->Buffer, "w"))
   {
      switch (what)
      {
	 case GDG_GADGETS:
	    TITLE("Saving gadgets...");
#ifndef DEBUG
	    lock = LockIBase(0);
#endif
	    if (gad = IntuitionBase->ActiveWindow->FirstGadget)
	    {
	       save_gadget(gad);
	       TITLE("Saving done.");
	    }
	    else
	    {
	       TITLE("Window has no gadgets!");
	       DisplayBeep(NULL);
	    }
#ifndef DEBUG
	    UnlockIBase(lock);
#endif
	    break;
	 case GDG_MENUS:
	    TITLE("Saving menus...");
#ifndef DEBUG
	    lock = LockIBase(0);
#endif
	    if (m = IntuitionBase->ActiveWindow->MenuStrip)
	    {
	       save_menu(m);
	       TITLE("Saving done.");
	    }
	    else
	    {
	       TITLE("Window has no menus!");
	       DisplayBeep(NULL);
	    }
#ifndef DEBUG
	    UnlockIBase(lock);
#endif
	    break;
      } /* switch (what) */

      free_lists();                       /* free lists used by save_... */
      fclose(fhd);
   } /* if (fopen()) */
   else
   {
      TITLE("Can`t open file!");
      DisplayBeep(NULL);
   }

   ModifyIDCMP(main_wd, MY_IDCMP);
}


static void handleIDCMP(void)
{
   struct IntuiMessage *msg;
   ULONG class;
   UWORD code;
   struct Gadget *gad;

   TITLE(title_normal);
   while (msg = GT_GetIMsg(main_wd->UserPort))
   {
      class = msg->Class;
      code = msg->Code;
      gad = (struct Gadget *)(msg->IAddress);      /* might be odd!! */

      GT_ReplyIMsg(msg);

      switch (class)
      {
	 case IDCMP_ACTIVEWINDOW:
	    ActivateGadget(fname_gdg, main_wd, NULL);
	    break;
	 case IDCMP_CLOSEWINDOW:
	    ende = TRUE;
	    break;
	 case IDCMP_GADGETUP:
	    switch (gad->GadgetID)
	    {
	       case GDG_FNAME:
		  ActivateGadget(time_gdg, main_wd, NULL);
		  break;
	       case GDG_WAITTIME:
		  config.wait_time = ABS(((struct StringInfo *)
			   time_gdg->SpecialInfo)->LongInt);
		  ActivateGadget(linelen_gdg, main_wd, NULL);
		  break;
	       case GDG_LINELEN:
		  config.line_length = ABS(((struct StringInfo *)
			   linelen_gdg->SpecialInfo)->LongInt);
		  ActivateGadget(fname_gdg, main_wd, NULL);
		  break;
	       case GDG_USE_DEFINES:
		  config.use_defines ^= TRUE;
		  break;
	       case GDG_EXPANDED:
		  config.expanded ^= TRUE;
		  break;
	       case GDG_GADGETS:
		  do_it(GDG_GADGETS);
		  break;
	       case GDG_MENUS:
		  do_it(GDG_MENUS);
		  break;
	    }
	    break;
	 case IDCMP_MENUPICK:
	    while (code != MENUNULL)
	    {
	       switch (code)
	       {
		  case MENU_SAVE_CONFIG:
		     save_config();
		     break;
		  case MENU_ABOUT:
		     EasyRequest(main_wd, &AboutES, NULL, NULL);
		     break;
		  case MENU_QUIT:
		     ende = TRUE;
		     break;
	       }
	       code = (ItemAddress(menu, code))->NextSelect;
	    } /* while () */
	    break;
	 case IDCMP_REFRESHWINDOW:
	    GT_BeginRefresh(main_wd);
	    GT_EndRefresh(main_wd, TRUE);
	    break;
      } /* switch (class) */
   } /* while() */
}


static void mainloop(void)
{
   ULONG signal;
   ULONG idcmp_bit = 1L << main_wd->UserPort->mp_SigBit;

#define break_bit (SIGBREAKF_CTRL_C | SIGBREAKF_CTRL_D)

   while (!ende)
   {
      /* auf Aktion warten */
      signal = Wait(idcmp_bit | break_bit);

      if (signal & idcmp_bit)
	 handleIDCMP();
      if (signal & break_bit)
	 ende = TRUE;
   }
}


static void open_all(void)
/* alles Benötigte öffnen, allokieren, etc. */
{
   if (SysBase->SoftVer < 36)                /* Ist OS 2.0 vorhanden? */
      close_all("You need OS 2.0 or better", 20);

   if (!(IntuitionBase = (struct IntuitionBase *)OpenLibrary("intuition.library", 36)))
      close_all("No intuition.lib", 20);
   if (!(GadToolsBase = OpenLibrary("gadtools.library", 36)))
      close_all("No gadtools.lib", 20);
}


static void open_window(void)
{
   struct Screen *scr;
   struct Gadget *gad;
   struct NewGadget new_gdg;

   if (!(scr = LockPubScreen(NULL)))
      close_all("Can`t lock screen", 20);
   if (!(vinfo = GetVisualInfo(scr, TAG_DONE)))
   {
      UnlockPubScreen(NULL, scr);
      close_all("Can`t get visual info", 20);
   }
   if (!(gad = CreateContext(&gad_list)))
   {
      UnlockPubScreen(NULL, scr);
      close_all("Can`t create context", 20);
   }
   newmain_wd.FirstGadget = gad;

   new_gdg.ng_LeftEdge = scr->WBorLeft + 94;
   new_gdg.ng_TopEdge = scr->WBorTop + scr->Font->ta_YSize + 1 + INTERHEIGHT;
   new_gdg.ng_Width = 12 + 20*8;
   new_gdg.ng_Height = 13;
   new_gdg.ng_GadgetText = "Filename:";
   new_gdg.ng_GadgetID = GDG_FNAME;
   new_gdg.ng_TextAttr = &ta;
   new_gdg.ng_Flags = PLACETEXT_LEFT;
   new_gdg.ng_VisualInfo = vinfo;
   new_gdg.ng_UserData = NULL;
   if (!(fname_gdg = gad = CreateGadget(STRING_KIND, gad, &new_gdg,
	    GTST_MaxChars,80,  TAG_DONE)))
      goto no_gad;

   new_gdg.ng_TopEdge += 16;
   new_gdg.ng_Width = 12 + 3*8;
   new_gdg.ng_GadgetText = "Wait time:";
   new_gdg.ng_GadgetID = GDG_WAITTIME;
   if (!(time_gdg = gad = CreateGadget(INTEGER_KIND, gad, &new_gdg,
	    GTIN_Number,config.wait_time,  GTIN_MaxChars,2,  TAG_DONE)))
      goto no_gad;

   new_gdg.ng_LeftEdge += 12 + 3*8;
   new_gdg.ng_Width = 0;
   new_gdg.ng_GadgetText = "seconds";
   new_gdg.ng_GadgetID = 0;
   new_gdg.ng_Flags = PLACETEXT_RIGHT;
   if (!(gad = CreateGadget(TEXT_KIND, gad, &new_gdg, TAG_DONE)))
      goto no_gad;

   new_gdg.ng_LeftEdge -= 12 + 3*8;
   new_gdg.ng_TopEdge += 16;
   new_gdg.ng_Width = 12 + 4*8;
   new_gdg.ng_GadgetText = "Line len:";
   new_gdg.ng_GadgetID = GDG_LINELEN;
   new_gdg.ng_Flags = PLACETEXT_LEFT;
   if (!(linelen_gdg = gad = CreateGadget(INTEGER_KIND, gad, &new_gdg,
	    GTIN_Number,config.line_length,  GTIN_MaxChars,3,  TAG_DONE)))
      goto no_gad;

   new_gdg.ng_TopEdge += 20;
   new_gdg.ng_LeftEdge -= 16;
   new_gdg.ng_Width = 0;
   new_gdg.ng_GadgetText = "Options:";
   new_gdg.ng_GadgetID = 0;
   if (!(gad = CreateGadget(TEXT_KIND, gad, &new_gdg, TAG_DONE)))
      goto no_gad;

   new_gdg.ng_Width = 20;
   new_gdg.ng_GadgetText = "use defines";
   new_gdg.ng_GadgetID = GDG_USE_DEFINES;
   new_gdg.ng_Flags = PLACETEXT_RIGHT;
   if (!(gad = CreateGadget(CHECKBOX_KIND, gad, &new_gdg,
	    GTCB_Checked,config.use_defines,  TAG_DONE)))
      goto no_gad;

   new_gdg.ng_TopEdge += 12;
   new_gdg.ng_GadgetText = "expanded";
   new_gdg.ng_GadgetID = GDG_EXPANDED;
   if (!(gad = CreateGadget(CHECKBOX_KIND, gad, &new_gdg,
	    GTCB_Checked,config.expanded,  TAG_DONE)))
      goto no_gad;

   new_gdg.ng_TopEdge += 20;
   new_gdg.ng_LeftEdge = scr->WBorLeft + 38;
   new_gdg.ng_Width = 80;
   new_gdg.ng_GadgetText = "Gadgets";
   new_gdg.ng_GadgetID = GDG_GADGETS;
   new_gdg.ng_Flags = PLACETEXT_IN;
   if (!(gad = CreateGadget(BUTTON_KIND, gad, &new_gdg, TAG_DONE)))
      goto no_gad;

   new_gdg.ng_LeftEdge += 80+38;
   new_gdg.ng_GadgetText = "Menus";
   new_gdg.ng_GadgetID = GDG_MENUS;
   if (!(gad = CreateGadget(BUTTON_KIND, gad, &new_gdg, TAG_DONE)))
      goto no_gad;

   /*** init menus ***/
   if (!(menu = CreateMenus(menu_data, GTMN_FullMenu,TRUE, TAG_DONE)))
   {
      UnlockPubScreen(NULL, scr);
      close_all("Can`t create menus", 20);
   }
   if (!LayoutMenus(menu, vinfo, TAG_DONE))
   {
      UnlockPubScreen(NULL, scr);
      close_all("Can`t layout menus", 20);
   }

   /*** open window ***/
   if (!(main_wd = OpenWindowTags(&newmain_wd, WA_Left,config.wd_x,
	    WA_Top,config.wd_y,  WA_InnerWidth,274,  WA_InnerHeight,105,
	    WA_AutoAdjust,TRUE,  TAG_DONE)))
   {
      UnlockPubScreen(NULL, scr);
      close_all("Can`t open window", 20);
   }

   UnlockPubScreen(NULL, scr);
   GT_RefreshWindow(main_wd, NULL);
   SetMenuStrip(main_wd, menu);
   return;

no_gad:
   UnlockPubScreen(NULL, scr);
   close_all("Can`t create gadget", 20);
}


void main(int argc, char *argv[])
{
   if (argc == 0)
      wbmsg = (struct WBStartup *)argv;
   open_all();
   load_config();
   open_window();

   mainloop();

   close_all(NULL, 0);
}

