/*
    Tag-o-Miga
*/

/// "Includes"
#include <ctype.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>
#include "mytypes.h"
#include "g_exlist.h"
#include "tmiga.h"
#include "config.h"
#include "tagfile.h"
#include "macros.h"
#include "protos.h"

//#ifdef ACTIVATE_GUI
//#include "gui.h"
//#endif
///
/// "Defines"
#define CFG_USE 0
#define CFG_SAVE 1

enum modes { MODE_MOOD, MODE_HEADER, MODE_SIG, MODE_TAGLINE };
///
/// "Prototypes"
Prototype void realMain(void);
Local void cleanup (void);

Prototype void DoEdit (void);
Local void DoMessage (void);
Prototype char *GenerateMessage (void);
Local char *GenerateInfoLine (void);
Local void ChooseTagline (char *Filename, int p);
Prototype char *ExpandMacros(char *orig);
Prototype char *instr(char *big, char *little);

Local int GetFileLen (char *name);
Local void DoCommand (char *cmd);
Local BOOL IsQuoted (char *line);
///
/// "Global variables"
char *Fixes[5][4] = { "MOOD: ", "",  "MOOD:", "",
                      "",        "",  "",       "",
                      "[NP: ",   "]", "[NP: ",  "]",
                      "",        "",  "",       "",
                      "... ",    "",  "    ",   "" };
char Preview[5][512] = { "", "", "", "", "" };
char MsgMacro[6][256];
char EmailTo[256];
char *MsgPtr;
struct TagFile *TagFile[5] = { NULL, NULL, NULL, NULL, NULL };
ULONG bytes = 0, quoted = 0, tagcnt = 0, qpc = 0;
BOOL Cancel = FALSE, changed;

extern int Build;
extern char progPath[];
///

// Startup & exit
/// "main"
/* ----------------------------------- main ------------------------------------

 Comment: this is the main routine...

*/

void realMain(void)
{
    /* Set the auto-cleanup routine */
        atexit (cleanup);

    /* Get command line arguments */
    //GetArgs (argc, argv);

    /* Read preferences file */
    load_config_custom (arg_CONFIG);

    /* Main */
        DoMessage ();

    /* End */
        return;
}
///
/// "cleanup"
/* ---------------------------------- cleanup ----------------------------------

 Comment: called automatically when program ends for any reason

*/

void
cleanup()
{
    /* Clean up */
  //GetArgs_TidyUp ();
}
///

// Main section
/// DoEdit
/* ---------------------------------- DoEdit -----------------------------------

 Comment: edit the message

*/

