/* -*- Mode: C; indent-tabs-mode:nil; c-basic-offset: 8-*- */

/*
 * This file is part of The Croco Library
 *
 * This program is free software; you can redistribute it and/or
 * modify it under the terms of version 2.1 of the GNU Lesser General Public
 * License as published by the Free Software Foundation.
 *
 * 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 Lesser General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
 * USA
 *
 * Author: Dodji Seketeli
 * See COPYRIGHTS file for copyright information.
 */

#include <string.h>
#include "cr-utils.h"
#include "cr-om-parser.h"

/**
 *@CROMParser:
 *
 *The definition of the CSS Object Model Parser.
 *This parser uses (and sits) the SAC api of libcroco defined
 *in cr-parser.h and cr-doc-handler.h
 */

struct _CROMParserPriv {
        CRParser *parser;
};

#define PRIVATE(a_this) ((a_this)->priv)

/*
 *Forward declaration of a type defined later
 *in this file.
 */
struct _ParsingContext;
typedef struct _ParsingContext ParsingContext;

static ParsingContext *new_parsing_context (void);

static void destroy_context (ParsingContext * a_ctxt);

static void unrecoverable_error (CRDocHandler * a_this);

static void error (CRDocHandler * a_this);

static void property (CRDocHandler * a_this,
                      CRString * a_name, 
                      CRTerm * a_expression, 
                      gboolean a_important);

static void end_selector (CRDocHandler * a_this, 
                          CRSelector * a_selector_list);

static void start_selector (CRDocHandler * a_this, 
                            CRSelector * a_selector_list);

static void start_font_face (CRDocHandler * a_this,
                             CRParsingLocation *a_location);

static void end_font_face (CRDocHandler * a_this);

static void end_document (CRDocHandler * a_this);

static void start_document (CRDocHandler * a_this);

static void charset (CRDocHandler * a_this, 
                     CRString * a_charset,
                     CRParsingLocation *a_location);

static void start_page (CRDocHandler * a_this, CRString * a_page,
                        CRString * a_pseudo_page, 
                        CRParsingLocation *a_location);

static void end_page (CRDocHandler * a_this, CRString * a_page, 
                      CRString * a_pseudo_page);

static void start_media (CRDocHandler * a_this, 
                         GList * a_media_list,
                         CRParsingLocation *a_location);

static void end_media (CRDocHandler * a_this, 
                       GList * a_media_list);

static void import_style (CRDocHandler * a_this, 
                          GList * a_media_list,
                          CRString * a_uri, 
                          CRString * a_uri_default_ns,
                          CRParsingLocation *a_location);

struct _ParsingContext {
        CRStyleSheet *stylesheet;
        CRStatement *cur_stmt;
        CRStatement *cur_media_stmt;
};

/********************************************
 *Private methods
 ********************************************/

static ParsingContext *
new_parsing_context (void)
{
        ParsingContext *result = NULL;

        result = g_try_malloc (sizeof (ParsingContext));
        if (!result) {
                cr_utils_trace_info ("Out of Memory");
                return NULL;
        }
        memset (result, 0, sizeof (ParsingContext));
        return result;
}

static void
destroy_context (ParsingContext * a_ctxt)
{
        g_return_if_fail (a_ctxt);

        if (a_ctxt->stylesheet) {
                cr_stylesheet_destroy (a_ctxt->stylesheet);
                a_ctxt->stylesheet = NULL;
        }
        if (a_ctxt->cur_stmt) {
                cr_statement_destroy (a_ctxt->cur_stmt);
                a_ctxt->cur_stmt = NULL;
        }
        g_free (a_ctxt);
}

