/************************************************
***  Complex example program for INI library  ***
***          © 1999 by Basty/Seasons          ***
***   This program demonstrates the powerful  ***
***       functions of the INI library        ***
***               C/C++ version               ***
***  StormC 3.0 DSK was used for development  ***
************************************************/

#include <stdlib.h>
#include <stdio.h>
#include <string.h>

#include <exec/types.h>
#include <exec/libraries.h>
#include <exec/lists.h>
#include <exec/nodes.h>
#include <libraries/dosextens.h>
#include <libraries/ini_lib.h>
#include <proto/exec.h>
#include <proto/intuition.h>
#include <proto/graphics.h>
#include <proto/ini.h>
#include <intuition/intuitionbase.h>
#include <intuition/screens.h>
#include <intuition/intuition.h>
#include <graphics/rastport.h>

#include "INI_Example.h"

#define DefaultScrTitle "Example INI Test Screen © 1999 by Basty/Seasons"
#define DefaultWinTitle "Example INI Test Window © 1999 by Basty/Seasons"

struct INIScreen
{
  struct MinList iniscr_ScrList;
};

struct ScreenList
{
  struct MinNode iniscr_MinNode;
  struct Screen *iniscr_ScreenPtr;
  struct MinList iniscr_WinList;
};

struct ScreenWinList
{
  struct MinNode scrwin_MinNode;
  struct Window *scrwin_WindowPtr;
};

struct iniLibBase *INIBase;
struct IntuitionBase *IntuitionBase;
struct GfxBase *GfxBase;
struct iniFile *DefINIFile;
struct ScreenList WBScreen =
{
  {NULL, NULL},
  NULL,
  {(struct MinNode *) &(WBScreen.iniscr_WinList.mlh_Tail),NULL,(struct MinNode *) &(WBScreen.iniscr_WinList.mlh_Head)}
};

struct INIScreen Screens =
{
  {(struct MinNode *) &(WBScreen.iniscr_MinNode.mln_Succ),NULL,(struct MinNode *) &(WBScreen.iniscr_MinNode.mln_Succ)}
};

UWORD DefaultColTab[256] =
{
  0x000, 0xFFF, 0x777, 0xCCC,
  0x444, 0x555, 0x666, 0x888,
  0x111, 0x222, 0xAAA, 0x333,
  0x999, 0xDDD, 0xEEE, 0xBBB
};

struct NewScreen NewScreenStr =
{
  0, 0, /* LeftEdge, TopEdge */
  640, 256, /* Width, Height */
  4, /* Depth */
  1, 0, /* DetailPen, BlockPen */
  0x8000, /* ViewModes */
  CUSTOMSCREEN, /* Type */
  0, 0, /* TextAttr, DefaultTitle, */
  0, 0 /* Gadgets, BitMap */
};

struct NewWindow NewWindowStr =
{
  0, 0, /* LeftEdge, TopEdge */
  640, 256, /* Width, Height */
  1, 0, /* DetailPen, BlockPen */
  0x200, 0x100F, /* IDCMPFlags, Flags */
  0, 0, /* Gadget, Image */
  0, 0, 0, /* Title, Screen, BitMap */
  0, 0, /* MinWidth, MinHeight */
  640, 256, /* MaxWidth, MaxHeight */
  CUSTOMSCREEN /* Type */
};

unsigned char ScreenContext[16] = "Screen";
unsigned char WindowContext[16] = "Window";

void InitNode (struct List *NodeList)
{
  NodeList->lh_Head = (struct Node *) &(NodeList->lh_Tail);
  NodeList->lh_Tail = (struct Node *) NULL;
  NodeList->lh_TailPred = (struct Node *) &(NodeList->lh_Head);
}