void DoEdit()
{
    time_t t1 = time(NULL);
    struct tm *tp = localtime(&t1);
    unsigned int origlen, s, t;
    char linebuf[256], linebuf0[256], *buf, *ptr;
    FILE *EditF;

        changed = TRUE;

    /* Get length of edit file */
    origlen = GetFileLen (arg_EDITFILE);

    /* Sort out edit command */
    s = 0; t = 0;
    do {
        if (Config.EditCmd[s] == '@' && toupper (Config.EditCmd[s+1]) == 'A') {
            strcpy (&linebuf0[t], arg_ARGS);
            s += 2;
            t += strlen (arg_ARGS);
        } else {
            linebuf0[t++] = Config.EditCmd[s++];
        }
    } while (Config.EditCmd[s-1]);

    s = 0; t = 0;
        do {
        if (linebuf0[s] == '@' && toupper(linebuf0[s+1]) == 'F') {
            strcpy (&linebuf[t], arg_EDITFILE);
            s += 2;
            t += strlen (arg_EDITFILE);
        } else {
            linebuf[t++] = linebuf0[s++];
        }
    } while (linebuf0[s-1]);

    /* Run editor */
                DoCommand (linebuf);

                /* Has edit file changed? */
    bytes = GetFileLen (arg_EDITFILE);
    if (bytes == origlen) {
                changed = FALSE;
        return;
    }

    /* Read the edit file into memory */
    EditF = fopen (arg_EDITFILE, "r");
    if (!EditF) fail (20, "Couldn't read file %s", arg_EDITFILE);

    MsgPtr = malloc (bytes+1);
    if (!MsgPtr) fail (20, "Out of memory");

    *MsgPtr = 0;
    for (s = 0; s < 6; s++) MsgMacro[s][0] = 0;
        while (buf = ReadLine (EditF)) {
        if (strncmp (buf, "MSGTO: ", 6) == 0)
            strcpy (MsgMacro[0], &buf[7]);
        else if (strncmp (buf, "SUBJECT: ", 9) == 0)
            strcpy (MsgMacro[5], &buf[9]);
        else if (strnicmp (buf, "To: ", 4) == 0) {
            strcpy (EmailTo, &buf[4]);
            buf = ReadLine (EditF); /* Line after To: should be blank */
        } else {
            strcat (MsgPtr, buf);
            strcat (MsgPtr, "\n");
        }
    }

    fclose (EditF);

    /* Check to see if there is a To:, MSGTO: or SUBJECT: line in the file */
    ptr = MsgPtr;
    if (MsgMacro[0][0]) {
                                for (s = 0; MsgMacro[0][s]; s++) {
            MsgMacro[1][s] = MsgMacro[0][s];
            if (MsgMacro[0][s] == ' ') {
                MsgMacro[1][s] = 0;
                                break;
            }
        }
        t = 0;
        for (s = 0; MsgMacro[0][s]; s++)
            if (MsgMacro[0][s] == ' ') t = s + 1;
        strcpy (MsgMacro[2], &MsgMacro[0][t]);
    }

    /* Get message date and time */
    strftime (MsgMacro[3], 255, "%x", tp);
    strftime (MsgMacro[4], 255, "%X", tp);

    /* Check to see if there is already a tagline in the file */
    ptr = MsgPtr;
        while (buf = ReadLineFromMemory (&ptr)) {
        if (strncmp (buf, "... ", 4) == 0)
                        exit (0);
                if (strncmp (buf, " >> Tag-o-Miga", 14) == 0)
            exit (0);
    }

    /* Count the quoted lines */
    ptr = MsgPtr;
        while (buf = ReadLineFromMemory (&ptr)) {
        if (IsQuoted (buf))
            quoted += strlen (buf);
    }
    qpc = quoted * 100;
        qpc = qpc / bytes;
}
///
/// DoMessage
/* ---------------------------------- do_msg -----------------------------------

 Comment: Add everything to the message

*/

void DoMessage ()
{
    char *ptr;
    int p;
    BOOL ret = FALSE, guiret = TRUE;
    FILE *EditF;

                ret = ret; /* To prevent compiler warnings */

    if (Config.UseCount == 0)
        AlertUser(  "Welcome to " ID_NAME "\n\n"
                    "If you have any problems setting up this program,\n"
                    "the author will be pleased to help you out!  See\n"
                    "the documentation for contact information.\n\n"
                    "Don't forget, this program is Postcardware!", "OK");

    DoEdit ();
        if (changed == FALSE) return;

    for (p = 0; p < 5; p++) {
        if (Config.Part[p].Mode) {
            ChooseTagline (Config.Part[p].File, p);
        }
    }

#ifdef ACTIVATE_GUI
        if (Config.GUI) guiret = DoGUI ();
#endif

        if (guiret) {
                ptr = GenerateMessage ();
                if (!ptr) outofmemory ();

                EditF = fopen (arg_EDITFILE, "w");
                if (!EditF) fail (20, "Couldn't open edit file for writing");

                fprintf (EditF, "%s", ptr);
                fclose (EditF);
                free (ptr);

                if (Config.UseCount <= 25) {
                        Config.UseCount++;
                        save_config_custom (arg_CONFIG);
                        if (strcmp(arg_CONFIG, "ENV:Tag-o-Miga/Config") == 0)
                                save_config(CFG_SAVE);
                }
                if (Config.UseCount == 25) {
                        AlertUser(  "You seemed to have used this program 25 times\n"
                                                "now.  Remember that this program is Postcardware,\n"
                                                "so if you intend to use the program any more, you\n"
                                                "need to send me a postcard.  See the documentation\n"
                                                "for contact information.", "OK");
                }
        }
}
///
/// GenerateMessage
/* ------------------------------ GenerateMessage ------------------------------

 Comment: Pull together all the component parts

*/