static enum CRStatus
cr_om_parser_init_default_sac_handler (CROMParser * a_this)
{
        CRDocHandler *sac_handler = NULL;
        gboolean created_handler = FALSE;
        enum CRStatus status = CR_OK;

        g_return_val_if_fail (a_this && PRIVATE (a_this)
                              && PRIVATE (a_this)->parser,
                              CR_BAD_PARAM_ERROR);

        status = cr_parser_get_sac_handler (PRIVATE (a_this)->parser,
                                            &sac_handler);
        g_return_val_if_fail (status == CR_OK, status);

        if (!sac_handler) {
                sac_handler = cr_doc_handler_new ();
                created_handler = TRUE;
        }

        /*
         *initialize here the sac handler.
         */
        sac_handler->start_document = start_document;
        sac_handler->end_document = end_document;
        sac_handler->start_selector = start_selector;
        sac_handler->end_selector = end_selector;
        sac_handler->property = property;
        sac_handler->start_font_face = start_font_face;
        sac_handler->end_font_face = end_font_face;
        sac_handler->error = error;
        sac_handler->unrecoverable_error = unrecoverable_error;
        sac_handler->charset = charset;
        sac_handler->start_page = start_page;
        sac_handler->end_page = end_page;
        sac_handler->start_media = start_media;
        sac_handler->end_media = end_media;
        sac_handler->import_style = import_style;

        if (created_handler) {
                status = cr_parser_set_sac_handler (PRIVATE (a_this)->parser,
                                                    sac_handler);
                cr_doc_handler_unref (sac_handler);
        }

        return status;

}

static void
start_document (CRDocHandler * a_this)
{
        ParsingContext *ctxt = NULL;
        CRStyleSheet *stylesheet = NULL;

        g_return_if_fail (a_this);

        ctxt = new_parsing_context ();
        g_return_if_fail (ctxt);

        stylesheet = cr_stylesheet_new (NULL);
        ctxt->stylesheet = stylesheet;
        cr_doc_handler_set_ctxt (a_this, ctxt);
}

static void
start_font_face (CRDocHandler * a_this,
                 CRParsingLocation *a_location)
{
        enum CRStatus status = CR_OK;
        ParsingContext *ctxt = NULL;
        ParsingContext **ctxtptr = NULL;

        (void) a_location;

        g_return_if_fail (a_this);

        g_return_if_fail (a_this);
	ctxtptr = &ctxt;
        status = cr_doc_handler_get_ctxt (a_this, (gpointer *) ctxtptr);
        g_return_if_fail (status == CR_OK && ctxt);
        g_return_if_fail (ctxt->cur_stmt == NULL);

        ctxt->cur_stmt =
                cr_statement_new_at_font_face_rule (ctxt->stylesheet, NULL);

        g_return_if_fail (ctxt->cur_stmt);
}

static void
end_font_face (CRDocHandler * a_this)
{
        enum CRStatus status = CR_OK;
        ParsingContext *ctxt = NULL;
        ParsingContext **ctxtptr = NULL;
        CRStatement *stmts = NULL;

        g_return_if_fail (a_this);

        g_return_if_fail (a_this);
	ctxtptr = &ctxt;
        status = cr_doc_handler_get_ctxt (a_this, (gpointer *) ctxtptr);
        g_return_if_fail (status == CR_OK && ctxt);
        g_return_if_fail
                (ctxt->cur_stmt
                 && ctxt->cur_stmt->type == AT_FONT_FACE_RULE_STMT
                 && ctxt->stylesheet);

        stmts = cr_statement_append (ctxt->stylesheet->statements,
                                     ctxt->cur_stmt);
        if (!stmts)
                goto error;

        ctxt->stylesheet->statements = stmts;
        stmts = NULL;
        ctxt->cur_stmt = NULL;

        return;

      error:

        if (ctxt->cur_stmt) {
                cr_statement_destroy (ctxt->cur_stmt);
                ctxt->cur_stmt = NULL;
        }

        if (!stmts) {
                cr_statement_destroy (stmts);
                stmts = NULL;
        }
}

static void
end_document (CRDocHandler * a_this)
{
        enum CRStatus status = CR_OK;
        ParsingContext *ctxt = NULL;
        ParsingContext **ctxtptr = NULL;

        g_return_if_fail (a_this);
	ctxtptr = &ctxt;
        status = cr_doc_handler_get_ctxt (a_this, (gpointer *) ctxtptr);
        g_return_if_fail (status == CR_OK && ctxt);

        if (!ctxt->stylesheet || ctxt->cur_stmt)
                goto error;

        status = cr_doc_handler_set_result (a_this, ctxt->stylesheet);
        g_return_if_fail (status == CR_OK);

        ctxt->stylesheet = NULL;
        destroy_context (ctxt);
        cr_doc_handler_set_ctxt (a_this, NULL);

        return;

      error:
        if (ctxt) {
                destroy_context (ctxt);
        }
}