int ReadINIScreens (struct iniFile *iniFile)
{
  struct iniContext *CurrentContext;
  struct ScreenList *ScrStrBuf;
  struct Screen *ScreenStr;
  unsigned long ScrNumCols;
  UWORD ScrColorTable[256];

  /* Set FAIL error mark */

  int rc = 20;

  /* Start with first screen */

  unsigned long CurrentScrNum = 1;

  for (;;)
  {

    /* Convert screen number to ASCII using standard decimal format */

    iniIntToStr ( ScreenContext+6, CurrentScrNum, INI_FORMAT_DEC, 0L, ' ');

    /* Increment screen counter */

    CurrentScrNum++;

    /* Search for context and check if found */

    if (!(CurrentContext = iniFindContext ( iniFile, ScreenContext, 0L)))
    {
      /* Found? No -> end of search */
      rc = 0;
      break;
    }

    /* Get left edge value or use default value of zero. */

    NewScreenStr.LeftEdge = iniReadLong ( iniFile, ScreenContext,
      "LeftEdge", 0L, 0L);

    /* Get top edge value or use default value of zero. */

    NewScreenStr.TopEdge = iniReadLong ( iniFile, ScreenContext,
      "TopEdge", 0L, 0L);

    /* Get width value or default width of 640 pixels. */

    NewScreenStr.Width = iniReadLong ( iniFile, ScreenContext,
      "Width", 640L, 0L);

    /* Get height value or default height of 256 pixels. */

    NewScreenStr.Height = iniReadLong ( iniFile, ScreenContext,
      "Height", 256L, 0L);

    /* Get depth value or default of 4 bitplanes (16 colors). */

    NewScreenStr.Depth = iniReadLong ( iniFile, ScreenContext,
      "Depth", 4L, 0L);

    /* Get view modes value or default view mode of HIRES (0x8000). */

    NewScreenStr.ViewModes = iniReadLong ( iniFile, ScreenContext,
      "ViewModes", 0x8000L, 0L);

    /* Get screen default title or use default string */

    NewScreenStr.DefaultTitle = iniReadStr ( iniFile, ScreenContext,
      "Title", DefaultScrTitle, 0L);

    /* Copy all 256 default colors to updated color table */

    memcpy (ScrColorTable,DefaultColTab,256);
    ScrNumCols = (NewScreenStr.Depth < 8 ? 1L<<NewScreenStr.Depth : 256L);

    /* Read all necessary color table entries into color table */

    iniReadWordA ( iniFile, ScreenContext, "ColorTable", (WORD *) &ScrColorTable,
      ScrNumCols, 0L);

    /* We need storage buffers for our screens */

    if (!(ScrStrBuf = (struct ScreenList *) iniAllocPMem (sizeof(struct ScreenList))))
    {
      /* Deallocate name string buffer. Don't forget ! */

      iniFreeNameStr (NewScreenStr.DefaultTitle);
      break;
    }

    /* Add them to list */

    AddTail ((struct List *) &Screens.iniscr_ScrList, (struct Node *) ScrStrBuf);

    /* Open the screen */

    ScreenStr = OpenScreen (&NewScreenStr);

    ScrStrBuf->iniscr_ScreenPtr = ScreenStr;

    /* Initialize window node list */

    InitNode ((struct List *) &(ScrStrBuf->iniscr_WinList));

    if (ScreenStr == NULL)
    {
      /* Deallocate name string buffer. Don't forget */

      iniFreeNameStr (NewScreenStr.DefaultTitle);
      break;
    }

    LoadRGB4 (&ScreenStr->ViewPort, ScrColorTable, ScrNumCols);
  }

  return ( rc );
}

