/* ====================================================================
 * Copyright 1999 Web Juice, LLC. All rights reserved.
 *
 * template.c
 *
 * The end-user API for the templating library.
 *
 * ==================================================================== */

#include <sys/types.h>
#include <sys/stat.h>
#include <sys/param.h>
#include <stdlib.h>
#include <string.h>
#include <stdio.h>
#include <unistd.h>

#include <template.h>
#include <default_tags.h>


staglist_p simple_tags;
tagplist_p tag_pairs;
char otag[TEMPLATE_DELIMITER_SIZE] = "<!--#";
char ctag[TEMPLATE_DELIMITER_SIZE] = "-->";


int  parser(context_p ctx, int looping, char *input, char **output);


/* ====================================================================
 * NAME:          template_init
 *
 * DESCRIPTION:   Initializes the templating library end-user API.  This
 *                includes initializing a context and global tag lists,
 *                and registering all the default tags.
 *
 * RETURN VALUES: If there's a problem, returns NULL; otherwise returns
 *                the main context.
 *
 * BUGS:          Hopefully none.
 * ==================================================================== */
context_p
template_init(void)
{
    char *cwd;
    context_p ctx = context_init();

    if (ctx == NULL)
    {
        return NULL;
    }

    simple_tags = staglist_init();
    if (simple_tags == NULL)
    {
        return NULL;
    }

    tag_pairs = tagplist_init();
    if (tag_pairs == NULL)
    {
        return NULL;
    }

    if (! staglist_exists(simple_tags, "echo")) {
        template_register_simple("echo",    simple_tag_echo);
    }
    if (! staglist_exists(simple_tags, "include")) {
        template_register_simple("include", simple_tag_include);
    }

    if (! tagplist_is_closetag(tag_pairs, "comment", "endcomment")) {
        template_register_pair(0, "comment", "endcomment", tag_pair_comment);
    }
    if (! tagplist_is_closetag(tag_pairs, "loop", "endloop")) {
        template_register_pair(1, "loop",    "endloop",    tag_pair_loop);
    }
    if (! tagplist_is_closetag(tag_pairs, "if", "endif")) {
        template_register_pair(0, "if",      "endif",      tag_pair_if);
    }
    if (! tagplist_is_closetag(tag_pairs, "ifn", "endifn")) {
        template_register_pair(0, "ifn",     "endifn",     tag_pair_ifn);
    }
    if (! tagplist_is_closetag(tag_pairs, "debug", "enddebug")) {
        template_register_pair(0, "debug",   "enddebug",   tag_pair_debug);
    }

    cwd = getcwd(NULL, MAXPATHLEN);

    template_set_value(ctx, "INTERNAL_debug", "0");
    template_set_value(ctx, "INTERNAL_strip", "1");
    template_set_value(ctx, "INTERNAL_dir",   cwd);

    free(cwd);

    return(ctx);
}




/* ====================================================================
 * NAME:          template_set_debug
 *
 * DESCRIPTION:   Change the debugging level of this context.
 *
 * RETURN VALUES: 0 if there's a problem; 1 on success.
 *               
 * BUGS:          Hopefully none.
 * ==================================================================== */
int
template_set_debug(context_p ctx, int debug_level)
{
    if (ctx == NULL)
    {
        return(0);
    }

    if (debug_level == TEMPLATE_DEBUG_NONE)
    {
        return(template_set_value(ctx, "INTERNAL_debug", "0"));
    }
    else if (debug_level == TEMPLATE_DEBUG_SOME)
    {
        return(template_set_value(ctx, "INTERNAL_debug", "1"));
    }
    else if (debug_level == TEMPLATE_DEBUG_LOTS)
    {
        return(template_set_value(ctx, "INTERNAL_debug", "2"));
    }
    return(0);
}



/* ====================================================================
 * NAME:          template_set_strip
 *
 * DESCRIPTION:   Modify the behavior of the parser - if strip is on,
 *                the parser will not output the first character after
 *                a tag *if* it is a newline.  If strip is off, the parser
 *                outputs all non-tag characters.
 *
 * RETURN VALUES: 0 if there's a problem; 1 on success.
 *               
 * BUGS:          Hopefully none.
 * ==================================================================== */
int
template_set_strip(context_p ctx, int strip)
{
    if (ctx == NULL)
    {
        return(0);
    }

    if (strip)
    {
        return(template_set_value(ctx, "INTERNAL_strip", "1"));
    }
    else
    {
        return(template_set_value(ctx, "INTERNAL_strip", "0"));
    }
    return(0);
}

/* ====================================================================
 * NAME:          template_set_dir
 *
 * DESCRIPTION:   Set the directory in which templates will be sought
 *                by template_parse_file() and the include tag.
 *
 * RETURN VALUES: 0 if there's a problem; 1 on success.
 *                                                    
 * BUGS:          Hopefully none.
 * ==================================================================== */
int
template_set_dir(context_p ctx, char *directory)
{
    if (ctx == NULL)
    {
        return(0);
    }

    if (directory == NULL)
    {
        return(0);
    }

    return(template_set_value(ctx, "INTERNAL_dir", directory));
}