static void
charset (CRDocHandler * a_this, CRString * a_charset,
         CRParsingLocation *a_location)
{
        enum CRStatus status = CR_OK;
        CRStatement *stmt = NULL,
                *stmt2 = NULL;
        CRString *charset = NULL;

        ParsingContext *ctxt = NULL;
        ParsingContext **ctxtptr = NULL;

        (void) a_location;

        g_return_if_fail (a_this);
	ctxtptr = &ctxt;
        status = cr_doc_handler_get_ctxt (a_this, (gpointer *) ctxtptr);
        g_return_if_fail (status == CR_OK && ctxt);
        g_return_if_fail (ctxt->stylesheet);

        charset = cr_string_dup (a_charset) ;
        stmt = cr_statement_new_at_charset_rule (ctxt->stylesheet, charset);
        if (stmt)
                stmt2 = cr_statement_append (ctxt->stylesheet->statements, stmt);
        if (!stmt2) {
                if (stmt) {
                        cr_statement_destroy (stmt);
                        stmt = NULL;
                }
                if (charset) {
                        cr_string_destroy (charset);
                }
                return;
        }
        ctxt->stylesheet->statements = stmt2;
        stmt2 = NULL;
}

static void
start_page (CRDocHandler * a_this, 
            CRString * a_page, 
            CRString * a_pseudo,
            CRParsingLocation *a_location)
{
        enum CRStatus status = CR_OK;
        ParsingContext *ctxt = NULL;
        ParsingContext **ctxtptr = NULL;

        (void) a_location;

        g_return_if_fail (a_this);
	ctxtptr = &ctxt;
        status = cr_doc_handler_get_ctxt (a_this, (gpointer *) ctxtptr);
        g_return_if_fail (status == CR_OK && ctxt);
        g_return_if_fail (ctxt->cur_stmt == NULL);

        ctxt->cur_stmt = cr_statement_new_at_page_rule
                (ctxt->stylesheet, NULL, NULL, NULL);
        if (a_page) {
                ctxt->cur_stmt->kind.page_rule->name =
                        cr_string_dup (a_page) ;

                if (!ctxt->cur_stmt->kind.page_rule->name) {
                        goto error;
                }
        }
        if (a_pseudo) {
                ctxt->cur_stmt->kind.page_rule->pseudo =
                        cr_string_dup (a_pseudo) ;
                if (!ctxt->cur_stmt->kind.page_rule->pseudo) {
                        goto error;
                }
        }
        return;

 error:
        if (ctxt->cur_stmt) {
                cr_statement_destroy (ctxt->cur_stmt);
                ctxt->cur_stmt = NULL;
        }
}

static void
end_page (CRDocHandler * a_this, 
          CRString * a_page, 
          CRString * a_pseudo_page)
{
        enum CRStatus status = CR_OK;
        ParsingContext *ctxt = NULL;
        ParsingContext **ctxtptr = NULL;
        CRStatement *stmt = NULL;

        (void) a_page;
        (void) a_pseudo_page;

        g_return_if_fail (a_this);

	ctxtptr = &ctxt;
        status = cr_doc_handler_get_ctxt (a_this, (gpointer *) ctxtptr);

        g_return_if_fail (status == CR_OK && ctxt);

        g_return_if_fail (ctxt->cur_stmt
                          && ctxt->cur_stmt->type == AT_PAGE_RULE_STMT
                          && ctxt->stylesheet);

        stmt = cr_statement_append (ctxt->stylesheet->statements,
                                    ctxt->cur_stmt);

        if (stmt) {
                ctxt->stylesheet->statements = stmt;
                stmt = NULL;
                ctxt->cur_stmt = NULL;
        }

        if (ctxt->cur_stmt) {
                cr_statement_destroy (ctxt->cur_stmt);
                ctxt->cur_stmt = NULL;
        }
        a_page = NULL;          /*keep compiler happy */
        a_pseudo_page = NULL;   /*keep compiler happy */
}

static void
start_media (CRDocHandler * a_this, 
             GList * a_media_list,
             CRParsingLocation *a_location)
{
        enum CRStatus status = CR_OK;
        ParsingContext *ctxt = NULL;
        ParsingContext **ctxtptr = NULL;
        GList *media_list = NULL;

        (void) a_location;

        g_return_if_fail (a_this);
	ctxtptr = &ctxt;
        status = cr_doc_handler_get_ctxt (a_this, (gpointer *) ctxtptr);
        g_return_if_fail (status == CR_OK && ctxt);

        g_return_if_fail (ctxt
                          && ctxt->cur_stmt == NULL
                          && ctxt->cur_media_stmt == NULL
                          && ctxt->stylesheet);
        if (a_media_list) {
                /*duplicate the media_list */
                media_list = cr_utils_dup_glist_of_cr_string 
                        (a_media_list);
        }
        ctxt->cur_media_stmt =
                cr_statement_new_at_media_rule
                (ctxt->stylesheet, NULL, media_list);

}

