#include <stdio.h>
#include "folder.h"
#include "foldert.h"
#include "profile.h"
#include "end.h"
#include "memory.h"
#include "altime.h"
#include "al.h"
#include "data.h"
#include "mh_folder.h"
#include "msg.h"
#include "ruleset.h"
#include "RSreg.h"
#include "FTreg.h"
#include "MTreg.h"
#include "registry.h"
#include "rule.h"
#include "rulerun.h"
#include "sysdep.h"
#include "verbrgy.h"
#include "MsgType.h"

/* Dummy out some unwritten functions */
/* #define AlData_set_option(str)    Bool_TRUE */

/* Defines used for debugging aids */
#ifndef MY_DEBUG
#define MY_DEBUG  0
#endif

#if (MY_DEBUG == 1)
#define Al_rr_debug(s1)  printf("%s\n", s1);
#else
#define Al_rr_debug(s1)  
#endif

/* Some basic useful constants */
#define MINARGC 1
#define OR ||

/* String constants used for help options, and documentation */
#define USAGEPARMS  \
 "ruleset [folder] [-Mmailer_type] [-n] [-S[on/off]] [-U[on/off]] [-w]\
 [-D[option]] [-Ttime] [-help]"

/*------------------------------------------------------------------------ */
/* Note:   -Ttime   Valid time strings are defined by the AlTime_set func. */

#define HELP_STRING  "\n\
 ------------------------------------------------------------------------ \n\
  Valid Parameters are:                                                   \n\
  ---------------------                                                   \n\
  -Mmailer_type     Specifies the type of folder (how it should be        \n\
                    interpreted). The default is:   -Mmh                  \n\
  -n                Specifies that nested sub-folders are to be read. The \n\
                    default is to NOT do sub-folders (sub-directories).   \n\
  -S[on/off]        Saving output of the state information. The           \n\
                    default is to not save state information (i.e. - read \n\
                    state info in when processing a message, if it exists,\n\
                    but do not write out when done processing the message).\n\
                    The -S option will cause all state info read or       \n\
                    created to be saved with the message, it can be       \n\
                    immediately followed by 'on' or 'off' as is appropriate.\n\
  -Ttime            Flags the time that the runrule runner is being fired \n\
                    as a string 'incoming', 'outgoing', etc..             \n\
  -U[on/off]        Save output of the 'permanent' user property information.\n\
                    The default is to save user property information with \n\
                    the message (i.e. - read any existing user properties \n\
                    information in when processing a message, if it exists,\n\
                    and write out all 'permanent' user properties when done\n\
                    processing the message). Note that only user properties\n\
                    which have been set 'permanent' can be saved. The -U  \n\
                    option can be immediately followed by 'on' or 'off',  \n\
                    as is appropriate, to make the intent explicit.       \n\
  -D[option]        Choose Data Collection option desired. If \"-d\" is   \n\
                    used, with no option specified, then all off is used. \n\
                    The default is all on, if \"-d\" is not specified.    \n\
                    Some valid options are:       on     off              \n\
                    -d option can be used more than once on command line. \n\
  -w                Suppress top level warning msgs. Warnings are         \n\
                    when msg file is not accessible (no read access), etc.\n\
  --                Last '-' option. This allows a ruleset name or folder \n\
                    name to be a filename that begins with a dash ('-').  \n\
                    (Either \"--\" or \"- \" is valid.)                       \n\
\n\
** Example1:  runrules test.rul inbox -n \n\
** Example2:  runrules -Mmh -Soff -Uon -Tincoming -Doff -w -n test.rul inbox\n\
** Example3:  runrules -Mmh -Soff -Uon -Tincoming -Doff -w -n -- -rs.rul inbox\n\
\n\
** NOTE ** : The ruleset MUST occur before the folder in the command line.\n\
             If the folder is omitted, standard input is used (when possible).\n\
             The '-' parameters may occur in any order.                   \n\
             Parameters taking arguments are a single capital letter,     \n\
             directly followed by the argument (with no spaces).          \n\
 ------------------------------------------------------------------------ \n\
"

/* Note: Data Collection function "Al_set_data_collection_level(str)"       */
/* ----- is assumed to take a string as an argument, and return a Bool_TRUE */
/*       if successful, and a Bool_FALSE if the string passed was invalid.  */
/* ------------------------------------------------------------------------ */
#define DEFAULT_DATA_COLLECTION_OPTION "off"
#define INVALID_EXTRA_ARGUMENT \
  " Error :: Only ruleset and folder can follow \"--\". Invalid extra: %s\n"