/* ====================================================================
 * NAME:          template_set_delimiters
 *
 * DESCRIPTION:   Allows the user to set the opening and closing delimiters
 *                for template tags.
 *
 * RETURN VALUES: 0 if there's a problem; 1 on success.
 *
 * BUGS:          Hopefully none.
 * ==================================================================== */
int
template_set_delimiters(char *opentag, char *closetag)
{
    if ((strlen(opentag) > TEMPLATE_DELIMITER_SIZE)
        || (strlen(closetag) > TEMPLATE_DELIMITER_SIZE))
    {
        return 0;
    }

    strncpy(otag,  opentag, TEMPLATE_DELIMITER_SIZE);
    strncpy(ctag, closetag, TEMPLATE_DELIMITER_SIZE);

    return 1;
}



/* ====================================================================
 * NAME:          template_register_simple
 *
 * DESCRIPTION:   Registers a new simple tag into the global simple tag list.
 *
 * RETURN VALUES: The return of staglist_register (true or false)
 *
 * BUGS:          Should it verify that the tag name isn't taken as a tag
 *                pair name?
 * ==================================================================== */
int
template_register_simple(char *name,
                         void (*function)(context_p, char **, int, char**))
{
    return(staglist_register(simple_tags, name, function));
}



/* ====================================================================
 * NAME:          template_register_pair
 *
 * DESCRIPTION:   Registers a new tag pair into the global tag pair list.
 *
 * RETURN VALUES: The return of tagplist_register (true or false)
 *
 * BUGS:          Should it verify that the tag name isn't taken as a 
 *                simple tag name?
 * ==================================================================== */
int
template_register_pair(char named_context, char *open_name, char *close_name,
                       void (*function)(context_p, int, char**))
{
    return(tagplist_register(tag_pairs, named_context, open_name, close_name,
                             function));
}



/* ====================================================================
 * NAME:          template_set_value
 *
 * DESCRIPTION:   Sets a variable within the given context to a new value.
 *
 * RETURN VALUES: The return of context_set_value (true or false)
 *
 * BUGS:          Should it be a macro?
 * ==================================================================== */
int
template_set_value(context_p ctx, char *name, char *value)
{
    return(context_set_value(ctx, name, value));
}



/* ====================================================================
 * NAME:          template_parse_file
 *
 * DESCRIPTION:   Takes a filename and context as input - reads the file,
 *                runs it through parser(), and makes sure that everything
 *                goes okay.
 *
 * RETURN VALUES: Returns 0 if there are any problems with the file or parser,
 *                otherwise returns 1.
 *
 * BUGS:          Hopefully none.
 * ==================================================================== */
int
template_parse_file(context_p ctx, char *template_filename, char **output)
{
    struct stat finfo;
    FILE        *filehandle;
    char        *template;
    char        *real_filename;
    int         retval;

    *output = NULL;

    if ((template_filename == NULL) || (output == NULL))
    {
        return 0;
    }

    if (stat(template_filename, &finfo) != 0)
    {
        int size = strlen(template_filename)
                 + strlen(context_get_value(ctx, "INTERNAL_dir")) + 1;
        real_filename = (char *)malloc(size);
        snprintf(real_filename, size, "%s%s",
                 context_get_value(ctx, "INTERNAL_dir"), template_filename);
        if (stat(real_filename, &finfo) != 0)
        {
            free(real_filename);
            return 0;
        }
    }
    else
    {
        real_filename = (char *)malloc(strlen(template_filename) + 1);
        strcpy(real_filename, template_filename);
    }

    if ((filehandle = fopen(real_filename, "r")) == NULL)
    {
        free(real_filename);
        return 0;
    }

    template = (char *)malloc(finfo.st_size + 1);
    if (template == NULL)
    {
        free(real_filename);
        fclose(filehandle);
        return 0;
    }

    fread(template, 1, finfo.st_size, filehandle);
    template[finfo.st_size] = '\0';

    fclose(filehandle);

    retval = parser(ctx, 1, template, output);
    free(real_filename);
    free(template);

    return(retval);
}



/* ====================================================================
 * NAME:          template_parse_string
 *
 * DESCRIPTION:   Takes an input string and context as input - runs the
 *                input through parser().
 *
 * RETURN VALUES: Returns 0 if the parsing fails, 1 otherwise.
 *
 * BUGS:          Hopefully none.
 * ==================================================================== */
int
template_parse_string(context_p ctx, char *tmpl, char **output)
{
    return(parser(ctx, 1, tmpl, output));
}



/* ====================================================================
 * NAME:          template_destroy
 *
 * DESCRIPTION:   Frees up all the memory used by the templating library.
 *
 * RETURN VALUES: None.
 *
 * BUGS:          Hopefully none.
 * ==================================================================== */
void
template_destroy(context_p ctx)
{
    context_destroy(ctx);
    staglist_destroy(simple_tags);
    simple_tags = NULL;
    tagplist_destroy(tag_pairs);
    tag_pairs = NULL;
}