static void
end_media (CRDocHandler * a_this, GList * a_media_list)
{
        enum CRStatus status = CR_OK;
        ParsingContext *ctxt = NULL;
        ParsingContext **ctxtptr = NULL;
        CRStatement *stmts = NULL;

        (void) a_media_list;

        g_return_if_fail (a_this);

	ctxtptr = &ctxt;
        status = cr_doc_handler_get_ctxt (a_this, (gpointer *) ctxtptr);

        g_return_if_fail (status == CR_OK && ctxt);

        g_return_if_fail (ctxt
                          && ctxt->cur_media_stmt
                          && ctxt->cur_media_stmt->type == AT_MEDIA_RULE_STMT
                          && ctxt->stylesheet);

        stmts = cr_statement_append (ctxt->stylesheet->statements,
                                     ctxt->cur_media_stmt);

        if (!stmts) {
                cr_statement_destroy (ctxt->cur_media_stmt);
                ctxt->cur_media_stmt = NULL;
        }

        ctxt->stylesheet->statements = stmts;
        stmts = NULL;

        ctxt->cur_stmt = NULL ;
        ctxt->cur_media_stmt = NULL ;
        a_media_list = NULL;
}

static void
import_style (CRDocHandler * a_this, 
              GList * a_media_list,
              CRString * a_uri, 
              CRString * a_uri_default_ns,
              CRParsingLocation *a_location)
{
        enum CRStatus status = CR_OK;
        CRString *uri = NULL;
        CRStatement *stmt = NULL,
                *stmt2 = NULL;
        ParsingContext *ctxt = NULL;
        ParsingContext **ctxtptr = NULL;
        GList *media_list = NULL ;

        (void) a_uri_default_ns;
        (void) a_location;

        g_return_if_fail (a_this);

	ctxtptr = &ctxt;
        status = cr_doc_handler_get_ctxt (a_this, (gpointer *) ctxtptr);

        g_return_if_fail (status == CR_OK && ctxt);

        g_return_if_fail (ctxt->stylesheet);

        uri = cr_string_dup (a_uri) ;

        if (a_media_list)
                media_list = cr_utils_dup_glist_of_cr_string (a_media_list) ;

        stmt = cr_statement_new_at_import_rule
                (ctxt->stylesheet, uri, media_list, NULL);

        if (!stmt)
                goto error;

        if (ctxt->cur_stmt) {
                stmt2 = cr_statement_append (ctxt->cur_stmt, stmt);
                if (!stmt2)
                        goto error;
                ctxt->cur_stmt = stmt2;
                stmt2 = NULL;
                stmt = NULL;
        } else {
                stmt2 = cr_statement_append (ctxt->stylesheet->statements,
                                             stmt);
                if (!stmt2)
                        goto error;
                ctxt->stylesheet->statements = stmt2;
                stmt2 = NULL;
                stmt = NULL;
        }

        return;

      error:
        if (uri) {
                cr_string_destroy (uri);
        }

        if (stmt) {
                cr_statement_destroy (stmt);
                stmt = NULL;
        }
        a_uri_default_ns = NULL; /*keep compiler happy */
}

static void
start_selector (CRDocHandler * a_this, CRSelector * a_selector_list)
{
        enum CRStatus status = CR_OK ;
        ParsingContext *ctxt = NULL;
        ParsingContext **ctxtptr = NULL;

        g_return_if_fail (a_this);
	ctxtptr = &ctxt;
        status = cr_doc_handler_get_ctxt (a_this, (gpointer *) ctxtptr);
        g_return_if_fail (status == CR_OK && ctxt);
        if (ctxt->cur_stmt) {
                /*hmm, this should be NULL so free it */
                cr_statement_destroy (ctxt->cur_stmt);
                ctxt->cur_stmt = NULL;
        }

        ctxt->cur_stmt = cr_statement_new_ruleset
                (ctxt->stylesheet, a_selector_list, NULL, NULL);
}