int ReadINIWindows (struct iniFile *iniFile)
{
  struct iniContext *CurrentContext;
  struct ScreenWinList *WinStrBuf;
  struct Window *WindowStr;
  struct ScreenList *scr;
  unsigned long ScreenNum,i;

  /* Set FAIL error mark */

  int rc = 20;

  /* Start with first window */

  unsigned long CurrentWinNum = 1;

  for (;;)
  {

    /* Convert window number to ASCII using standard decimal format */

    iniIntToStr ( WindowContext+6, CurrentWinNum, INI_FORMAT_DEC, 0L, ' ');

    /* Increment window counter */

    CurrentWinNum++;

    /* Search for context and check if found */

    if (!(CurrentContext = iniFindContext ( iniFile, WindowContext, 0L)))
    {
      /* Found? No -> end of search */
      rc = 0;
      break;
    }

    /* Get left edge value or use default value of zero. */

    NewWindowStr.LeftEdge = iniReadLong ( iniFile, WindowContext,
      "LeftEdge", 0L, 0L);

    /* Get top edge value or use default value of zero. */

    NewWindowStr.TopEdge = iniReadLong ( iniFile, WindowContext,
      "TopEdge", 0L, 0L);

    /* Get width value or default width of 640 pixels. */

    NewWindowStr.Width = iniReadLong ( iniFile, WindowContext,
      "Width", 640L, 0L);

    /* Get height value or default height of 256 pixels. */

    NewWindowStr.Height = iniReadLong ( iniFile, WindowContext,
      "Height", 256L, 0L);

    /* Get IDCMP flags or default to 0x20000 (borderless). */

    NewWindowStr.IDCMPFlags = iniReadLong ( iniFile, WindowContext,
      "IDCMP", 0x20000, 0L);

    /* Get window flags or default to 0x100F (Autoactivate + all gadgets) */

    NewWindowStr.Flags = iniReadLong ( iniFile, WindowContext,
      "Flags", 0x100FL, 0L);

    /* Get screen default title or use default string */

    NewWindowStr.Title = iniReadStr ( iniFile, WindowContext,
      "Title", DefaultWinTitle, 0L);

    /* Get min width value or default min width of 32 pixels. */

    NewWindowStr.MinWidth = iniReadLong ( iniFile, WindowContext,
      "MinWidth", 32L, 0L);

    /* Get max width value or default max width of 640 pixels. */

    NewWindowStr.MaxWidth = iniReadLong ( iniFile, WindowContext,
      "MaxWidth", 640L, 0L);

    /* Get min height value or default min height of 32 pixels. */

    NewWindowStr.MinHeight = iniReadLong ( iniFile, WindowContext,
      "MinHeight", 32L, 0L);

    /* Get max height value or default max height of 256 pixels. */

    NewWindowStr.MaxHeight = iniReadLong ( iniFile, WindowContext,
      "MaxHeight", 256L, 0L);

    /* Get screen number or use default screen (first screen). */

    ScreenNum = iniReadLong ( iniFile, WindowContext,
      "Screen", 1L, 0L);

    /* Find wanted screen */

    scr = (struct ScreenList *) &(Screens.iniscr_ScrList.mlh_Head);

    for (i = ScreenNum+1 ; i > 0 ; i--)
    {

      /* Next screen */
      scr = (struct ScreenList *) scr->iniscr_MinNode.mln_Succ;

      if (scr->iniscr_MinNode.mln_Succ == NULL)
      {
        /* Yes -> done */
        rc = 0;
        break;
      }
    }

    /* Check if last entry was found */

    if (rc == 0)
    {
      rc = 20;
      continue;
    }

    /* Copy screen pointer */

    NewWindowStr.Screen = scr->iniscr_ScreenPtr;

    /* We need storage buffers for our windows */

    if (!(WinStrBuf = (struct ScreenWinList *) iniAllocPMem (sizeof(struct ScreenWinList))))
    {
      /* Deallocate name string buffer. Don't forget ! */

      iniFreeNameStr (NewWindowStr.Title);
      break;
    }

    /* Add them to list */

    AddTail ((struct List *) &scr->iniscr_WinList, (struct Node *) WinStrBuf);

    /* Open the window */

    WindowStr = OpenWindow (&NewWindowStr);

    if ((WinStrBuf->scrwin_WindowPtr = WindowStr) == NULL)
    {
      /* Deallocate name string buffer. Don't forget */

      iniFreeNameStr (NewWindowStr.Title);
      break;
    }
  }

  return ( rc );
}

