/**
 * Scout - The Amiga System Monitor
 *
 *------------------------------------------------------------------
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or
 * any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
 *
 * You must not use this source code to gain profit of any kind!
 *
 *------------------------------------------------------------------
 *
 * @author Andreas Gelhausen
 * @author Richard Körber <rkoerber@gmx.de>
 */

#include "system_headers.h"

/*********************************************************************
                           Definitionen
*********************************************************************/
#define MYLIBVERSION    37
#define MUIVERSION      8

/*********************************************************************
                         Globale Variablen
*********************************************************************/
extern struct  ExecBase *SysBase;

struct Library          *MUIMasterBase;
struct Library          *IdentifyBase;
struct Library          *CxBase;
struct Library          *LayersBase;
struct LocaleBase       *LocaleBase;

struct Device           *TimerBase;

struct Task             *myprocess;
struct MsgPort          *myarexxport,*ScoutPort;
BOOL   ScoutPortAdded = FALSE;

struct IOStdReq         *InputIORequest;
BOOL   input = TRUE, timer2 = TRUE;
struct timerequest      *TimerIORequest;
BYTE   TimerSignal;

BOOL   input;
char   *portname;

APTR   globalPool;

STRPTR decimalSeparator;

/*
**  MUI
*/

APTR AP_Scout;

APTR WI_Main;

BPTR     printfile, prevdir;

UBYTE *AllocationsText   = "Allocations";      /*-*/
UBYTE *AssignsText       = "Assigns";
UBYTE *ClassesText       = "BoopsiClasses";    /*-*/
UBYTE *CommoditiesText   = "Commodities";      /*-*/
UBYTE *DevicesText       = "Devices";
UBYTE *CommandsText      = "Resident Cmds";
UBYTE *ExpansionsText    = "Expansions";
UBYTE *FontsText         = "Fonts";
UBYTE *InputHandlersText = "InputHandlers";
UBYTE *InterruptsText    = "Interrupts";
UBYTE *LibrariesText     = "Libraries";
UBYTE *LocksText         = "Locks";
UBYTE *LowMemoryText     = "LowMemory";        /*-*/
UBYTE *MemoryText        = "Memory";
UBYTE *MountText         = "Mounted Devs";
UBYTE *PortsText         = "Ports";
UBYTE *ResidentsText     = "Residents";
UBYTE *ResourcesText     = "Resources";
UBYTE *ScreenModeText    = "ScreenMode";
UBYTE *SemaphoresText    = "Semaphores";
UBYTE *SystemText        = "System";           /*-*/
UBYTE *TasksText         = "Tasks";
UBYTE *TimerText         = "Timer";            /*-*/
UBYTE *VectorsText       = "Vectors";
UBYTE *WindowsText       = "Windows";
UBYTE *PatchesText       = "Patches";
UBYTE *CatalogsText      = "Catalogs";
UBYTE *AudioModesText    = "AudioModes";

/*********************************************************************
                                 Fail
   Diese Routine wird zum Schluß des Programmes aufgerufen!
*********************************************************************/
static void fail1( void )
{
    Forbid();
    if (FindPort("AMITCP")) {
       Permit();
       failtcp();
    } else {
       Permit();
    }

    if (globalPool) tbDeletePool(globalPool);

    if (TimerIORequest) {
        if (!timer2) {
            if (timer_ticking) {
                if (!CheckIO((struct IORequest *)TimerIORequest)) AbortIO((struct IORequest *)TimerIORequest);
                WaitIO((struct IORequest *)TimerIORequest);
            }
            CloseDevice((struct IORequest *)TimerIORequest);
        }
        DeleteIORequest(TimerIORequest);
    }

    if (InputIORequest) {
        if (!input) CloseDevice((struct IORequest *)InputIORequest);
        DeleteIORequest(InputIORequest);
    }

    if (ScoutPort) {
        if (ScoutPortAdded) RemPort(ScoutPort);
        DeleteMsgPort(ScoutPort);
    }

    if (prevdir) CurrentDir(prevdir);

    CloseScoutCatalog();

    if (RexxSysBase) CloseLibrary((struct Library *)RexxSysBase);
    if (ExpansionBase) CloseLibrary((struct Library *)ExpansionBase);
    if (IdentifyBase) CloseLibrary (IdentifyBase);
    if (CxBase) CloseLibrary (CxBase);
    if (AslBase) CloseLibrary (AslBase);
    if (DiskfontBase) CloseLibrary (DiskfontBase);
    if (UtilityBase) CloseLibrary (UtilityBase);
    if (IconBase) CloseLibrary (IconBase);
    if (IntuitionBase) CloseLibrary((struct Library *)IntuitionBase);
    if (GfxBase) CloseLibrary((struct Library *) GfxBase);
    if (LayersBase) CloseLibrary(LayersBase);
    if (LocaleBase) CloseLibrary((struct Library *)LocaleBase);
}