static void
end_selector (CRDocHandler * a_this, CRSelector * a_selector_list)
{
        enum CRStatus status = CR_OK;
        ParsingContext *ctxt = NULL;
        ParsingContext **ctxtptr = NULL;

        (void) a_selector_list;

        g_return_if_fail (a_this);

	ctxtptr = &ctxt;
        status = cr_doc_handler_get_ctxt (a_this, (gpointer *) ctxtptr);

        g_return_if_fail (status == CR_OK && ctxt);

        g_return_if_fail (ctxt->cur_stmt && ctxt->stylesheet);

        if (ctxt->cur_stmt) {
                CRStatement *stmts = NULL;

                if (ctxt->cur_media_stmt) {
                        CRAtMediaRule *media_rule = NULL;

                        media_rule = ctxt->cur_media_stmt->kind.media_rule;

                        stmts = cr_statement_append
                                (media_rule->rulesets, ctxt->cur_stmt);

                        if (!stmts) {
                                cr_utils_trace_info
                                        ("Could not append a new statement");
                                cr_statement_destroy (media_rule->rulesets);
                                ctxt->cur_media_stmt->
                                        kind.media_rule->rulesets = NULL;
                                return;
                        }
                        media_rule->rulesets = stmts;
                        ctxt->cur_stmt = NULL;
                } else {
                        stmts = cr_statement_append
                                (ctxt->stylesheet->statements,
                                 ctxt->cur_stmt);
                        if (!stmts) {
                                cr_utils_trace_info
                                        ("Could not append a new statement");
                                cr_statement_destroy (ctxt->cur_stmt);
                                ctxt->cur_stmt = NULL;
                                return;
                        }
                        ctxt->stylesheet->statements = stmts;
                        ctxt->cur_stmt = NULL;
                }

        }

        a_selector_list = NULL; /*keep compiler happy */
}

static void
property (CRDocHandler * a_this,
          CRString * a_name, 
          CRTerm * a_expression, 
          gboolean a_important)
{
        enum CRStatus status = CR_OK;
        ParsingContext *ctxt = NULL;
        ParsingContext **ctxtptr = NULL;
        CRDeclaration *decl = NULL,
                *decl2 = NULL;
        CRString *str = NULL;

        g_return_if_fail (a_this);
	ctxtptr = &ctxt;
        status = cr_doc_handler_get_ctxt (a_this, (gpointer *) ctxtptr);
        g_return_if_fail (status == CR_OK && ctxt);

        /*
         *make sure a current ruleset statement has been allocated
         *already.
         */
        g_return_if_fail
                (ctxt->cur_stmt
                 &&
                 (ctxt->cur_stmt->type == RULESET_STMT
                  || ctxt->cur_stmt->type == AT_FONT_FACE_RULE_STMT
                  || ctxt->cur_stmt->type == AT_PAGE_RULE_STMT));

        if (a_name) {
                str = cr_string_dup (a_name);
                g_return_if_fail (str);
        }

        /*instantiates a new declaration */
        decl = cr_declaration_new (ctxt->cur_stmt, str, a_expression);
        if (decl == NULL)
                g_clear_pointer (&str, cr_string_destroy);
        g_return_if_fail (decl);
        str = NULL;
        decl->important = a_important;
        /*
         *add the new declaration to the current statement
         *being build.
         */
        switch (ctxt->cur_stmt->type) {
        case RULESET_STMT:
                decl2 = cr_declaration_append
                        (ctxt->cur_stmt->kind.ruleset->decl_list, decl);
                if (!decl2) {
                        cr_utils_trace_info
                                ("Could not append decl to ruleset");
                        goto error;
                }
                ctxt->cur_stmt->kind.ruleset->decl_list = decl2;
                decl = NULL;
                decl2 = NULL;
                break;

        case AT_FONT_FACE_RULE_STMT:
                decl2 = cr_declaration_append
                        (ctxt->cur_stmt->kind.font_face_rule->decl_list,
                         decl);
                if (!decl2) {
                        cr_utils_trace_info
                                ("Could not append decl to ruleset");
                        goto error;
                }
                ctxt->cur_stmt->kind.font_face_rule->decl_list = decl2;
                decl = NULL;
                decl2 = NULL;
                break;
        case AT_PAGE_RULE_STMT:
                decl2 = cr_declaration_append
                        (ctxt->cur_stmt->kind.page_rule->decl_list, decl);
                if (!decl2) {
                        cr_utils_trace_info
                                ("Could not append decl to ruleset");
                        goto error;
                }
                ctxt->cur_stmt->kind.page_rule->decl_list = decl2;
                decl = NULL;
                decl2 = NULL;
                break;

        default:
                goto error;
                break;
        }

        return;

      error:
        if (str) {
                g_free (str);
                str = NULL;
        }

        if (decl) {
                cr_declaration_destroy (decl);
                decl = NULL;
        }
}

