/*
 * StringInOut    (C) 1992 Software Sculptors
 *                         All Rights Under Copyright Reserved
 *
 * Version 4.0
 *
 * Written using SAS/C 6.00
 *
 * PROGRAM AND SOURCE CODE ARE FREELY (RE)DISTRIBUTABLE.
 *
 * By John M. Haubrich, Jr.
 */
#include <exec/libraries.h>
#include <libraries/commodities.h>
#include <intuition/intuition.h>
#include <intuition/intuitionbase.h>
#include <dos/dos.h>
#include <dos/rdargs.h>
#include <clib/dos_protos.h>
#include <clib/exec_protos.h>
#include <clib/alib_protos.h>
#include <clib/commodities_protos.h>
#include <clib/intuition_protos.h>
#include <stdlib.h>
#include <string.h>
#include <stdio.h>

#ifdef LATTICE
int CXBRK(void) { return(0); }
int chkabort(void) { return(0); }
#endif

#define EVT_OUTHOTKEY   1L
#define EVT_INHOTKEY    2L


/*
 * VERSION STRING ===================================
 */
char *vers="$VER: StringInOut 4.0 (04.06.93)";


/*
 * STRUCTURE DEFINITIONS ----------------------------
 */
struct NewBroker newbroker = {
   NB_VERSION,
   "StringInOut",
   "Activate/deactivate string gadgets",
   "in the current window.      (V4.0)",
   NBU_UNIQUE | NBU_NOTIFY,
   0, 0, 0, 0
};

struct MyGlobals {
   struct MsgPort       *broker_mp;
   CxObj                *broker;
   ULONG                 ulDelayValue;
   BOOL                  fRunFromWB;
   ULONG                 cxsigflag;  // our combined message port signal FLAG
   BOOL                  fBeQuiet;
};


/*
 * FUNCTION PROTOTYPES ------------------------------
 */
void main( int, char ** );
void InitVars( struct MyGlobals * );
void ProcessMsg( struct MyGlobals * );
void complain( struct MyGlobals *, char * );
void quit( struct MyGlobals *, char * );


/*
 * DEFINES ------------------------------------------
 */
#define NUM_ARGS        4
#define ARG_DELAY       0
#define ARG_OUTHOTKEY   1
#define ARG_INHOTKEY    2
#define ARG_QUIET       3


/*
 * LIBRARY VARIABLES ---------------------------------
 */
struct Library       *CxBase = NULL,
                     *IconBase = NULL;
struct IntuitionBase *IntuitionBase = NULL;


/*
 * FUNCTIONS ----------------------------------------
 */

/*
 * InitVars(): initialize variables to default values
 */
VOID InitVars( struct MyGlobals *mg )
{

   mg->broker_mp     = NULL;

   mg->ulDelayValue  = 5L;

   mg->fRunFromWB    = TRUE;
   mg->fBeQuiet      = FALSE;
}

/*
 * complain(): pop up easyrequest() with a complaint message if WB flag set
 *             else send output to stdout
 */
void complain(struct MyGlobals *mg, char *t) {
static struct EasyStruct es = {
   sizeof(struct EasyStruct),
   NULL,
   "StringInOut Complaint...",
   NULL,
   "OK"
};

   if (t) {
      if ( mg->fRunFromWB == TRUE )
      {
         es.es_TextFormat = t;
         EasyRequest(NULL, &es, NULL, TAG_DONE);
      }
      else
      {
         printf( "%s\n", t );
      }
   }
}

/*
 * quit(): close down everything we've opened and quit
 */
void quit( struct MyGlobals *mg, char *t )
{

   if ( (t) && ( mg->fBeQuiet == FALSE ) ) complain( mg, t );

   if (mg->broker)            DeleteCxObjAll(mg->broker);
   if (mg->broker_mp)         DeletePort(mg->broker_mp);

   if (IconBase)        CloseLibrary(IconBase);
   if (CxBase)          CloseLibrary(CxBase);
   if (IntuitionBase)   CloseLibrary((struct Library *)IntuitionBase);

   exit( ( t ? TRUE : FALSE ) );
}


/**
 ** M A I N   P R O G R A M   -----------------------
 **/