static void fail( void )
{
    if (AP_Scout) {
        struct DiskObject *dob = NULL;

        get(AP_Scout, MUIA_Application_DiskObject, &dob);
        if (dob) FreeDiskObject(dob);

        MUI_DisposeObject(AP_Scout);
        AP_Scout = NULL;
    }

    if (MUIMasterBase) {
        DeleteCustomClasses();
        CloseLibrary(MUIMasterBase);
    }

    fail1();
}

/*********************************************************************
                                 Init
   Diese Routine wird am Beginn des Programmes aufgerufen!
*********************************************************************/
static BOOL init1( void )
{
    GfxBase = (struct GfxBase *)OpenLibrary(GRAPHICSNAME, MYLIBVERSION);
    IntuitionBase = (struct IntuitionBase *)OpenLibrary("intuition.library", MYLIBVERSION);
    IconBase = OpenLibrary(ICONNAME, MYLIBVERSION);
    UtilityBase = OpenLibrary(UTILITYNAME, MYLIBVERSION);

    if ((!GfxBase) || (!IntuitionBase) || (!IconBase) || (!UtilityBase)) return FALSE;

    if (!(AslBase = MyOpenLibrary("asl.library", MYLIBVERSION))) return FALSE;
    if (!(DiskfontBase = MyOpenLibrary("diskfont.library", MYLIBVERSION))) return FALSE;
    if (!(CxBase = MyOpenLibrary("commodities.library", MYLIBVERSION))) return FALSE;
    if (!(ExpansionBase = (struct ExpansionBase *)MyOpenLibrary(EXPANSIONNAME, MYLIBVERSION))) return FALSE;
    if (!(RexxSysBase = (struct RxsLib *)MyOpenLibrary(RXSNAME, 0))) return FALSE;
    if (!(LayersBase = MyOpenLibrary("layers.library", MYLIBVERSION))) return FALSE;
    IdentifyBase = OpenLibrary("identify.library", IDENTIFYVERSION);

    decimalSeparator = ","; // english separator for 1000s
    if (LocaleBase = (struct LocaleBase *)OpenLibrary("locale.library", 37)) {
        struct Locale *loc;

        if (loc = OpenLocale(NULL)) {
            decimalSeparator = loc->loc_GroupSeparator;
        }
    }

    OpenScoutCatalog();

    myprocess = FindTask(NULL);
    prevdir = CurrentDir(((struct Process *)myprocess)->pr_HomeDir);

    if (ScoutPort = CreateMsgPort()) {
        ScoutPort->mp_Node.ln_Name = "scout.reply";
        ScoutPort->mp_Node.ln_Pri = 1;

        TimerSignal = ScoutPort->mp_SigBit;

        AddPort(ScoutPort);
        ScoutPortAdded = TRUE;

        if (InputIORequest = CreateIORequest(ScoutPort, sizeof(struct IOStdReq))) {
            if (input = OpenDevice("input.device", 0, (struct IORequest *)InputIORequest, 0)) {
                aprintf(msgCantOpenInputDevice);
                return FALSE;
            }
        } else {
            return FALSE;
        }

        if (TimerIORequest = CreateIORequest(ScoutPort, sizeof(struct timerequest))) {
            if (timer2 = OpenDevice(TIMERNAME, 0, (struct IORequest *)TimerIORequest, 0)) {
                aprintf(msgCantOpenTimerDevice);
                return FALSE;
            }
        } else {
            return FALSE;
        }

        if (!(globalPool = tbCreatePool(MEMF_CLEAR | MEMF_PUBLIC, 4096, 4096))) {
            return FALSE;
        }
    } else {
        return FALSE;
    }

    return TRUE;
}

