#define MAIN_C

#include <dos/dostags.h>
#include <exec/memory.h>
#include <exec/semaphores.h>

#include <workbench/workbench.h>
#include <intuition/intuition.h>
#include <proto/icon.h>

#include "common/extern.h"
#include "common/Grab_mcc.h"
#include "Config.h"

#define POOL_SIZE   200000

extern struct ObjApp *CreateApp (void);
extern struct SubTask *SpawnNetTask (char *name, BPTR func, APTR data);
extern struct preferences *LoadPrefs (void);

struct MsgPort *myport;
struct MsgPort *myrport;

struct SignalSemaphore mem_sema;
struct SignalSemaphore sub_sema;

extern struct URLNode *clip_nd;

/// "Global Data"

struct AslData asldata;
int asl_counter = 0;

ULONG pool_puddleSize = POOL_SIZE, pool_treshSize = POOL_SIZE / 2;

struct ScanData scanhd;
struct GlobalData gd;
struct ObjApp *oa = NULL;
struct preferences *prefs = NULL;

///

/// "Library Bases"
struct Library *MUIMasterBase = NULL;
struct Library *SocketBase = NULL;
struct LocaleBase *LocaleBase = NULL;
struct GfxBase *GfxBase = NULL;
struct IntuitionBase *IntuitionBase = NULL;
struct Library *IFFParseBase = NULL;
///

/* the following functions are called from the sub task */

/// "maintask2SubTask"
LONG
SendATaskMsg (struct SubTask *st, WORD command, APTR params)
{
  struct SubTaskMsg *st_Message;

  if ((st_Message = (struct SubTaskMsg *) AllocSPooled (gd.gd_pool, sizeof (struct SubTaskMsg))))
    {
      st_Message->stm_Message.mn_ReplyPort = myport;
      st_Message->stm_Message.mn_Length = sizeof (struct SubTaskMsg);
      st_Message->stm_Command = command;
      st_Message->stm_Parameter = params;
      st_Message->stm_Result = 0;

      PutMsg (st->st_Port, (struct Message *) st_Message);

      return (1);
    }

  return (0);
}

LONG
SendSubTaskMsg (struct SubTask * st, WORD command, APTR params)
{
  st->st_Message.stm_Message.mn_ReplyPort = myrport;
  st->st_Message.stm_Message.mn_Length = sizeof (struct SubTaskMsg);
  st->st_Message.stm_Command = command;
  st->st_Message.stm_Parameter = params;
  st->st_Message.stm_Result = 0;

  PutMsg (st->st_Port, (struct Message *) &st->st_Message);
  WaitPort (myrport);
  GetMsg (myrport);

  return (st->st_Message.stm_Result);
}

LONG
SendSubTaskStartMsg (struct SubTask * st, WORD command, APTR params)
{
  st->st_Message.stm_Message.mn_ReplyPort = myrport;
  st->st_Message.stm_Message.mn_Length = sizeof (struct SubTaskMsg);
  st->st_Message.stm_Command = command;
  st->st_Message.stm_Parameter = params;
  st->st_Message.stm_Result = 0;

  PutMsg (&st->st_Task->pr_MsgPort, (struct Message *) &st->st_Message);
  WaitPort (myrport);
  GetMsg (myrport);

  return (st->st_Message.stm_Result);
}

struct SubTask *
SpawnSubTask (char *name, VOID (*func) (VOID), APTR data)
{
  struct SubTask *st;

  if ((st = (struct SubTask *) AllocSPooled (gd.gd_pool, sizeof (struct SubTask))))
    {
      st->st_MainPort = myrport;
      st->st_Reply = myport;
      st->st_Data = data;

      if ((st->st_Task = CreateNewProcTags (NP_Entry, (ULONG) func,
                                            NP_Name, (ULONG) name,
                                            NP_StackSize, 1024 * 8,
                                            NP_Priority, -5,
      //NP_Cli, TRUE,
                                            TAG_DONE)))
        {
          if (SendSubTaskStartMsg (st, STC_STARTUP, st))
            {
              return (st);
            }
        }

      FreeSPooled (gd.gd_pool, st, sizeof (struct SubTask));
    }
  return (NULL);
}

VOID
KillSubTask (struct SubTask * st)
{
  SendSubTaskMsg (st, STC_SHUTDOWN, st);
  FreeSPooled (gd.gd_pool, st, sizeof (struct SubTask));
}
///