char *GenerateMessage (void)
{
    unsigned int len = 1, p, q;
    char *msg, *ptr, *info, *buf;
    char *part[5], linebuf[256];

    /* Find out how long the parts are */
    for (p = 0; p < 5; p++) {
        part[p] = ExpandMacros (tagfile_get (TagFile[p]));
        //part[p] = tagfile_get (TagFile[p]);

        if (part[p] == NULL) {

            err_showerror ();
            fail (10, NULL);
        }
        if (part[p] != (char *)(-1)) {
            ptr = part[p];
            q = 0;
                        while (buf = ReadLineFromMemory (&ptr)) {
                len += strlen (Fixes[p][q]) + strlen (buf) +
                        strlen (Fixes[p][q+1]) + 1;
                q = 2;
            }
        }
    }

    /* Get the info line and add its length */
    info = GenerateInfoLine ();
    if (info)
        len += strlen (info);

    /* Add the length of the email To: line */
    if (EmailTo[0])
                                len += 6 + strlen (EmailTo);

    /* Add the length of the message itself */
    len += strlen (MsgPtr);

    /* Allocate the memory */
    msg = malloc (len);
    if (!msg) fail (20, "Out of memory");
    *msg = 0;

    /* Build the message */
    for (p = 0; p < 5; p++) {
        if (part[p] != (char *)(-1)) {
            ptr = part[p];
            q = 0;
                        while (buf = ReadLineFromMemory (&ptr)) {
                sprintf (linebuf, "%s%s%s\n",
                            Fixes[p][q],
                            buf,
                            Fixes[p][q+1]);
                q = 2;
                strcat (msg, linebuf);
            }
            free (part[p]);
        }

        /* After part 0, add the email To: line */
        if (p == 0 && EmailTo[0]) {
            strcat (msg, "To: ");
            strcat (msg, EmailTo);
            strcat (msg, "\n\n");
        }

        /* After part 1, add the message */
        if (p == 1)
                                                strcat (msg, MsgPtr);
    }
    /* Add info line */
    if (info) {
        strcat (msg, info);
                                free (info);
                }

                return msg;
}
///
/// GenerateInfoLine
/* ----------------------------- GenerateInfoLine ------------------------------

 Comment: Generate the info line

*/

char *GenerateInfoLine (void)
{
                char infoline[512], buf[512];

                if (Config.NoInfo) return NULL;

                sprintf (infoline, " << %s %s", ID_LONGNAME, ID_REL);

#ifdef PRERELEASE
                sprintf (buf, ".%d", Build);
                strcat (infoline, buf);
#endif

                if (qpc) {
                                sprintf (buf, " - Quoted %d%%", qpc);
                                strcat (infoline, buf);
                }

                if (TagFile[4] && !Config.NoTagName) {
                                sprintf (buf, " - %d tags in ", TagFile[4]->TagCount);
                                strcat (infoline, buf);
                                strcat (infoline, tagfile_getname(TagFile[4]));
                }

                strcat (infoline, " >>");

                return strdup (infoline);
}
///
/// ChooseTagline
/* ------------------------------- ChooseTagline -------------------------------

 Comment:  Pick a tagline at random from the given file, add it to the list

*/

void
ChooseTagline(char *Filename, int p)
{
        enum types { TAGLINE, ALTLIST, COMMENT };
        int direction = 1, type;
        BOOL done = FALSE;
        char *tag, *ptr, *buf;
        struct TagFile *newtf;

        newtf = tagfile_openfile (TagFile[p], Filename);
        if (newtf == NULL) return;
        TagFile[p] = newtf;

        Preview[p][0] = 0;

        tagfile_random (TagFile[p]);

        while (!done) {
                tag = tagfile_get (TagFile[p]);

                if (tag == NULL) {
                        err_showerror ();
                        fail (10, NULL);
                }
                if (tag == (char *)(-1)) break;

                /* Read the first line of the tagfile, for special keyword checking. */
                ptr = tag;
                buf = ReadLineFromMemory (&ptr);
                if (buf) {
                        if (strncmp (buf, "[ALTLIST]", 9) == 0) type = ALTLIST;
                        else if (strncmp (buf, "[COMMENT]", 9) == 0) type = COMMENT;
                        else type = TAGLINE;

                        switch (type) {
                        case TAGLINE:
                                /* This is a tagline.  Copy it to the preview buffer. */
                                strcpy (Preview[p], buf);
                                while (!TagFile[p]->Flags&TAGFILE_ONELINER) {
                                        buf = ReadLineFromMemory (&ptr);
                                        if (!buf) break;
                                        if (strcmp (buf, "%%\n") == 0) break;
                                        if (strlen (Preview[p]) + strlen (buf) > 510) break;
                                        strcat (Preview[p], buf);
                                }
                                done = TRUE;
                                break;

                        case ALTLIST:
                                /* This is an altlist, so call ChooseTagline with it. */
                                ChooseTagline (&buf[9], p);
                                done = TRUE;
                                break;

                        case COMMENT:
                                /* This is a comment */
                                if (tagfile_eof (TagFile[p])) direction = -1;
                                if (tagfile_bof (TagFile[p]) && direction == -1) {
                                        AlertUser ("File %s is empty!", "_OK", Filename);
                                        done = TRUE;
                                }
                                if (direction == 1) tagfile_next (TagFile[p]);
                                else tagfile_prev (TagFile[p]);
                                break;
                        }
                } else {
                        done = TRUE;
                }
                free (tag);
        }

        return;
}
///
/// ExpandMacros
/* ------------------------------- ExpandMacros --------------------------------

 Comment: Expands all the macros in the given memory block, frees it, and returns
          the exp block.

*/