static BOOL init2( void )
{
    if (!(MUIMasterBase = MyOpenLibrary(MUIMASTER_NAME, MUIVERSION))) return FALSE;
    if (!CreateCustomClasses()) return FALSE;

    return (TRUE);
}

/*********************************************************************
 BEGIN:                        MAIN
*********************************************************************/

ULONG scout_main( void )
{
    BOOL RETURN_FLAG = FALSE;
    ULONG RETURN_CODE = RETURN_OK;
    struct Library *miamilib = NULL;

    if (!init1()) {
        fail1();
        return RETURN_FAIL;
    }

    if ((opts.User || opts.Password) && (!opts.Host)) {
        aprintf(msgNoHostNameSpecified);
        RETURN_CODE = RETURN_FAIL;
        RETURN_FLAG = TRUE;
    } else if (opts.Host) {
        Forbid();
        if (FindPort("AMITCP") != NULL || (miamilib = OpenLibrary("miami.library",0L)) != NULL) {
            Permit();

            if (miamilib) CloseLibrary(miamilib);
            if (opts.Command) {
                RETURN_CODE = netshellclient();
                RETURN_FLAG = TRUE;
            } else if (!(clientstate = ConnectToServer())) {
                RETURN_CODE = RETURN_FAIL;
                RETURN_FLAG = TRUE;
            }
        } else {
            Permit();

            aprintf(msgStartTCPIPStack);
            RETURN_CODE = RETURN_FAIL;
            RETURN_FLAG = TRUE;
        }
    } else if (opts.Command) {
        shellstate = TRUE;
        RETURN_CODE = ExecuteCommand(opts.Command);
        RETURN_FLAG = TRUE;
    } else {
        Forbid();
        if (FindPort("AMITCP") && isNetCall()) {
            Permit();
            RETURN_CODE = netdaemon();
            RETURN_FLAG = TRUE;
        } else {
            Permit();
        }
    }

    if (RETURN_FLAG) {
        fail1();
        return RETURN_CODE;
    }

    if (!init2()) {
        fail();
        return RETURN_FAIL;
    }

    if ((opts.IntervalTime) && (IsReal(opts.IntervalTime))) {
        if (strlen(opts.IntervalTime) <= 6) {
           strcpy(updatetimetext, opts.IntervalTime);
        }
    }

    if (!GetApplication()) {
        aprintf(msgCantCreateApplication);
        fail();
        return RETURN_FAIL;
    }

    Forbid();
    if (portname = FindMyARexxPort("SCOUT")) {
        myarexxport = FindPort(portname);
    } else {
        portname = " < ERROR > ";
        myarexxport = NULL;
    }

    if ((opts.PortName) && (myarexxport)) {
        portname = opts.PortName;
        myarexxport->mp_Node.ln_Name = opts.PortName;
    }
    Permit();

    if (opts.Iconified) set(AP_Scout, MUIA_Application_Iconified, TRUE);

/*
** Everything's ready, lets launch the application. We will
** open the master window now at this old position.
*/

    DoMethod(AP_Scout, MUIM_Application_Load, MUIV_Application_Load_ENV);

    set(WI_Main, MUIA_Window_Open, TRUE);

/*
** Now the ARexx startup script will be started. Herewith it's
** possible to open window at the beginning.
*/

    if (opts.Startup) {
        BPTR startuplock;

        if (startuplock = Lock(opts.Startup, SHARED_LOCK)) {
            UnLock (startuplock);

            if (!SendStartupMsg("REXX", opts.Startup, TRUE)) {
                aprintf(msgCantSendARexxScript, opts.Startup, "REXX");
            }
        } else {
            if (!SendStartupMsg(myarexxport->mp_Node.ln_Name, opts.Startup, FALSE)) {
                aprintf(msgCantSendARexxScript, opts.Startup, myarexxport->mp_Node.ln_Name);
            }
        }
    }

/*
** This is the main loop. As you can see, it does just nothing.
** Everything is handled by MUI, no work for the programmer.
*/

#if 0
    {
        ULONG sigs = 0, received = 0;
        ULONG reid;

        while (TRUE) {
            reid = DoMethod(AP_Scout, MUIM_Application_NewInput, &sigs);

            if (reid == MUIV_Application_ReturnID_Quit) {
                break;
            }

/*
            case ID_LOWMEMORYCAUSE:
               if (entryp = GetActiveEntry (lowmemorylist)) {
                  MyDoCommand ("CauseLowMemory %s", ((struct LowMemoryEntry *) entryp)->lme_Address);
               }
               break;
            case ID_TIMERREMOVE:
               if (entryp = GetActiveEntry (timerlist)) {
                  if (MyRequest ("Yes|No", "Do you really want to remove\n\'%s\'?", ((struct TimerEntry *) entryp)->timer_name)) {
                     if (MyDoCommand ("RemoveTimer %s", ((struct TimerEntry *) entryp)->timer_address)) {
                        SetCountText (timercount, --timercnt);
                        RemoveActiveEntry (timerlist);
                     } else {
                        ShowTimer();
                     }
                  }
               }
               break;
*/

            if (sigs) {
                sigs |= SIGBREAKF_CTRL_C | SIGBREAKF_CTRL_D | SIGBREAKF_CTRL_E | SIGBREAKF_CTRL_F | (1 << TimerSignal);
                received = Wait(sigs);
            }

            if (received & (SIGBREAKF_CTRL_C | SIGBREAKF_CTRL_D | SIGBREAKF_CTRL_E)) {
                aprintf(msgBreak);
                break;
            }

            if (received & SIGBREAKF_CTRL_F) {
                set(AP_Scout, MUIA_Application_Iconified, FALSE);
            }

            if (received & (1 << TimerSignal)) {
                GetMsg(ScoutPort);
                HandleTimerRequest(TRUE);
            }

            sigs &= ~(SIGBREAKF_CTRL_C | SIGBREAKF_CTRL_D | SIGBREAKF_CTRL_E | SIGBREAKF_CTRL_F);
            sigs |= (1 << TimerSignal);
        }
    }
#else
    {
        ULONG sigs = 0;

        while (DoMethod(AP_Scout, MUIM_Application_NewInput, &sigs) != MUIV_Application_ReturnID_Quit) {
            if (sigs) {
                sigs = Wait(sigs | SIGBREAKF_CTRL_C | SIGBREAKF_CTRL_D | SIGBREAKF_CTRL_E | SIGBREAKF_CTRL_F);

                if (sigs & (SIGBREAKF_CTRL_C | SIGBREAKF_CTRL_D | SIGBREAKF_CTRL_E)) {
                    aprintf(msgBreak);
                    break;
                }
                if (sigs & SIGBREAKF_CTRL_F) {
                    set(AP_Scout, MUIA_Application_Iconified, FALSE);
                }
            }
        }
    }
#endif

    if (opts.Host) SendDaemon(CMD_END);

    // not just close the just, but trigger the CloseSubWindows method, too
    set(WI_Main, MUIA_Window_CloseRequest, TRUE);

    fail();
    return RETURN_OK;
}

/*********************************************************************
 END:                           MAIN
*********************************************************************/
