/* This is -*- C -*- */
/* vim: set sw=2: */
/* $Id$ */

/*
 * rhymecore.c
 *
 * Copyright (C) 2003 The Free Software Foundation, Inc.
 *
 */

/*
 * This program is free software; you can redistribute it and/or
 * modify it under the terms of the GNU General Public License as
 * published by the Free Software Foundation; either version 2 of the
 * License, or (at your option) any later version.
 * 
 * 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 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.
 */

#ifdef CONFIG_H
#include <config.h>
#endif
#include "rhymecore.h"

#include <Python.h>
#include <string.h>
#include "phoneme.h"

typedef struct _RhymeTree RhymeTree;
struct _RhymeTree {
  int code;
  RhymeTree *branch[PHONEME_LAST];
  PyObject *list;
};

static RhymeTree *rhyme_tree_root = NULL;
static RhymeTree *rhyme_tree_slanted_root = NULL;

static RhymeTree *
node_new (int code)
{
  RhymeTree *node = (RhymeTree *) malloc (sizeof (RhymeTree));
  memset(node, 0, sizeof (RhymeTree));
  node->code = code;
  return node;
}

PyObject *
rhyme_index_word (PyObject *self, PyObject *args)
{
  PyObject *word, *decomp, *py_phon, *tuple;
  RhymeTree *node, *slant_node;
  int i, phon, code, scode;

  if (! PyArg_ParseTuple (args, "OO", &word, &decomp))
    return NULL;

  if (! PyString_Check (word)) {
    /* FIXME */
    return NULL;
  }

  if (! PySequence_Check (decomp)) {
    /* FIXME */
    return NULL;
  }

  if (rhyme_tree_root == NULL) 
    rhyme_tree_root = node_new (0);

  if (rhyme_tree_slanted_root == NULL) 
    rhyme_tree_slanted_root = node_new (0);

  node = rhyme_tree_root;
  slant_node = rhyme_tree_slanted_root;

  i = PySequence_Size (decomp) - 1;
  while (i >= 0) {
    py_phon = PySequence_GetItem (decomp, i);
    if (! PyInt_Check (py_phon)) {
      /* FIXME */
      return NULL;
    }
    phon = PyInt_AsLong (py_phon);
    code = PHONEME_TO_CODE (phon);
    scode = code;
    if (PHONEME_IS_VOWEL (scode))
      scode = 0;
    
    if (node->branch[code] == NULL)
      node->branch[code] = node_new (code);
    node = node->branch[code];

    if (slant_node->branch[scode] == NULL)
      slant_node->branch[scode] = node_new (scode);
    slant_node = slant_node->branch[scode];

    if (PHONEME_IS_STRESSED (phon))
      break;

    --i;
  }

  if (i >= 0) {
    if (node->list == NULL)
      node->list = PyList_New (0);
    if (slant_node->list == NULL)
      slant_node->list = PyList_New (0);
    
    tuple = Py_BuildValue ("(OO)", word, decomp);
    PyList_Append (node->list, tuple);
    PyList_Append (slant_node->list, tuple);
    Py_DECREF (tuple);
  }

  Py_INCREF (Py_None);
  return Py_None;
}


/* ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** */

static PyObject *
get_candidates (PyObject *decomp, int slant)
{
  RhymeTree *node;
  PyObject *py_phon;
  int i, phon, code;
  
  if (slant)
    node = rhyme_tree_slanted_root;
  else
    node = rhyme_tree_root;

  if (node == NULL) {
    /* FIXME */
    return NULL;
  }

  if (! PySequence_Check (decomp)) {
    /* FIXME */
    return NULL;
  }

  i = PySequence_Size (decomp) - 1;

  while (i >= 0) {
    py_phon = PySequence_GetItem (decomp, i);
    if (! PyInt_Check (py_phon)) {
      /* FIXME */
      return NULL;
    }
    phon = PyInt_AsLong (py_phon);
    if (slant && PHONEME_IS_VOWEL (phon))
      code = 0;
    else
      code = PHONEME_TO_CODE (phon);

    node = node->branch[code];
    if (node == NULL || PHONEME_IS_STRESSED (phon))
      break;
    
    --i;
  }

  if (i >= 0 && node && node->list) {
    Py_INCREF (node->list);
    return node->list;
  }

  return NULL;

}

PyObject *
rhyme_get_candidates(PyObject *self, PyObject *args)
{
  PyObject *decomp, *candidates;

 if (! PyArg_ParseTuple (args, "O", &decomp))
    return NULL;

  if (! PySequence_Check (decomp)) {
    /* FIXME */
    return NULL;
  }

  candidates = get_candidates (decomp, 0);
  if (candidates == NULL) {
    candidates = PyList_New (0);
  }

  return candidates;
}

PyObject *
rhyme_get_candidates_slanted(PyObject *self, PyObject *args)
{
  PyObject *decomp, *candidates;

 if (! PyArg_ParseTuple (args, "O", &decomp))
    return NULL;

  if (! PySequence_Check (decomp)) {
    /* FIXME */
    return NULL;
  }

  candidates = get_candidates (decomp, 1);
  if (candidates == NULL) {
    candidates = PyList_New (0);
  }

  return candidates;
}
  
/* ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** */

static PyMethodDef rhymecore_methods[] = {
  { "index_word", rhyme_index_word, METH_VARARGS,
    "Add a word to the rhyme index." },

  { "get_candidates", rhyme_get_candidates, METH_VARARGS,
    "Get a list of rhyme candidates for a given decomposition." },

  { "get_candidates_slanted", rhyme_get_candidates_slanted, METH_VARARGS,
    "Get a list of slanted rhyme candidates for a given decomposition." },
  
  { NULL, NULL, 0, NULL }
};

void
initrhymecore (void)
{
  PyObject *m, *d;

  m = Py_InitModule ("rhymecore", rhymecore_methods);
  d = PyModule_GetDict (m);
}