#define INVALID_ONOFF_ARGUMENT \
  " Error :: Invalid option to parameter. Should be on or off, not: %s\n"

/* Note :  Tobey promised the following functions. For now, they are  */
/* defined as the same thing. This may change or not, time will tell. */
/* They are #defined here for ease of changing in the future.         */
/* ------------------------------------------------------------------ */
#define AlRSreg_get_by_name(ruleset_string)  AlRSreg_find (ruleset_string)
#define AlRSreg_get_by_file(ruleset_string)  AlRSreg_find (ruleset_string)

main(argc, argv)
     int argc;
     char *argv[];
 {
   int i;
   Bool use_ruleset_name;
   AlRuleSet the_ruleset;
   AlFolder the_folder;
   AlFolderType folder_type;
   Msg the_msg;
   char *ruleset_string;
   char *folder_string;
   char *folder_type_name;
   Bool turn_on_recursion;
   Bool suppress_external_state_usage;
   Bool suppress_external_uprop_usage;
   Bool allow_bad_ruleset;   /* allow data collection, even if bad ruleset */
   Bool not_more_parameters; /* this stops processing of argv's as options */
                             /* and allows '-' to be the 1st char of a     */
                             /* foldername or ruleset name. (e.g. :        */
                             /* runrules -- -myruleset -myfolder           */
   char *al_time_str;        /* run time specified in command line         */

   if (argc <= MINARGC)
     { fprintf(stderr, "Usage: %s %s\n", argv[0], USAGEPARMS);
       exit(0);
     };

   /* Initialize registry of folder types, and other stuff */
   /* ---------------------------------------------------- */
   Al_rr_debug(" RR DEBUG: before Al_init()");
   Al_init();
   Al_rr_debug(" RR DEBUG: after Al_init()");
   folder_type_name = AlFolderT_default_name();
   al_time_str = "";
   allow_bad_ruleset = Bool_FALSE;
   use_ruleset_name = Bool_FALSE;
   not_more_parameters = Bool_FALSE;
   ruleset_string = NULL;
   folder_string = NULL;

   /* Initialize the Folder Default values */
   /* ------------------------------------ */
   turn_on_recursion = Bool_FALSE;
   suppress_external_state_usage
              = Bool_not(AlProfile_get_BL_val(ALPROF_SAVSTAT));
   suppress_external_uprop_usage
              = Bool_not(AlProfile_get_BL_val(ALPROF_SAVPROP));

   /* Handle all of the command line options */
   /* -------------------------------------- */
   i = 0;
   Al_rr_debug(" RR DEBUG: before while()");
   while (argc > ++i)
     { if (not_more_parameters = Bool_FALSE)    /* After a "--", allow files */
	 { if (ruleset_string == NULL)          /* with '-' as 1st character */
	     ruleset_string = argv[i];          /* allow -ruleset name       */
           else if (folder_string == NULL)      /* allow -folder name        */
	     folder_string = argv[i];           /* ------------------------- */
           else
             { Al_fatal_error1(INVALID_EXTRA_ARGUMENT, argv[i]);
	     };
	 }
       else if (*argv[i] != '-')
	 { if (ruleset_string == NULL)             /* Set the rule set name. */
	     ruleset_string = argv[i];             /* ---------------------- */
	   else if (folder_string == NULL)
	     folder_string = argv[i];                /* Set the folder name. */
           else                                      /* -------------------- */
             { Al_fatal_error1(" Error :: Invalid extra argument: %s\n",
			      argv[i]);    /* Extra parameters, Fatal Error. */
	     };                            /* ------------------------------ */
	 }
       else if ((strcmp(argv[i], "-") == 0)
                OR (strcmp(argv[i], "--") == 0))
                                          /* Flag end of '-' arg processing. */
         not_more_parameters = Bool_TRUE; /* ------------------------------- */
       else 
       if (argv[i][1] ==  'M')           /* Set folder's mailer/folder type. */
	 {                               /* -------------------------------- */
	     folder_type_name = &(argv[i][2]);
	 }
       else if (strcmp(argv[i], "-help") == 0)      /* Display helpful info. */
	 { fprintf(stderr,                          /* --------------------- */
                   "Help follows (square brackets signify optional items):\n");
           fprintf(stderr, "\nUsage: %s %s\n", argv[0], USAGEPARMS);
	   fprintf(stderr, "%s\n", HELP_STRING);
	   exit(0);
	 }
       else if (strcmp(argv[i], "-n") == 0)  /* Do nested folders (sub-dir). */
	 turn_on_recursion = Bool_TRUE;      /* ---------------------------- */
       else if (argv[i][1] == 'S')
	 {                                /* Flag to save State information. */
                                          /* ------------------------------- */
           if ((strlen(argv[i]) == 2) OR (strcmp(&(argv[i][2]), "on") == 0))
             { /* Turn on state saving. */
               suppress_external_state_usage = Bool_FALSE;
               AlProfile_set_BL_val(ALPROF_SAVSTAT, Bool_TRUE);
	     }
	   else if (strcmp(&(argv[i][2]), "off") == 0)
	     { /* Turn off state saving. */
               suppress_external_state_usage = Bool_TRUE;
               AlProfile_set_BL_val(ALPROF_SAVSTAT, Bool_FALSE);
	     }
	   else
	     { Al_fatal_error1(INVALID_ONOFF_ARGUMENT, argv[i]);
	     };
	 }
       else if (argv[i][1] == 'U')
	 {                                   /* Flag to save User properties */
                                             /* ---------------------------- */
           if ((strlen(argv[i]) == 2) OR (strcmp(&(argv[i][2]), "on") == 0))
             { /* Turn on user property saving. */
               suppress_external_uprop_usage = Bool_FALSE;
               AlProfile_set_BL_val(ALPROF_SAVPROP, Bool_TRUE);
	     }
	   else if (strcmp(&(argv[i][2]), "off") == 0)
	     { /* Turn off user property saving. */
               suppress_external_uprop_usage = Bool_TRUE;
               AlProfile_set_BL_val(ALPROF_SAVPROP, Bool_FALSE);
	     }
	   else
	     { Al_fatal_error1(INVALID_ONOFF_ARGUMENT, argv[i]);
	     };
	 }
       else if (argv[i][1] == 'T')                        /* Set time sting. */
	 { al_time_str = &(argv[i][2]);                   /* --------------- */
	   allow_bad_ruleset = Bool_TRUE;   /* allow data collection, anyway */
	 }
       else if (strcmp(argv[i], "-D") == 0)      /* Set data collection off. */
	 {                                       /* ------------------------ */
	   AlData_set_option(DEFAULT_DATA_COLLECTION_OPTION);
	 }
       else if (argv[i][1] == 'D')          /* Set a data collection option. */
	 {                                  /* ----------------------------- */
	   if (AlData_set_option(&(argv[i][2])) == Bool_FALSE)
	     { Al_fatal_error1(
		     "Error :: Invalid Data Collect Option specified: %s\n",
		     argv[i]);
	     };
	 }
       else if (strcmp(argv[i], "-w") == 0)    /* Suppress warning messages. */
         Al_suppress_warnings();               /* -------------------------- */
       else
	 { Al_warning2("Warning:: %s %s\n", "Unknown parameter:", argv[i]);
	   fprintf(stderr,
                   "Help follows (square brackets signify optional items):\n");
           fprintf(stderr, "Usage: %s %s\n", argv[0], USAGEPARMS);
	   fprintf(stderr, "%s\n", HELP_STRING);
	   exit(0);
	 };
     };       /* end of while (argc > ++i) */

   Al_rr_debug(" RR DEBUG: after while()");

   /* Set the time of rules run string, and validate it. */
   /* -------------------------------------------------- */
   if (AlTime_set(al_time_str) == Bool_FALSE)
     { Al_fatal_error1("Error :: Invalid Time (-t option) specified: %s\n",
	 	       al_time_str);
     };

   Al_rr_debug(" RR DEBUG: after AlTime_set()");

   /* Create the Rule Set Object */
   /* -------------------------- */
   if (use_ruleset_name == Bool_TRUE)
     { Al_rr_debug(" RR DEBUG: use_ruleset_name == Bool_TRUE");
       if (((the_ruleset = AlRSreg_get_by_name(ruleset_string)) == NULL)
	   && (allow_bad_ruleset == Bool_FALSE))
         { Al_fatal_error1(
                 "Error in Rule Set. Bad rule set name specified: %s\n",
		 ruleset_string);
	 };
     }
   else 
     { Al_rr_debug(" RR DEBUG: use_ruleset_name == Bool_FALSE");
       if (((the_ruleset = AlRSreg_get_by_file(ruleset_string)) == NULL)
	   && (allow_bad_ruleset == Bool_FALSE))
         { Al_fatal_error1(
                       "Error in Rule Set. Bad rule set file specified: %s\n",
		       ruleset_string);
	 };
     };

   /* Create a Folder object. (Note: Folder is not actually opened until */
   /* the first used, i.e. - AlFolder_get_next_msg() is executed.        */
   /* ------------------------------------------------------------------ */
   Al_rr_debug(" RR DEBUG: before AlFolderT_get_folder_type()");
   if ((folder_type = AlFolderT_get_folder_type(folder_type_name)) == NULL)
     { Al_fatal_error1(
                    "Error ::  Unknown mailer/folder type specified:  -M%s\n",
		    folder_type_name);
     }
   else       /* Update the profile to reflect any overrides. */
     AlProfile_set_CP_val(ALPROF_MAILUA, folder_type_name);

   Al_rr_debug(" RR DEBUG: before AlFolder_create_from...()");
   if (folder_string != NULL)
     { if ((the_folder = AlFolder_create_from_file(folder_string,
                                                folder_type,
					        turn_on_recursion,
					        suppress_external_state_usage,
						suppress_external_uprop_usage,
						FOLDER_OPEN_RW))
	   == NULL)
         { Al_fatal_error1("Unable to create a folder from: %s\n",
			  folder_string);
	 };
       Al_rr_debug(" RR DEBUG: after AlFolder_create_from_file()");
     }
   else if ((the_folder = (AlFolder)
                            AlFolder_create_from_stdin(folder_type,
					        turn_on_recursion,
					        suppress_external_state_usage,
						suppress_external_uprop_usage))
	   == NULL)
         { Al_fatal_error1(
              "Unable to create the folder from standard input, of type: %s\n",
	      folder_type_name);
	 };

   /* Initialize:  Open the Folder and get first Message object, then */
   /* --------------------------------------------------------------- */
   if ((the_msg = AlFolder_get_next_msg(the_folder)) == NULL)
     { 
#if (MY_DEBUG == 1)
   AlFolder_print_self(the_folder, stdout);
   AlMsg_print_self(the_msg, stdout);
#endif
       if (AlFolder_get_error_code(the_folder) == 0)
         { Al_fatal_error1(
            "Error in Folder:: Unable to read first message from folder: %s\n",
            folder_string);
	  }
       else
         { Al_fatal_error1("Error in Folder :: %s\n",
		     AlFolder_get_error_msg(the_folder));
	 };
     };

   /* Loop through each Message object, and run the rules on each. */
   /* ------------------------------------------------------------ */
   Al_rr_debug(" RR DEBUG: before for() loop");
   for (;
	the_msg != NULL;    /* the_msg equals NULL when none are left to do */
	the_msg = AlFolder_get_next_msg(the_folder))
     { Al_rr_debug(" RR DEBUG: before AlData_msg_received()");
       AlData_msg_received(the_msg);    /* do data collection, if on */
       Al_rr_debug(" RR DEBUG: after AlData_msg_received()");
       if (AlFolder_get_error_code(the_folder) != 0)
	 { Al_rr_debug(" RR DEBUG: got error code");
	   if (AlMsg_get_error_code(the_msg) != 0)
	     Al_warning2("Folder Error :: %s\n Msg Error :: %s\n",
			    AlFolder_get_error_msg(the_folder),
			    AlMsg_get_error_msg(the_msg));
	   else if (AlFolder_get_error_code(the_folder) != 0)
	     { Al_warning2("Folder Error :: %s %s\n",
			    AlFolder_get_error_msg(the_folder),
			    " ");
	     }
	   else
	     { Al_warning1(
                 "Error :: Unable to sucessfully read any files from:%s\n",
                 AlFolder_get_name(the_folder));
	     };
	   Al_rr_debug(" RR DEBUG: after getting error code");
	 };
       if (the_ruleset != NULL)
	 { Al_rr_debug(" RR DEBUG: before AlRuleRun_do_rules()");
	   AlRuleRun_do_rules(the_folder, the_msg, the_ruleset);
	   Al_rr_debug(" RR DEBUG: after AlRuleRun_do_rules()");
	 };
     };
   Al_rr_debug(" RR DEBUG: before AlFolder_destroy()");
   AlFolder_destroy(the_folder);
   Al_rr_debug(" RR DEBUG: before AlRuleSet_destroy()");
   Al_rr_debug(" RR DEBUG: before Al_cleanup()");
   Al_cleanup();
   Al_rr_debug(" RR DEBUG: after Al_cleanup()");

 };              /* end of Rule Runner main()  */

