/*
 * This file is part of the Poliqarp suite.
 * 
 * Copyright (C) 2004-2009 by Instytut Podstaw Informatyki Polskiej
 * Akademii Nauk (IPI PAN; Institute of Computer Science, Polish
 * Academy of Sciences; cf. www.ipipan.waw.pl).  All rights reserved.
 * 
 * This file may be distributed and/or modified under the terms of the
 * GNU General Public License version 2 as published by the Free Software
 * Foundation and appearing in the file gpl.txt included in the packaging
 * of this file.  (See http://www.gnu.org/licenses/translations.html for
 * unofficial translations.)
 * 
 * A commercial license is available from IPI PAN (contact
 * Michal.Ciesiolka@ipipan.waw.pl or ipi@ipipan.waw.pl for more
 * information).  Licensees holding a valid commercial license from IPI
 * PAN may use this file in accordance with that license.
 * 
 * This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING
 * THE WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR
 * PURPOSE.
 */

#include <poliqarp-config.h>

#include <sakura/common/hash-table.h>

#include "bp-parser.h"
#include "list.h"
#include "syntax-parser.h"

static const int table_size = 16387;
static int group_id_seed = 0;

static struct group *group_create(struct syntax_parser *parser, const char *type)
{
   struct group *this = marena_alloc(&parser->arena, sizeof(*this));
   this->id = ++group_id_seed;
   this->type = marena_strdup(&parser->arena, type);
   return this;
}

static void group_append(struct syntax_parser *parser, struct hash_table *ht,
   struct group *group, const char *key)
{
   list_t list = (list_t)hash_table_get(ht, key);
   list = list_prepend(list, list_create_marena(group, &parser->arena));
   hash_table_set(ht, key, list);
}

static void start_handler(void *p, const xmlChar *xml_name, const xmlChar **xml_attr)
{
   const char *name = (const char*)xml_name;
   const char **attr = (const char**)xml_attr;
   struct syntax_parser *this = (struct syntax_parser *)p;
   if (strcmp(name, "group") == 0) {
      const char *from = NULL, *to = NULL, *synh = NULL, *semh = NULL, *type = NULL;
      while (attr && *attr) {
         const char *name = *attr++; 
         const char *value = *attr++;
         if (strcmp(name, "from") == 0)
            from = value;
         else if (strcmp(name, "to") == 0)
            to = value;
         else if (strcmp(name, "synh") == 0)
            synh = value;
         else if (strcmp(name, "semh") == 0)
            semh = value;
         else if (strcmp(name, "type") == 0)
            type = value;
      }
      if (type && from && to && 
          ((synh && semh) || (strcasecmp(type, "coordination") == 0 ||
                              strcasecmp(type, "conjunct") == 0)))
      {
         struct group *group = group_create(this, type);
         group_append(this, &this->htfrom, group, from);
         group_append(this, &this->htto, group, to);
         if (synh)
            group_append(this, &this->htsynh, group, synh);
         if (semh)
            group_append(this, &this->htsemh, group, semh);
      } else {
         vp_eprintf("%s/%s:%d: malformed group specification\n", this->dirname, 
            this->filename, this->xml_parser->input->line, name);
         this->num_errors++;
      }
   }
}

static void syntax_parser_work(struct bp_parser_env *env, 
   struct bp_parser *base, const char *pathname, const char *target_dir,
   const char *target_file)
{
   struct syntax_parser *this = (struct syntax_parser *)
      (((char *)base) + sizeof(*base));
   
   this->dirname = target_dir;
   this->filename = target_file;
   this->have_syntax = true;
   /* initialize hashtables */
   create_hash_table(&this->htfrom, table_size, HASHTABLE_DUPLICATE_KEYS, 
      &this->arena);
   create_hash_table(&this->htto, table_size, HASHTABLE_DUPLICATE_KEYS, 
      &this->arena);
   create_hash_table(&this->htsynh, table_size, HASHTABLE_DUPLICATE_KEYS, 
      &this->arena);
   create_hash_table(&this->htsemh, table_size, HASHTABLE_DUPLICATE_KEYS,
      &this->arena);
   group_id_seed = 0;
   if (generic_parse_loop(target_dir, target_file, this->xml_parser) == -1) {
      vp_eprintf("Some errors occurred while parsing syntax annotation file\n");
      this->num_errors++;
   }
   this->dirname = this->filename = NULL;
}

static int syntax_parser_postprocess(struct bp_parser *base)
{
   struct syntax_parser *this = (struct syntax_parser *)
      (((char *)base) + sizeof(*base));
   destroy_hash_table(&this->htfrom, NULL);
   destroy_hash_table(&this->htto, NULL);
   destroy_hash_table(&this->htsynh, NULL);
   destroy_hash_table(&this->htsemh, NULL);
   marena_release(&this->arena);
   this->have_syntax = false;
   return 0;
}

static void syntax_parser_destroy(struct bp_parser *base)
{
   struct syntax_parser *this = (void *)(((char *)base) + sizeof *base);
   marena_destroy(&this->arena);
   xmlFreeParserCtxt(this->xml_parser);
   free(base);
}

struct bp_parser *syntax_parser_create(const char *base_name)
{
   struct bp_parser *base;
   struct syntax_parser *this;

   base = malloc(sizeof(*base) + sizeof(*this));
   if (base == NULL)
      return NULL;
   base->destroy = syntax_parser_destroy;
   base->work = syntax_parser_work;
   base->postprocess = syntax_parser_postprocess;
   this = (struct syntax_parser *)(((char *)base) + sizeof(*base));

   this->sax_handler = bp_dummy_sax_handler;
   this->sax_handler.startElement = start_handler;
   this->xml_parser = xmlCreatePushParserCtxt(&this->sax_handler, this, NULL,
      0, NULL);
   base->num_errors = this->num_errors = 0;
   marena_create(&this->arena);
   if (this->xml_parser == NULL) {
      this->num_errors++;
      vp_eprintf("Couldn't allocate memory for parser");
   }
   this->have_syntax = false;
   return base;
}
