/* PlotMap
 *
 * (c) 1992 Thies Wellpott
 *
 * see PlotMap.txt for more information
 *
 */

#include "PlotMap.h"

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

extern struct config config;
extern struct Rectangle rect;
extern ULONG scr_displayid, scr_overscan;
extern UWORD centerx, centery, half_scr_width;
extern double m_vfactor;
extern struct Window *coord_wd;
extern struct NewMenu menu_data[];

//extern void handleARexx(void);
extern void handleIDCMP(void), handlecoordIDCMP(void), get_config(void);
extern void open_coord_window(void), close_coord_window(void);
extern void set_vfac_menu(void);

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

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

//struct RxsLib *RexxSysBase = NULL;
struct IntuitionBase *IntuitionBase = NULL;
struct GfxBase *GfxBase = NULL;
struct Library *GadToolsBase = NULL;

//struct MsgPort *ARexxPort = NULL;

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

UBYTE *screen_title = "PlotMap V"VERSION" (c) 1992 Thies Wellpott";
static struct Process *my_process;
struct RastPort *rp;
struct ViewPort *vp;
struct Screen *screen = NULL;
struct Window *main_wd = NULL, *old_wdptr = NULL;
static struct TmpRas tmpras;
PLANEPTR tmpras_mem = NULL;

struct TextAttr myfont = { NULL, 0, 0x00, 0x00 }; /* wird init. */
static UWORD pens[] = {0,1,1,1,2,~0};        /* for nicer 3D-look */

struct point *loadmap_buffer = NULL;
ULONG loadmap_buffersize;
struct mapinfo map[] =
{                                            /* map information */
   { "coast.pnt",   74967, 208,    0, NULL, NULL },
   { "island.pnt",  35171, 344,  620, NULL, NULL },
   { "country.pnt", 22359, 301,  208, NULL, NULL },
   { "state.pnt",    2259, 111,  509, NULL, NULL },
   { "lake.pnt",    15118, 103,  964, NULL, NULL },
   { "river.pnt",   28194, 196, 1067, NULL, NULL }
};
struct arc *segment_mem = NULL;

UWORD chip mouse_wait_data[] =
{
   0x0000,0x0000,             /* OS 2.0 busy pointer */
   0x0400,0x07C0, 0x0000,0x07C0, 0x0100,0x0380, 0x0000,0x07E0,
   0x07C0,0x1FF8, 0x1FF0,0x3FEC, 0x3FF8,0x7FDE, 0x3FF8,0x7FBE,
   0x7FFC,0xFF7F, 0x7EFC,0xFFFF, 0x7FFC,0xFFFF, 0x3FF8,0x7FFE,
   0x3FF8,0x7FFE, 0x1FF0,0x3FFC, 0x07C0,0x1FF8, 0x0000,0x07E0,
   0x0000,0x0000,
};

UWORD chip mouse_cross_data[] =
{
   0x0000,0x0000,             /* Zielkreuz */
   0x0200,0x0000, 0x0200,0x0000, 0x0200,0x0000, 0x0200,0x0000,
   0x0200,0x0200, 0x0000,0x0000, 0xF8F8,0x0880, 0x0000,0x0000,
   0x0200,0x0200, 0x0200,0x0000, 0x0200,0x0000, 0x0200,0x0000,
   0x0200,0x0000,
   0x0000,0x0000,
};

APTR vinfo = NULL;
struct Menu *menu = NULL;

static struct EasyStruct flood_error_ES =
   {sizeof(struct EasyStruct), 0, "PlotMap error",
   "Out of chip memory!\nFlood fill option disabled.", "Ok"};

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