static void
error (CRDocHandler * a_this)
{
        enum CRStatus status = CR_OK;
        ParsingContext *ctxt = NULL;
        ParsingContext **ctxtptr = NULL;

        g_return_if_fail (a_this);
	ctxtptr = &ctxt;
        status = cr_doc_handler_get_ctxt (a_this, (gpointer *) ctxtptr);
        g_return_if_fail (status == CR_OK && ctxt);

        if (ctxt->cur_stmt) {
                cr_statement_destroy (ctxt->cur_stmt);
                ctxt->cur_stmt = NULL;
        }
}

static void
unrecoverable_error (CRDocHandler * a_this)
{
        enum CRStatus status = CR_OK;
        ParsingContext *ctxt = NULL;
        ParsingContext **ctxtptr = NULL;

	ctxtptr = &ctxt;
        status = cr_doc_handler_get_ctxt (a_this, (gpointer *) ctxtptr);
        g_return_if_fail (status == CR_OK);

        if (ctxt) {
                if (ctxt->stylesheet) {
                        status = cr_doc_handler_set_result
                                (a_this, ctxt->stylesheet);
                        g_return_if_fail (status == CR_OK);
                }
                g_free (ctxt);
                cr_doc_handler_set_ctxt (a_this, NULL);
        }
}

/********************************************
 *Public methods
 ********************************************/

/**
 * cr_om_parser_new:
 *@a_input: the input stream.
 *
 *Constructor of the CROMParser.
 *Returns the newly built instance of #CROMParser.
 */
CROMParser *
cr_om_parser_new (CRInput * a_input)
{
        CROMParser *result = NULL;
        enum CRStatus status = CR_OK;

        result = g_try_malloc (sizeof (CROMParser));

        if (!result) {
                cr_utils_trace_info ("Out of memory");
                return NULL;
        }

        memset (result, 0, sizeof (CROMParser));
        PRIVATE (result) = g_try_malloc (sizeof (CROMParserPriv));

        if (!PRIVATE (result)) {
                cr_utils_trace_info ("Out of memory");
                goto error;
        }

        memset (PRIVATE (result), 0, sizeof (CROMParserPriv));

        PRIVATE (result)->parser = cr_parser_new_from_input (a_input);

        if (!PRIVATE (result)->parser) {
                cr_utils_trace_info ("parsing instantiation failed");
                goto error;
        }

        status = cr_om_parser_init_default_sac_handler (result);

        if (status != CR_OK) {
                goto error;
        }

        return result;

      error:

        if (result) {
                cr_om_parser_destroy (result);
        }

        return NULL;
}

/**
 * cr_om_parser_parse_buf:
 *@a_this: the current instance of #CROMParser.
 *@a_buf: the in memory buffer to parse.
 *@a_len: the length of the in memory buffer in number of bytes.
 *@a_enc: the encoding of the in memory buffer.
 *@a_result: out parameter the resulting style sheet
 *
 *Parses the content of an in memory  buffer.
 *
 *Returns CR_OK upon successful completion, an error code otherwise.
 */
enum CRStatus
cr_om_parser_parse_buf (CROMParser * a_this,
                        const guchar * a_buf,
                        gulong a_len,
                        enum CREncoding a_enc, CRStyleSheet ** a_result)
{

        enum CRStatus status = CR_OK;

        g_return_val_if_fail (a_this && a_result, CR_BAD_PARAM_ERROR);

        if (!PRIVATE (a_this)->parser) {
                PRIVATE (a_this)->parser = cr_parser_new (NULL);
        }

        status = cr_parser_parse_buf (PRIVATE (a_this)->parser,
                                      a_buf, a_len, a_enc);