void main( int argc, char *argv[] )
{
   UBYTE            **ttypes,
                     *temp,
                     *pInHotkey,
                     *pOutHotkey,
                      szInHotkey[256],
                      szOutHotkey[256];
   CxMsg             *msg;
   CxObj             *filterI, *filterO;
   LONG               errorcode;
   LONG               rda_Arguments[NUM_ARGS];
   USHORT             us;
   char               errbuf[256];
   struct RDArgs     *rda_Args;

   struct MyGlobals   MG;


   for ( us=0; us < NUM_ARGS; us++ )   rda_Arguments[us] = 0;

   if (!(IntuitionBase = (struct IntuitionBase *)
            OpenLibrary("intuition.library",37L)))
      exit(TRUE);

   if (!(CxBase = OpenLibrary("commodities.library",37L)))
      quit( &MG, "Cannot open commodities.library 37+");

   if (!(IconBase = OpenLibrary("icon.library",37L)))
      quit( &MG, "Cannot open icon.library 37+");

   InitVars( &MG );

   if (!(MG.broker_mp = CreateMsgPort()))
      quit( &MG, "Cannot create broker message port.");

   // run from Workbench or CLI/Shell?
   // if from Shell, print errors to stdout else use EasyRequest
   if ( argc > 0 )
   {
      MG.fRunFromWB = FALSE;

      // set defaults
      MG.fBeQuiet = FALSE;                // default is FALSE
      MG.ulDelayValue = 5;                // default is 5 ticks
      strcpy( szOutHotkey, "rawkey f1" ); // default is 'rawkey f1'
      strcpy( szInHotkey,  "rawkey f2" ); // default is 'rawkey f2'

      // get opts from command line
      if (rda_Args = ReadArgs( "DELAY/K/N,OUTHOTKEY/K,INHOTKEY/K,QUIET/S", rda_Arguments, NULL ))
      {
         if ( rda_Arguments[ARG_DELAY] )
         {
            MG.ulDelayValue = *((LONG *)rda_Arguments[ARG_DELAY]);
         }
         if ( rda_Arguments[ARG_OUTHOTKEY] )
         {
            strcpy( szOutHotkey, (char *)rda_Arguments[ARG_OUTHOTKEY] );
         }
         if ( rda_Arguments[ARG_INHOTKEY] )
         {
            strcpy( szInHotkey, (char *)rda_Arguments[ARG_INHOTKEY] );
         }
         if ( rda_Arguments[ARG_QUIET] )
         {
            MG.fBeQuiet = TRUE;
         }
         FreeArgs( rda_Args );
      }
      else
      {
         Fault(IoErr(), "StringInOut failed because ", errbuf, sizeof( errbuf ));
         quit( &MG, errbuf );
      }
   }
   else
   {
      MG.fRunFromWB = TRUE;

      // get opts from TOOLTYPES in icon
      ttypes = ArgArrayInit( argc, argv );

      // user-specified priority for the commodity
      newbroker.nb_Pri = (BYTE)ArgInt( ttypes, "CX_PRIORITY", 0 );

      // key to actuate the STRINGOUT function
      pOutHotkey = ArgString( ttypes, "OUTHOTKEY", "rawkey f1" );
      strcpy( szOutHotkey, pOutHotkey );

      // key to actuate the STRINGIN function
      pInHotkey = ArgString( ttypes, "INHOTKEY", "rawkey f2" );
      strcpy( szInHotkey, pInHotkey );

      // delay time between window flipping (in ticks -- 1 tick = 1/50 sec)
      MG.ulDelayValue = ArgInt( ttypes, "DELAY", 5L );

      // report errors?
      temp = ArgString( ttypes, "QUIET", "FALSE" );
      if ( strcmpi( temp, "TRUE" ) == 0 )
      {
         MG.fBeQuiet = TRUE;
      }
      else
      {
         MG.fBeQuiet = FALSE;
      }
      ArgArrayDone();
   }

   newbroker.nb_Port = MG.broker_mp;
   MG.cxsigflag = 1L << MG.broker_mp->mp_SigBit;

   if ( MG.broker = CxBroker(&newbroker, &errorcode) )
   {
      //
      // STRING-OUT filter
      //
      if (filterO = HotKey( szOutHotkey, MG.broker_mp, EVT_OUTHOTKEY ))
      {
         AttachCxObj( MG.broker, filterO );
         if ( !CxObjError( filterO ) )
         {
            //
            // STRING-IN filter
            //
            if (filterI = HotKey( szInHotkey, MG.broker_mp, EVT_INHOTKEY ))
            {
               AttachCxObj( MG.broker, filterI );
               if ( !CxObjError( filterI ) )
               {
                  ActivateCxObj( MG.broker, TRUE );
                  ProcessMsg( &MG );
               }
               else
               {
                  quit( &MG, "CX Object creation error - StringIN.");
               }
            }
            else
            {
               quit( &MG, "Invalid INHOTKEY string." );
            }
         }
         else
         {
            quit( &MG, "CX Object creation error - StringOUT.");
         }
      }
      else
      {
         quit( &MG, "Invalid OUTHOTKEY string." );
      }
   }
   else
   {
      //
      // an error has occured.. show user!
      //
      switch ( errorcode )
      {
         case CBERR_SYSERR:
            quit( &MG,  "Broker SYSTEM error:\nNo memory?" );
            break;
         case CBERR_DUP:
            //
            // this message is displayed in ProcessMsg(): 'already running...'
            //
            break;
         case CBERR_VERSION:
            quit( &MG,  "Broker VERSION error." );
            break;
      }
   }

   //
   // Empty message port of all remaining messages
   //
   while ( msg = (CxMsg *)GetMsg( MG.broker_mp ) )
      ReplyMsg( (struct Message *)msg );

   quit( &MG,  NULL );
}