void CloseIntStuff(struct INIScreen *scrlist)
{
  struct ScreenList *scr;
  struct ScreenWinList *win,*oldwin;
	char *Title;

  for (;;)
  {

    /* Next screen */

    scr = (struct ScreenList *) scrlist->iniscr_ScrList.mlh_Head;

    /* Have we reached last entry? */

    if (scr->iniscr_MinNode.mln_Succ == NULL)
      /* Yes -> done */
      break;

    /* Get window pointers */

    oldwin = (struct ScreenWinList *) &(scr->iniscr_WinList.mlh_Head);
    win = (struct ScreenWinList *) oldwin->scrwin_MinNode.mln_Succ; 
    for (;;)
    {
      /* Get next window pointer */

      oldwin = win;
      if (win->scrwin_MinNode.mln_Succ == NULL)
        /* Yes -> done */
        break;

      /* Get window to close and check if valid pointer */

      if ( win->scrwin_WindowPtr != NULL)
      {
        /* Save window title for dealloc */

        Title = win->scrwin_WindowPtr->Title;

        /* Pointer valid? -> Close the damn window! */

        CloseWindow ( win->scrwin_WindowPtr );

        /* Deallocate name string buffer. Don't forget */

        iniFreeNameStr (Title);

      }

      win = (struct ScreenWinList *) win->scrwin_MinNode.mln_Succ;

      /* Deallocate memory using ini.library */

      iniFreePMem ( oldwin, sizeof(struct ScreenWinList));

   };

    /* Remove node entry before dealloc */

    Remove ( (struct Node *) scr );

    if (scr->iniscr_ScreenPtr == WBScreen.iniscr_ScreenPtr)
      continue;

    /* Save screen title for dealloc */

    Title = scr->iniscr_ScreenPtr->Title;

    /* Close screen */

    CloseScreen (scr->iniscr_ScreenPtr );

    /* Deallocate name string buffer. Don't forget */

    iniFreeNameStr (Title);

    /* Deallocate memory */

    iniFreePMem ( scr, sizeof(struct ScreenList));
  };
}

void main()
{
  /* Set FAIL error mark */

  int rc = 20;

  /* Load v31.00 of ini.library */

  if (!( INIBase = (struct iniLibBase *) OpenLibrary("ini.library",31L)))
    exit ( rc );

  /* Load intuiton.library (any version) */

  IntuitionBase = (struct IntuitionBase *) OpenLibrary ("intuition.library",0L);

  /* Load graphics.library (any version) */

  GfxBase = (struct GfxBase *) OpenLibrary("graphics.library",0L);

  /* Initialize first screen node */

  WBScreen.iniscr_MinNode.mln_Succ = (struct MinNode *) &(Screens.iniscr_ScrList.mlh_Tail);
  WBScreen.iniscr_MinNode.mln_Pred = (struct MinNode *) &(Screens.iniscr_ScrList.mlh_Head);

  /* Get first screen (WB screen) */

  WBScreen.iniscr_ScreenPtr = IntuitionBase->FirstScreen;

  /* Open :Example.INI INI file/create it */

  if (!(DefINIFile = iniOpenDefault( DefaultINI, ":Example.INI", sizeof(DefaultINI))))
    goto CloseINI;

  rc = ReadINIScreens(DefINIFile);

  if (rc != 0)
    goto CloseINI;

  rc = ReadINIWindows(DefINIFile);

  if (rc != 0)
    goto CloseINI;

  /* Wait for mouse button */

  while (*( unsigned char *)0xBFE001 & 0x40);

CloseINI:

/* We want to deallocate all screens */

  CloseIntStuff(&Screens);
  
  iniClose ( DefINIFile );

  /* Close graphics.library */

  CloseLibrary ( (struct Library *) GfxBase );

  /* Close intuition.library */

  CloseLibrary ( (struct Library *) IntuitionBase );

  /* Close INI library */

  CloseLibrary ( (struct Library *) INIBase );

  /* Exit to DOS */

  exit ( rc );
}