        if (status == CR_OK) {
                CRStyleSheet *result = NULL;
                CRStyleSheet **resultptr = NULL;
                CRDocHandler *sac_handler = NULL;

                cr_parser_get_sac_handler (PRIVATE (a_this)->parser,
                                           &sac_handler);
                g_return_val_if_fail (sac_handler, CR_ERROR);
		resultptr = &result;
                status = cr_doc_handler_get_result (sac_handler,
                                                    (gpointer *) resultptr);
                g_return_val_if_fail (status == CR_OK, status);

                if (result)
                        *a_result = result;
        }

        return status;
}

/**
 * cr_om_parser_simply_parse_buf:
 *@a_buf: the css2 in memory buffer.
 *@a_len: the length of the in memory buffer.
 *@a_enc: the encoding of the in memory buffer.
 *@a_result: out parameter. The resulting css2 style sheet.
 *
 *The simpler way to parse an in memory css2 buffer.
 *
 *Returns CR_OK upon successful completion, an error code otherwise.
 */
enum CRStatus
cr_om_parser_simply_parse_buf (const guchar * a_buf,
                               gulong a_len,
                               enum CREncoding a_enc,
                               CRStyleSheet ** a_result)
{
        CROMParser *parser = NULL;
        enum CRStatus status = CR_OK;

        parser = cr_om_parser_new (NULL);
        if (!parser) {
                cr_utils_trace_info ("Could not create om parser");
                cr_utils_trace_info ("System possibly out of memory");
                return CR_ERROR;
        }

        status = cr_om_parser_parse_buf (parser, a_buf, a_len,
                                         a_enc, a_result);

        if (parser) {
                cr_om_parser_destroy (parser);
                parser = NULL;
        }

        return status;
}

/**
 * cr_om_parser_parse_file:
 *@a_this: the current instance of the cssom parser.
 *@a_file_uri: the uri of the file. 
 *(only local file paths are supported so far)
 *@a_enc: the encoding of the file.
 *@a_result: out parameter. A pointer 
 *the build css object model.
 *
 *Parses a css2 stylesheet contained
 *in a file.
 *
 * Returns CR_OK upon successful completion, an error code otherwise.
 */
enum CRStatus
cr_om_parser_parse_file (CROMParser * a_this,
                         const guchar * a_file_uri,
                         enum CREncoding a_enc, CRStyleSheet ** a_result)
{
        enum CRStatus status = CR_OK;

        g_return_val_if_fail (a_this && a_file_uri && a_result,
                              CR_BAD_PARAM_ERROR);

        if (!PRIVATE (a_this)->parser) {
                PRIVATE (a_this)->parser = cr_parser_new_from_file
                        (a_file_uri, a_enc);
        }

        status = cr_parser_parse_file (PRIVATE (a_this)->parser,
                                       a_file_uri, a_enc);

        if (status == CR_OK) {
                CRStyleSheet *result = NULL;
                CRStyleSheet **resultptr = NULL;
                CRDocHandler *sac_handler = NULL;

                cr_parser_get_sac_handler (PRIVATE (a_this)->parser,
                                           &sac_handler);
                g_return_val_if_fail (sac_handler, CR_ERROR);
		resultptr = &result;
                status = cr_doc_handler_get_result
                        (sac_handler, (gpointer *) resultptr);
                g_return_val_if_fail (status == CR_OK, status);
                if (result)
                        *a_result = result;
        }

        return status;
}

/**
 * cr_om_parser_simply_parse_file:
 *@a_file_path: the css2 local file path.
 *@a_enc: the file encoding.
 *@a_result: out parameter. The returned css stylesheet.
 *Must be freed by the caller using cr_stylesheet_destroy.
 *
 *The simpler method to parse a css2 file.
 *
 *Returns CR_OK upon successful completion, an error code otherwise.
 *Note that this method uses cr_om_parser_parse_file() so both methods
 *have the same return values.
 */
enum CRStatus
cr_om_parser_simply_parse_file (const guchar * a_file_path,
                                enum CREncoding a_enc,
                                CRStyleSheet ** a_result)
{
        CROMParser *parser = NULL;
        enum CRStatus status = CR_OK;

        parser = cr_om_parser_new (NULL);
        if (!parser) {
                cr_utils_trace_info ("Could not allocate om parser");
                cr_utils_trace_info ("System may be out of memory");
                return CR_ERROR;
        }

        status = cr_om_parser_parse_file (parser, a_file_path,
                                          a_enc, a_result);
        if (parser) {
                cr_om_parser_destroy (parser);
                parser = NULL;
        }

        return status;
}