void close_all(char *s, int err)
/* alles schließen, freigeben und Programm beenden */
{
   int i;
//   struct Message *msg;

   if (old_wdptr) my_process->pr_WindowPtr = old_wdptr;

   close_coord_window();

   if (tmpras_mem) FreeRaster(tmpras_mem, screen->Width, screen->Height);
   if (loadmap_buffer) FreeMem(loadmap_buffer, loadmap_buffersize);
   if (segment_mem) FreeMem(segment_mem, NSEGS*sizeof(struct arc));
   for (i = 0; i < NUM_MAPS; i++)         /* free map point buffers */
   {
      if (map[i].pt)
         FreeMem(map[i].pt, map[i].numpts * sizeof(struct point));
   }

   if (main_wd)
   {
      ClearMenuStrip(main_wd);
      CloseWindow(main_wd);
   }
   if (menu) FreeMenus(menu);
   if (vinfo) FreeVisualInfo(vinfo);
   if (screen) CloseScreen(screen);

//   Forbid();      /* damit keine neuen Msg an die Ports ankommen */
//   if (ARexxPort)
//   {
//      RemPort(ARexxPort);                 /* it is a public one! */
//      while (msg = GetMsg(ARexxPort)) ReplyMsg(msg);
//      DeleteMsgPort(ARexxPort);
//   }
//   Permit();      /* die Ports sind entfernt, andere Tasks dürfen wieder */

   if (GadToolsBase)    CloseLibrary(GadToolsBase);
   if (GfxBase)         CloseLibrary((struct Library *)GfxBase);
   if (IntuitionBase)   CloseLibrary((struct Library *)IntuitionBase);
//   if (RexxSysBase)     CloseLibrary((struct Library *)RexxSysBase);

   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/PlotMap error/CLOSE/WAIT", "w");
      else
         out = fopen("*", "w");

      if (out)
      {
         fputs(s, out);                /* diesen ausgeben */
         fputs("!\n", out);            /* "!" + LF anhängen */
         fclose(out);
      } /* if (out) */
   } /* if (s) */

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


BPTR get_map_fhd(UWORD num)
/* tries to open map num, searches in different dirs, => NULL: not found */
{
   BPTR fhd;
   char str[30];

   /* first look in current dir, if not found look in "PM_DATA:" */
   if (!(fhd = Open(map[num].mapname, MODE_OLDFILE)))
   {
      strcpy(str, "PM_DATA:");
      strcat(str, map[num].mapname);
      fhd = Open(str, MODE_OLDFILE);
   }

   return(fhd);
}


static void get_maps(void)
/* read map files */
{
   UWORD i;
   LONG num_to_read;
   BPTR fhd;

   TITLE_MSG("Reading map files");

   for (i = 0; i < NUM_MAPS; i++)
   {
      if (config.loadmap[i])                       /* nur wenn sie soll */
      {
         num_to_read = map[i].numpts * sizeof(struct point);
         if (map[i].pt = AllocMem(num_to_read, 0))
         {
            if (fhd = get_map_fhd(i))
            {                             /* I use DOS because of speed */
               if (Read(fhd, map[i].pt, num_to_read) != num_to_read)
               {
                  Close(fhd);
                  close_all("Can`t read mapfile", 20);
               }
               Close(fhd);
            }
            else
               close_all("Can`t open mapfile",20);
         }
         else
         {
            config.loadmap[i] = FALSE;
            TITLE_ERROR("Out of memory, can`t load the whole map!");
         } /* if (AllocMem()) */
      } /* if (config.loadmap[]) */
   } /* for(i) */
}


static void get_limits(void)             /* find limits of each segment */
/* read limits file */
{
   UWORD i, first_seg;
   BPTR fhd;
   struct arc *seg;

   if (!(segment_mem = seg = AllocMem(NSEGS*sizeof(struct arc), 0)))
      close_all("Out of memory", 20);

   for (i = 0; i < NUM_MAPS; i++)         /* link segment array to maps */
   {
      first_seg = map[i].first_seg;
      map[i].seg = (struct arc *) &(seg[first_seg].lat_min);
   }

   if (fhd = Open("map.limits.bin", MODE_OLDFILE))
   {
      if (Read(fhd, seg, NSEGS*sizeof(struct arc)) != NSEGS*sizeof(struct arc))
      {
         Close(fhd);
         close_all("Can`t read limits file, use PM_gen_limits", 20);
      }
      Close(fhd);
   }
   else
      close_all("Can`t open limits file, use PM_gen_limits", 20);
}


void get_loadmap_buffer(void)
/* get buffer to hold the largest segment (of all maps) */
{
   UWORD m,s, anz,max=0;
   struct arc *seg;
   BOOL get_buf = FALSE;

   for (m = 0; m < NUM_MAPS; m++)
   {
      if (!config.loadmap[m])             /* do not load whole map? */
         get_buf = TRUE;                  /* yes, so allocate load buffer */

      seg = map[m].seg;
      for (s = 0; s < map[m].nsegs; s++)
      {
         anz = seg[s].last - seg[s].first + 1;
         if (anz > max)
            max = anz;
      }
   }

   if (get_buf)                           /* allocate only if needed */
   {
      loadmap_buffersize = max * sizeof(struct point);
      if (!(loadmap_buffer = AllocMem(loadmap_buffersize, 0)))
         close_all("Out of memory", 20);
   }
}