/* Custom classes function: init/close! */

/// "Create/Delete Classes"
BOOL
CClass_Create (void)
{
  NLI_Class = MUI_CreateCustomClass (NULL, MUIC_Area, NULL, sizeof (struct NLIData), NLI_Dispatcher);
  MainWin_Class = MUI_CreateCustomClass (NULL, MUIC_Window, NULL, sizeof (struct MainWinData), MainWin_Dispatcher);
  PrefsWin_Class = MUI_CreateCustomClass (NULL, MUIC_Window, NULL, sizeof (struct PrefsWinData), PrefsWin_Dispatcher);
  AboutWin_Class = MUI_CreateCustomClass (NULL, MUIC_Window, NULL, sizeof (struct AboutWinData), AboutWin_Dispatcher);
  Grab_Class = MUI_CreateCustomClass (NULL, MUIC_Group, NULL, sizeof (struct GrabData), Grab_Dispatcher);
  WinGrab_Class = MUI_CreateCustomClass (NULL, MUIC_Window, NULL, sizeof (struct WinGrabData), WinGrab_Dispatcher);
  ProjWin_Class = MUI_CreateCustomClass (NULL, MUIC_Window, NULL, sizeof (struct ProjWinData), ProjWin_Dispatcher);
  UrlEditWin_Class = MUI_CreateCustomClass (NULL, MUIC_Window, NULL, sizeof (struct UrlEditWinData), UrlEditWin_Dispatcher);
  ButtonDrop_Class = MUI_CreateCustomClass (NULL, MUIC_Group, NULL, sizeof (struct ButtonDrop_Data), ButtonDrop_Dispatcher);
  StrDrop_Class = MUI_CreateCustomClass (NULL, MUIC_String, NULL, sizeof (struct StrDrop_Data), StrDrop_Dispatcher);
  ImageIFF_Class = MUI_CreateCustomClass (NULL, MUIC_Bodychunk, NULL, sizeof (struct ImageIFF_Data), ImageIFF_Dispatcher);
  EditWin_Class = MUI_CreateCustomClass (NULL, MUIC_Window, NULL, sizeof (struct EditWinData), EditWin_Dispatcher);
  LPrefsWin_Class = MUI_CreateCustomClass (NULL, MUIC_Window, NULL, sizeof (struct LPrefsWinData), LPrefsWin_Dispatcher);

  if (NLI_Class && Grab_Class && WinGrab_Class && MainWin_Class && ImageIFF_Class &&
   UrlEditWin_Class && ButtonDrop_Class && ProjWin_Class && StrDrop_Class &&
      EditWin_Class && LPrefsWin_Class)
    return (TRUE);

  return (FALSE);
}

void
CClass_Delete (void)
{
  if (LPrefsWin_Class)
    MUI_DeleteCustomClass (LPrefsWin_Class);
  LPrefsWin_Class = NULL;

  if (EditWin_Class)
    MUI_DeleteCustomClass (EditWin_Class);
  EditWin_Class = NULL;

  if (ImageIFF_Class)
    MUI_DeleteCustomClass (ImageIFF_Class);
  ImageIFF_Class = NULL;

  if (StrDrop_Class)
    MUI_DeleteCustomClass (StrDrop_Class);
  StrDrop_Class = NULL;

  if (ButtonDrop_Class)
    MUI_DeleteCustomClass (ButtonDrop_Class);
  ButtonDrop_Class = NULL;

  if (UrlEditWin_Class)
    MUI_DeleteCustomClass (UrlEditWin_Class);
  UrlEditWin_Class = NULL;

  if (ProjWin_Class)
    MUI_DeleteCustomClass (ProjWin_Class);
  ProjWin_Class = NULL;

  if (WinGrab_Class)
    MUI_DeleteCustomClass (WinGrab_Class);
  WinGrab_Class = NULL;

  if (Grab_Class)
    MUI_DeleteCustomClass (Grab_Class);
  Grab_Class = NULL;

  if (AboutWin_Class)
    MUI_DeleteCustomClass (AboutWin_Class);
  AboutWin_Class = NULL;

  if (PrefsWin_Class)
    MUI_DeleteCustomClass (PrefsWin_Class);
  PrefsWin_Class = NULL;

  if (MainWin_Class)
    MUI_DeleteCustomClass (MainWin_Class);
  MainWin_Class = NULL;

  if (NLI_Class)
    MUI_DeleteCustomClass (NLI_Class);
  NLI_Class = NULL;

}