/**
 * cr_om_parser_parse_paths_to_cascade:
 *@a_this: the current instance of #CROMParser
 *@a_author_path: the path to the author stylesheet
 *@a_user_path: the path to the user stylesheet
 *@a_ua_path: the path to the User Agent stylesheet
 *@a_encoding: the encoding of the sheets.
 *@a_result: out parameter. The resulting cascade if the parsing
 *was okay
 *
 *Parses three sheets located by their paths and build a cascade
 *
 *Returns CR_OK upon successful completion, an error code otherwise
 */
enum CRStatus
cr_om_parser_parse_paths_to_cascade (CROMParser * a_this,
                                     const guchar * a_author_path,
                                     const guchar * a_user_path,
                                     const guchar * a_ua_path,
                                     enum CREncoding a_encoding,
                                     CRCascade ** a_result)
{
        enum CRStatus status = CR_OK;

        /*0->author sheet, 1->user sheet, 2->UA sheet */
        CRStyleSheet *sheets[3];
        guchar *paths[3];
        CRCascade *result = NULL;
        gint i = 0;

        g_return_val_if_fail (a_this, CR_BAD_PARAM_ERROR);

        memset (sheets, 0, sizeof (CRStyleSheet*) * 3);
        paths[0] = (guchar *) a_author_path;
        paths[1] = (guchar *) a_user_path;
        paths[2] = (guchar *) a_ua_path;

        for (i = 0; i < 3; i++) {
                status = cr_om_parser_parse_file (a_this, paths[i],
                                                  a_encoding, &sheets[i]);
                if (status != CR_OK) {
                        if (sheets[i]) {
                                cr_stylesheet_unref (sheets[i]);
                                sheets[i] = NULL;
                        }
                        continue;
                }
        }
        result = cr_cascade_new (sheets[0], sheets[1], sheets[2]);
        if (!result) {
                for (i = 0; i < 3; i++) {
                        cr_stylesheet_unref (sheets[i]);
                        sheets[i] = 0;
                }
                return CR_ERROR;
        }
        *a_result = result;
        return CR_OK;
}

/**
 * cr_om_parser_simply_parse_paths_to_cascade:
 *@a_author_path: the path to the author stylesheet
 *@a_user_path: the path to the user stylesheet
 *@a_ua_path: the path to the User Agent stylesheet
 *@a_encoding: the encoding of the sheets.
 *@a_result: out parameter. The resulting cascade if the parsing
 *was okay
 *
 *Parses three sheets located by their paths and build a cascade
 *
 *Returns CR_OK upon successful completion, an error code otherwise
 */
enum CRStatus
cr_om_parser_simply_parse_paths_to_cascade (const guchar * a_author_path,
                                            const guchar * a_user_path,
                                            const guchar * a_ua_path,
                                            enum CREncoding a_encoding,
                                            CRCascade ** a_result)
{
        enum CRStatus status = CR_OK;
        CROMParser *parser = NULL;

        parser = cr_om_parser_new (NULL);
        if (!parser) {
                cr_utils_trace_info ("could not allocated om parser");
                cr_utils_trace_info ("System may be out of memory");
                return CR_ERROR;
        }
        status = cr_om_parser_parse_paths_to_cascade (parser,
                                                      a_author_path,
                                                      a_user_path,
                                                      a_ua_path,
                                                      a_encoding, a_result);
        if (parser) {
                cr_om_parser_destroy (parser);
                parser = NULL;
        }
        return status;
}

/**
 * cr_om_parser_destroy:
 *@a_this: the current instance of #CROMParser.
 *
 *Destructor of the #CROMParser.
 */
void
cr_om_parser_destroy (CROMParser * a_this)
{
        g_return_if_fail (a_this && PRIVATE (a_this));

        if (PRIVATE (a_this)->parser) {
                cr_parser_destroy (PRIVATE (a_this)->parser);
                PRIVATE (a_this)->parser = NULL;
        }

        if (PRIVATE (a_this)) {
                g_free (PRIVATE (a_this));
                PRIVATE (a_this) = NULL;
        }

        if (a_this) {
                g_free (a_this);
                a_this = NULL;
        }
}
