/*
 * Yet another daemon library especially designed to be used
 * with libsxmp based daemons.
 *
 * (c) Alexander Vdolainen 2016 <avdolainen@zoho.com>
 *
 * libsxmp is free software: you can redistribute it and/or modify it
 * under the terms of the GNU Lesser General Public License as published
 * by the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * libsxmp 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 Lesser 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, see <http://www.gnu.org/licenses/>.";
 *
 */

#include <stdlib.h>
#include <stdio.h>
#include <errno.h>
#include <string.h>
#include <pthread.h>
#include <sys/stat.h>
#include <fcntl.h>

#include <tdata/usrtc.h>
#include <sexpr/sexp.h>

#include <ydaemon/ydaemon.h>

static long __cmp_cstrs(const void *a, const void *b)
{
  return (long)(strcmp((const char *)a, (const char *)b));
}

static void __scm_func_tree_init(scm_function_tree_t *pt, void *priv)
{
  usrtc_t *foost = (usrtc_t *)&(pt->functions);

  usrtc_init(foost, USRTC_SPLAY, MAX_SCM_FUNCTIONS, __cmp_cstrs);
  return;
}

static scm_function_tree_t *__scm_func_tree_alloc(void *priv)
{
  scm_function_tree_t *t = malloc(sizeof(scm_function_tree_t));

  if(!t) goto __fail;
  else __scm_func_tree_init(t, priv);

 __fail:
  return t;
}

void scm_func_tree_free(scm_function_tree_t *pt)
{
  usrtc_t *ftree = &(pt->functions);
  usrtc_node_t *node = NULL;
  scm_function_t *sf;

  if(usrtc_count(ftree)) {
    for(node = usrtc_first(ftree); node != NULL; node = usrtc_first(ftree)) {
      sf = (scm_function_t *)usrtc_node_getdata(node);
      /* remove from the tree first */
      usrtc_delete(ftree, node);
      /* free all */
      free(sf->name);
      free(sf);
    }
  }

  free(pt);

  return;
}

/* external API */
int scm_func_tree_init(scm_function_tree_t **pt, void *priv)
{
  scm_function_tree_t *nt = __scm_func_tree_alloc(priv);

  if(!nt) return ENOENT;
  else *pt = nt;

  return 0;
}

int scm_func_tree_insert(yd_context_t *zdx, const char *name,
                         scret_t (*call)(yd_context_t *, sexp_t *, void *),
                         void *priv)
{
  scm_function_tree_t *pt = zdx->func;
  usrtc_t *ftree = &(pt->functions);
  scm_function_t *n_func = malloc(sizeof(scm_function_t));
  usrtc_node_t *node = NULL;
  int e = 0;

  /* just check existence */
  if(!n_func)    return ENOMEM;
  if(usrtc_count(ftree) >= MAX_SCM_FUNCTIONS) {
    e = ENOMEM;
    goto __end;
  }
  if((node = usrtc_lookup(ftree, (const void *)name))) {
    e = EEXIST;
    goto __end;
  }

  /* init structure */
  if(!(n_func->name = strdup(name))) {
    free(n_func);
    return ENOMEM;
  }
  n_func->call = call;
  n_func->priv = priv;
  node = &(n_func->node);
  usrtc_node_init(node, n_func);

  /* insert to the tree */
  usrtc_insert(ftree, node, (const void *)name);

 __end:
  if(e) free(n_func);

  return e;
}

int scm_func_tree_insert_t(yd_context_t *zdx, scm_function_t *f)
{
  scm_function_tree_t *pt = zdx->func;
  usrtc_t *ftree = &(pt->functions);
  usrtc_node_t *node = usrtc_lookup(ftree, (const void *)f->name);

  if(node) return EEXIST;
  else node = &(f->node);
  if(usrtc_count(ftree) >= MAX_SCM_FUNCTIONS) return ENOMEM;

  usrtc_node_init(node, f);
  usrtc_insert(ftree, node, (const void *)f->name);

  return 0;
}

scret_t scm_func_tree_call(yd_context_t *zdx, void *cnf, sexp_t *sx,
                       char *name)
{
  scm_function_tree_t *pt = zdx->func;
  usrtc_t *ftree = &(pt->functions);
  usrtc_node_t *node = usrtc_lookup(ftree, (const void *)name);
  scm_function_t *fn;

  if(!node) {
    scret_t rets;
    RETURN_SRET_SERR(rets, ENOENT);
  } else fn = (scm_function_t *)usrtc_node_getdata(node);

  cnf = zdx;

  return fn->call(cnf, sx, fn->priv);
}

int scm_func_tree_delete(yd_context_t *zdx, char *name)
{
  scm_function_tree_t *pt = zdx->func;
  usrtc_t *ftree = &(pt->functions);
  usrtc_node_t *node = usrtc_lookup(ftree, (const void *)name);
  scm_function_t *fn;

  if(!node) return ENOENT;
  else fn = (scm_function_t *)usrtc_node_getdata(node);

  /* remove from tree first */
  usrtc_delete(ftree, node);

  /* free */
  free(fn->name);
  free(fn);

  return 0;
}