///

/*******************************************************/
/* Main routine of the program                         */
/*******************************************************/

/// "LONG request_message (char *msg)"
LONG
request_message (char *msg)
{
  struct EasyStruct es =
  {
    sizeof (struct EasyStruct),
    0,
    STRING (ERR_REQTITLE, "GiambyNetGrabber Error"),
    msg,
    STRING (MSG_REQOK, "OK")
  };

  return (EasyRequestArgs (NULL, &es, NULL, NULL));
}
///

/// "void save_exitprefs (void)"
void
save_exitprefs (void)
  // After exit, save some configs
{
  ConfigClass cfg(gd.gd_pool, "configs/GNetGrab.cfg");

  if (cfg.Load () != CFG_READ_FAIL)
    {
      ObtainSemaphore (&prefs->sema);
      cfg.Set ("Recents", prefs->recents, 4);
      cfg.Set ("RequesterCoords", &prefs->req_top, 4);
      ReleaseSemaphore (&prefs->sema);

      if (!cfg.Save ())
        error_req (STRING (MSG_OK, "*_OK"), "%s", STRING (ERR_CFGSAVE, "Error while saving config file"));
    }
  else
    error_req (STRING (MSG_OK, "*_OK"), "%s", STRING (ERR_NOMEM, "Out of Memory"));
}
///

/// "BOOL open_libs (void)"
BOOL
open_libs (void)
{
  char *lib = NULL;
  int ver = 0;

  if ((GfxBase = (struct GfxBase *) OpenLibrary ("graphics.library", 38)))
    {
      if ((IntuitionBase = (struct IntuitionBase *) OpenLibrary ("intuition.library", 38)))
        {
          if ((IFFParseBase = OpenLibrary ("iffparse.library", 38)))
            {
              if ((MUIMasterBase = OpenLibrary (MUIMASTER_NAME, MUIMASTER_VMIN)))
                {
                  if ((LocaleBase = (struct LocaleBase *) OpenLibrary ("locale.library", 38)))
                    {

                      return (TRUE);

                      CloseLibrary ((struct Library *) LocaleBase);
                    }
                  else
                    {
                      lib = "locale.library";
                      ver = 38;
                    }

                  CloseLibrary (MUIMasterBase);
                }
              else
                {
                  lib = MUIMASTER_NAME;
                  ver = MUIMASTER_VMIN;
                }

              CloseLibrary (IFFParseBase);
            }
          else
            {
              lib = "iffparse.library";
              ver = 38;
            }

          CloseLibrary ((struct Library *) IntuitionBase);
        }
      else
        {
          lib = "intuition.library";
          ver = 38;
        }

      CloseLibrary ((struct Library *) GfxBase);
    }
  else
    {
      lib = "graphics.library";
      ver = 38;
    }


  FILE *outFile;
  outFile = fopen ("CON:5/20/320/100/GiambyNetGrabber Fatal Error!/WAIT/CLOSE", "w+");

  if (outFile)
    {
      fprintf (outFile, "Error opening \"%s\" V%ld\n", lib, ver);

      fclose (outFile);
    }

  return (FALSE);
}
///

///  "BOOL open_mems (void)"
BOOL
open_mems (void)
{
  if ((gd.gd_pool = CreatePool (MEMF_PUBLIC | MEMF_ANY, pool_puddleSize, pool_treshSize)) != NULL)
    {
      if ((myport = CreateMsgPort ()))
        {
          if ((myrport = CreateMsgPort ()))
            {
              return (TRUE);

              DeleteMsgPort (myrport);
            }
          else
            request_message (STRING (ERR_NOPORT, "Can't create message port"));

          DeleteMsgPort (myport);
        }
      else
        request_message (STRING (ERR_NOPORT, "Can't create message port"));

      DeletePool (gd.gd_pool);
    }
  else
    request_message (STRING (ERR_NOPOOLMEM, "Can't create memory pool"));

  return (FALSE);
}
///

void realmain (void);