static struct MenuItem *fix_color(struct MenuItem *ip, int anz, int w)
{
   int i,j;
   struct MenuItem *sp;

   for (i = 0; i < anz; i++)
   {
      sp = ip->SubItem;
      ((struct Image *)sp->ItemFill)->LeftEdge -= w;
      for (j = 0; j < 16; j++)
      {
         sp->Flags &= ~HIGHCOMP;       /* set HIGHBOX for all subitems */
         sp->Flags |= HIGHBOX;
         sp->Width -= w;               /* adjust width */
         sp = sp->NextItem;
      }
      ip = ip->NextItem;
   } /* for (i) */
   return(ip);
}


static void open_screen(void)
/* opens screen and window */
{
   int w;
   struct MenuItem *ip;

   if (!(screen = OpenScreenTags(NULL,
         SA_Depth,config.scr_depth,  SA_Overscan,scr_overscan,
         SA_Pens,pens,  SA_Font,&myfont,  SA_DisplayID,scr_displayid,
         SA_Title,screen_title,  TAG_DONE)))
      close_all("Can`t open screen", 20);

   if (!(vinfo = GetVisualInfo(screen, TAG_DONE)))
      close_all("Can`t get visual info", 20);
   if (!(menu = CreateMenus(menu_data, GTMN_FullMenu,TRUE, TAG_DONE)))
      close_all("Can`t create menus", 20);
   if (!LayoutMenus(menu, vinfo, TAG_DONE))
      close_all("Can`t layout menus", 20);

   /*         Prefs,    Colors,   Palette,  BARLABEL, background */
   ip = menu->NextMenu->NextMenu->FirstItem->NextItem->NextItem;
   /* there is an error in LayoutMenus(): the first image has a wrong
    * LeftEdge (about 20 pixel too big). Because of the wrong item the
    * width of all items is too big. I correct it all manually.
    * Also, it is not possible to select HIGHBOX selection. I disable
    * HIGHCOMP and set HIGHBOX after the call of LayoutMenus */
   w = ((struct Image *)ip->SubItem->ItemFill)->LeftEdge -
            ((struct Image *)ip->SubItem->NextItem->ItemFill)->LeftEdge;

   ip = fix_color(ip, 2, w)->NextItem;
   ip = fix_color(ip, NUM_MAPS, w)->NextItem;
   ip = fix_color(ip, 3, w)->NextItem;
   fix_color(ip, 4, w);

   if (!(main_wd = OpenWindowTags(NULL,
            WA_Left,0,  WA_Top,0,  WA_Width,screen->Width,
            WA_Height,screen->Height,  WA_CustomScreen,screen,
            WA_Flags,WFLG_NOCAREREFRESH | WFLG_SMART_REFRESH |
               WFLG_BACKDROP | WFLG_BORDERLESS | WFLG_ACTIVATE,
            TAG_DONE)))
      close_all("Can`t open window", 20);
   old_wdptr = my_process->pr_WindowPtr;     /* DOS requesters on my */
   my_process->pr_WindowPtr = main_wd;       /* screen, please */

   rp = main_wd->RPort;
   vp = &(screen->ViewPort);

   if (config.flood_fill)
   {
      if (tmpras_mem = AllocRaster(screen->Width, screen->Height))
      {
         InitTmpRas(&tmpras, tmpras_mem, RASSIZE(screen->Width, screen->Height));
         rp->TmpRas = &tmpras;               /* link it to the RastPort */
      }
      else
      {                             /* disable Flood fill and Fill oceans */
         ItemAddress(menu, FULLMENUNUM(4,1,NOSUB))->Flags &= ~ITEMENABLED;
         ItemAddress(menu, FULLMENUNUM(4,10,NOSUB))->Flags &= ~ITEMENABLED;
         EasyRequest(main_wd, &flood_error_ES, NULL, NULL);
      }
   }
   else
   {                                /* disable Flood fill and Fill oceans */
      ItemAddress(menu, FULLMENUNUM(4,1,NOSUB))->Flags &= ~ITEMENABLED;
      ItemAddress(menu, FULLMENUNUM(4,10,NOSUB))->Flags &= ~ITEMENABLED;
   }

   LoadRGB4(vp, config.colormap, 16);              /* set colors */
   SetAPen(rp, 1);
   SetBPen(rp, 0);
   SetDrMd(rp, JAM2);

   half_scr_width = screen->Width / 2;
   centerx = screen->Width / 2;
   centery = screen->Height / 2;
   m_vfactor = (double)(screen->Height) / 5.90 / config.vfac;
}