void ProcessMsg( struct MyGlobals *mg )
{
   CxMsg             *msg;
   ULONG              sigrcvd,
                      msgid,
                      msgtype;
   BOOL               fKeepGoing = TRUE;
   struct Window     *wdwUser;
   struct Requester  *rqstptr;
   struct Gadget     *gadptr;


   static struct Requester dummyreq = {
      NULL,0,0,0,0,0,0,
      NULL,NULL,NULL,
      0,0,NULL,
      {0},NULL,NULL,NULL,{0}
   };

   while ( fKeepGoing == TRUE )
   {
      sigrcvd = Wait( SIGBREAKF_CTRL_C | mg->cxsigflag );

      while ( msg = (CxMsg *)GetMsg( mg->broker_mp ) )
      {
         msgid = CxMsgID( msg );
         msgtype = CxMsgType( msg );
         ReplyMsg( (struct Message *)msg );

         switch ( msgtype )
         {
            case CXM_IEVENT:
               switch ( msgid )
               {
                  case EVT_OUTHOTKEY:
                     //
                     // disable the current window by enabling any other
                     // window -- we'll use the first we can find; then
                     // re-enable the user's current window and the string
                     // gadget he/she was in will not be active!
                     //
                     wdwUser = IntuitionBase->ActiveWindow;
                     Request( &dummyreq, wdwUser );
                     Delay( mg->ulDelayValue );
                     EndRequest( &dummyreq, wdwUser );
                     break;

                  case EVT_INHOTKEY:
                     //
                     // enable the first string gadget of the current window
                     //
                     wdwUser = IntuitionBase->ActiveWindow;

                     rqstptr = wdwUser->FirstRequest;

                     if ( rqstptr )
                     {
                        // requesters have priority; use last requester in
                        // req list (actually, the last-activated requester
                        // is the FIRST in the list (newer requests are linked
                        // to the FRONT of the list attached to the window)
                        gadptr = rqstptr->ReqGadget;
                     }
                     else
                     {
                        // check window next
                        gadptr = wdwUser->FirstGadget;
                     }

                     // find first string gadget
                     do
                     {
                        if ( ( gadptr->GadgetType & GTYP_GTYPEMASK ) == GTYP_STRGADGET )
                        {
                           // only use this gadget if it isn't disabled
                           if ( ( gadptr->Flags & GFLG_DISABLED ) == 0 )
                           {
                              break;
                           }
                        }
                        gadptr = gadptr->NextGadget;
                     }
                     while ( gadptr );

                     // if we found a string gadget, activate it
                     if ( gadptr )
                     {
                        ActivateGadget( gadptr, wdwUser, rqstptr );
                     }
                     break;
               }
               break;

            case CXM_COMMAND:
               switch ( msgid )
               {
                  case CXCMD_DISABLE:
                     ActivateCxObj( mg->broker, FALSE );
                     break;

                  case CXCMD_ENABLE:
                     ActivateCxObj( mg->broker, TRUE );
                     break;

                  case CXCMD_KILL:
                     fKeepGoing = FALSE;
                     break;

                  case CXCMD_UNIQUE:
                     if ( mg->fBeQuiet == FALSE )
                     {
                        complain( mg, "StringInOut is already running!" );
                     }
                     break;
               }
               break;
         }
      }
      if ( sigrcvd & SIGBREAKF_CTRL_C )
      {
         fKeepGoing = FALSE;
      }
   }
}