/// "void wbmain (struct WBStartup *wbmsg)"
void
wbmain (struct WBStartup *wbmsg)
{
  if (wbmsg)
    {
      struct DiskObject *dobj;

      UnLock (CurrentDir (DupLock (wbmsg->sm_ArgList[0].wa_Lock)));

      if ((dobj = GetDiskObject (wbmsg->sm_ArgList[0].wa_Name)))
        {
          UBYTE **tt = (UBYTE **) dobj->do_ToolTypes;
          char *poolsize, *treshsize, *keyfilename;

          poolsize = (char *) FindToolType (tt, "POOLPUDDLESIZE");
          treshsize = (char *) FindToolType (tt, "POOLTRESHSIZE");
          keyfilename = (char *) FindToolType (tt, "KEYFILE");


          if (poolsize)
            {
              pool_puddleSize = atoi (poolsize);
            }

          if (treshsize)
            {
              pool_treshSize = atoi (treshsize);
            }

          if (keyfilename)
            {
              if ((gd.keyfilename = (STRPTR) AllocVec (strlen (keyfilename), MEMF_PUBLIC)))
                strcpy (gd.keyfilename, keyfilename);
            }
          else
            gd.keyfilename = NULL;

          FreeDiskObject (dobj);
        }
    }

  realmain ();
}
///

/// "void main (int argc, char **argv)"
#define NOPTS           3
#define TEMPLATE        "POOLPUDDLESIZE=PPS/N,POOLTRESHSIZE=PTS/N,KEYFILE,"

#define OPT_PUDDLESIZE  0
#define OPT_TRESHSIZE   1
#define OPT_KEYFILE     2

#define OPT_NOADD       3
#define OPT_PAR         4
#define OPT_PREFS       5
#define OPT_HELP        6

void
main (int argc, char **argv)
{
  if (argc == 0)
    wbmain ((struct WBStartup *) argv);
  else
    {
      struct RDArgs *rdargs;
      LONG opts[NOPTS] =
      {0};

      rdargs = ReadArgs (TEMPLATE, opts, NULL);

      if (rdargs != NULL)
        {

          if (opts[OPT_PUDDLESIZE] != 0)
            {
              pool_puddleSize = *((LONG *) opts[OPT_PUDDLESIZE]);
            }

          if (opts[OPT_TRESHSIZE] != 0)
            {
              pool_treshSize = *((LONG *) opts[OPT_TRESHSIZE]);
            }

          if (opts[OPT_KEYFILE] != 0)
            {
              if ((gd.keyfilename = (STRPTR) AllocVec (strlen ((STRPTR) opts[OPT_KEYFILE]), MEMF_PUBLIC)))
                strcpy (gd.keyfilename, (STRPTR) opts[OPT_KEYFILE]);
            }
          else
            gd.keyfilename = NULL;

          FreeArgs (rdargs);
        }

      realmain ();
    }
}
///