static void usage(void)
/* Usage ausgeben, nur vom CLI aus möglich */
{
//   FILE *out;
//
//   if (out = fopen("*", "w"))       /* stdout ist nicht geöffnet!! */
//   {
//      fputs("\2330 p\2334m", out);  /* CRSR aus, unterstreichen */
//      fputs(&version[7], out);
//      fputs("\2330m by Thies Wellpott\n\n\
//Usage: PlotMap [???] [???]\n", out);
//
//      fputs("\n\233 p", out);    /* CRSR wieder ein */
//   } /* if (fopen) */
//
//   close_all(NULL, 5);           /* Programm beenden */
}


static void handle_args(int argc, char *argv[])
/* Behandelt die dem Prog. übergebenen Parameter */
{
//   int i;
//
   if (argc == 0)                               /* von WB gestartet? */
      wbmsg = (struct WBStartup *)argv;
//   else if ((argc == 2) && (*argv[1] == '?'))   /* Hilfe erwünscht? */
//      usage();
//   else
//      for (i = 1; i < argc; i++)                /* Parameter ermitteln */
//      {
//      } /* for (i) */
}


static void open_all(void)
/* get all I need (libs, mem, ports, etc.) */
{
   if (SysBase->SoftVer < 37)                /* Ist OS 2.04 vorhanden? */
      close_all("You need OS 2.04 or better for this version", 20);

//   Forbid();         /* FindPort() muß zw. Forbid() und Permit() stehen */
//   if (FindPort(REXXPORTNAME))      /* existiert der ARexx-Port schon? */
//   {
//      Permit();
//      close_all("PlotMap already running!", 5);
//   }
//   else
//   {
//      if (!(ARexxPort = CreateMsgPort())) /* nein, also PlotMap starten */
//      {
//         Permit();
//         close_all("Can`t create port", 20);
//      }
//      ARexxPort->mp_Node.ln_Pri = 0;
//      ARexxPort->mp_Node.ln_Name = REXXPORTNAME;
//      AddPort(ARexxPort);                 /* make it public */
//      Permit();
//   }

//   if (!(RexxSysBase = (struct RxsLib *)OpenLibrary("rexxsyslib.library", 36)))
//      close_all("No rexxsyslib.lib", 20);
   if (!(IntuitionBase = (struct IntuitionBase *)OpenLibrary("intuition.library", 37)))
      close_all("No intuition.lib", 20);
   if (!(GfxBase = (struct GfxBase *)OpenLibrary("graphics.library", 37)))
      close_all("No graphics.lib", 20);
   if (!(GadToolsBase = OpenLibrary("gadtools.library", 37)))
      close_all("No gadtools.lib", 20);
}


static void mainloop(void)
{
   ULONG signal;
//   ULONG arexx_bit = 1L << ARexxPort->mp_SigBit,   /* get signal bits */
   ULONG idcmp_bit = 1L << main_wd->UserPort->mp_SigBit, coord_bit;

#define break_bit (SIGBREAKF_CTRL_C | SIGBREAKF_CTRL_D)

   while (!ende)
   {
      coord_bit = 0;
      if (coord_wd)
         coord_bit = 1L << coord_wd->UserPort->mp_SigBit;

      /* auf Aktion warten */
      signal = Wait(idcmp_bit | coord_bit | break_bit);

      if (signal & idcmp_bit)
         handleIDCMP();
      if (signal & coord_bit)
         handlecoordIDCMP();
//      if (signal & arexx_bit)
//         handleARexx();
      if (signal & break_bit)
         ende = TRUE;
   } /* while (!ende) */
}


void main(int argc, char *argv[])
{
   my_process = (struct Process *)FindTask(NULL);
   handle_args(argc, argv);
   open_all();
   get_config();
   open_screen();
   MOUSE_WAIT;
   get_maps();
   get_limits();
   get_loadmap_buffer();

   if (config.coord_window) open_coord_window();
   TITLE_NORMAL;
   MOUSE_NORMAL;
   IDCMP(NORMAL_IDCMP);
   SetMenuStrip(main_wd, menu);
   set_vfac_menu();
   mainloop();

   close_all(NULL, 0);
}