char *ExpandMacros(char *orig)
{
    int l, i, j;
    BOOL changed = FALSE;
    char *p, *q, *exp;

    // return orig;

                if ((int)orig == -1) return orig;
    if (orig == NULL) return orig;

    l = strlen (orig);

    /* Check for macros */
    for (j = 0; j < 6; j++) {
        p = orig;
        i = strlen (Config.Macro[j]);
                while (q = instr (p, Config.Macro[j])) {
            l -= i;
            l += strlen (MsgMacro[j]);
            p = q + i;
            changed = TRUE;
        }
    }

    if (changed == FALSE)
        return orig;

    exp = malloc (l+1);
    if (exp == NULL)
        return orig;
    memset (exp, 0, l+1);

    for (p = orig, q = exp; *p; p++, q++) {
        *q = *p;
        for (j = 0; j < 6; j++) {
            if (strncmp (p, Config.Macro[j], strlen (Config.Macro[j])) == 0) {
                strcpy (q, MsgMacro[j]);
                while (*((char *)(q+1))) q++;
                p = p + strlen (Config.Macro[j]) - 1;
            }
        }
    }

    free (orig);
                return exp;
}
///
/// instr
/* ----------------------------------- instr -----------------------------------

 Comment: Search for little string in big string

*/

char *instr(char *big, char *little)
{
    if ((strlen (little) == 0) ||
        (strlen (big) == 0)) return NULL;

                while (*big) {
                                if (strncmp (big, little, strlen (little)) == 0) return big;
                                big++;
                }
                return NULL;
}
///

// Tools
/// GetFileLen
/* -------------------------------- GetFileLen ---------------------------------

 Comment: Returns the length of the given file, or -1 if error

*/

int GetFileLen (char *name)
{
                FILE *f;
                long p;

                f = fopen (name, "r");
                if (!f) return -1;

                p = fseek (f, 0, SEEK_END);
                p = ftell (f);

                fclose (f);
                return p;
}
///
/// DoCommand
/* --------------------------------- DoCommand ---------------------------------

 Comment: Run a command, check and handle return code

*/

void
DoCommand(char *cmd)
{
                int ret;

                ret = system (cmd);//, TAG_END);
                if (ret == -1) fail (20, "Couldn't launch command\n%s", cmd);

                if (ret > 0) {
                                ret = AlertUser (
                                                                "%s\nreturned code %d (%s)\n", "_Continue|_Quit", cmd, ret,
                                                                (ret>=20)?"FAIL":(ret>=10)?"ERROR":(ret>=5)?"WARN":"OK");
                }
}
///
/// IsQuoted
/* --------------------------------- IsQuoted ----------------------------------

 Comment: Check to see if a line is quoted

*/

BOOL
IsQuoted(char *line)
{
    int p;
    BOOL ret = FALSE;

    for (p = 0; p < 5; p++) {
        if (*line == '>') {
            ret = TRUE;
            break;
        }
        if (!isalpha (*line) && !isspace (*line)) {
            ret = FALSE;
            break;
        }
        line++;
    }

    return ret;
}

///