/// "void realmain (void)"
void
realmain (void)
{
  if (open_libs ())
    {
      // Init Global Data structure...
      InitSemaphore (&mem_sema);
      InitSemaphore (&sub_sema);
      clip_nd = NULL;

      OpenCat (NULL);


      // check if pool sizes are correct!
      if (pool_puddleSize < POOL_SIZE)
        pool_puddleSize = POOL_SIZE / 2;

      if (pool_treshSize < POOL_SIZE / 4)
        pool_treshSize = POOL_SIZE / 4;

      if (pool_treshSize > pool_puddleSize)
        pool_treshSize = pool_puddleSize / 2;

      D (printf ("PoolSize:  %ld\n", pool_puddleSize));
      D (printf ("TreshSize: %ld\n", pool_treshSize));

      if (open_mems ())
        {
          if ((prefs = LoadPrefs ()))
            {
              if (CClass_Create ())
                {
                  if (CreateApp ())
                    {
                      InitSemaphore (&asldata.sema);
                      asldata.mem_sema = &mem_sema;
                      asldata.prefs = prefs;
                      asldata.pool = gd.gd_pool;
                      asldata.main_win = oa->main_win;

                      gd.gd_app = oa->App;

                      BPTR subproc;

                      scanhd.sema = &sub_sema;
                      scanhd.mem_sema = &mem_sema;
                      scanhd.prefs = prefs;
                      scanhd.pool = gd.gd_pool;
                      scanhd.app = gd.gd_app;
                      InitSemaphore (&scanhd.lsema);
                      NewList (&scanhd.scanlist);

                      if ((subproc = LoadSeg ("PROGDIR:modules/html")))
                        {
                          if ((gd.gd_subtask = SpawnNetTask ("GNG-Decoding", subproc, &scanhd)))
                            {
                              ULONG sigs = 0;

                              while (DoMethod ((Object *) oa->App, MUIM_Application_NewInput, &sigs) != MUIV_Application_ReturnID_Quit)
                                {
                                  if (sigs)
                                    {
                                      sigs = Wait (sigs | SIGBREAKF_CTRL_C | (1 << myport->mp_SigBit));

                                      if (sigs & (1 << myport->mp_SigBit))
                                        {
                                          struct SubTaskMsg *stm;

                                          while ((stm = (struct SubTaskMsg *) GetMsg (myport)))
                                            {
                                              switch (stm->stm_Command)
                                                {
                                                case STC_MUIMSG:
                                                  stm->stm_Result = DoMethodA ((Object *) ((struct MUIMessage *) stm->stm_Parameter)->muim_obj,
                                                                               (Msg) ((struct MUIMessage *) stm->stm_Parameter)->muim_message);
                                                  ReplyMsg ((struct Message *) stm);
                                                  break;

                                                case STC_MUIAMSG:
                                                  {
                                                    ULONG buf[20];
                                                    Object *obj = (Object *) ((struct MUIMessage *) stm->stm_Parameter)->muim_obj;
                                                    int parnum = ((struct MUIMessage *) stm->stm_Parameter)->muim_parnum;

                                                    for (int i = 0;; i++)
                                                      {
                                                        if (parnum-- > 0)
                                                          buf[i] = ((struct MUIMessage *) stm->stm_Parameter)->muim_message[i];
                                                        else
                                                          {
                                                            buf[++i] = NULL;
                                                            break;
                                                          }
                                                      }

                                                    stm->stm_Result = 1;
                                                    ReplyMsg ((struct Message *) stm);
                                                    DoMethodA (obj, (Msg) buf);
                                                  }
                                                  break;


                                                default:
                                                  ReplyMsg ((struct Message *) stm);
                                                  break;
                                                }
                                            }
                                        }

                                      if (sigs & SIGBREAKF_CTRL_C)
                                        break;
                                    }
                                }

                              D (printf ("Ending\n"));

                              ObtainSemaphore (&sub_sema);
                              scanhd.app = NULL;
                              ReleaseSemaphore (&sub_sema);

                              DoMethod ((Object *) oa->main_win, MUIM_Main_ShutDown, NULL);
                              save_exitprefs ();
                              KillSubTask (gd.gd_subtask);
                            }
                          else
                            error_req (STRING (MSG_OK, "*_OK"), "\033c%s", STRING (ERR_NODECODING, "Failed to create\ndecoding subtask"));

                          UnLoadSeg (subproc);
                        }
                      else
                        error_req (STRING (MSG_OK, "*_OK"), "\033c%s", STRING (ERR_NODECODING, "Failed to create\ndecoding subtask"));

                      MUI_DisposeObject ((Object *) oa->App);

                      MUI_DisposeObject ((Object *) oa->proj_stimg[6]);
                      MUI_DisposeObject ((Object *) oa->proj_stimg[5]);
                      MUI_DisposeObject ((Object *) oa->proj_stimg[4]);
                      MUI_DisposeObject ((Object *) oa->proj_stimg[3]);
                      MUI_DisposeObject ((Object *) oa->proj_stimg[2]);
                      MUI_DisposeObject ((Object *) oa->proj_stimg[1]);
                      MUI_DisposeObject ((Object *) oa->proj_stimg[0]);
                    }
                }
              else
                request_message (STRING (ERR_NOCLASSES, "Failed to create\nMUI custom classes"));

              CClass_Delete ();
            }

          DeleteMsgPort (myrport);
          DeleteMsgPort (myport);
          DeletePool (gd.gd_pool);
        }

      CloseCat ();

      CloseLibrary ((struct Library *) LocaleBase);
      CloseLibrary (MUIMasterBase);
      CloseLibrary (IFFParseBase);
      CloseLibrary ((struct Library *) IntuitionBase);
      CloseLibrary ((struct Library *) GfxBase);
    }
}
///


























/* */
