/* This file is part of GNU Pies.
   Copyright (C) 2015-2022 Sergey Poznyakoff

   GNU Pies 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 3, or (at your option)
   any later version.

   GNU Pies 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 GNU Pies.  If not, see <http://www.gnu.org/licenses/>. */

#include "ident.h"

struct grecs_list *identity_provider_list;

static struct grecs_symtab *idmech_symtab;

static int
idmech_copy (void *a, void *b)
{
  pies_identity_mechanism_t ma = a;
  pies_identity_mechanism_t mb = b;

  *ma = *mb;
  ma->name = grecs_strdup (mb->name);
  return 0;
}

int
pies_identity_mechanism_register (pies_identity_mechanism_t mech)
{
  int install;
	
  if (!idmech_symtab)
    {
      idmech_symtab = grecs_symtab_create (sizeof (*mech),
					   NULL,
					   NULL,
					   idmech_copy,
					   NULL,
					   NULL);
      if (!idmech_symtab)
	grecs_alloc_die ();
    }

  install = 1;
  if (!grecs_symtab_lookup_or_install (idmech_symtab, mech, &install))
    abort ();
  return !install;
}

pies_identity_mechanism_t
pies_identity_mechanism_lookup (char const *name)
{
  struct pies_identity_mechanism key;
  if (!idmech_symtab)
    return NULL;
  key.name = name;
  return grecs_symtab_lookup_or_install (idmech_symtab, &key, NULL);
}
  
static int
idmech_help (void *data, void *unused)
{
  pies_identity_mechanism_t p = data;
	
  if (p->confhelp)
    p->confhelp ();
  return 0;
}

void
pies_config_identity_mechanisms_help ()
{
  grecs_symtab_foreach (idmech_symtab, idmech_help, NULL);
}

static int
get_string_node (struct grecs_node *node, const char *name,
		 struct grecs_node **pret)
{
  struct grecs_node *p = grecs_find_node (node->down, name);
	
  if (!p)
    {
      grecs_error (&node->locus, 0,
		   "no \"%s\" statement found", name);
      return 1;
    }

  if (p->type != grecs_node_stmt)
    {
      grecs_error (&p->locus, 0, "must be simple statement");
      return 1;
    }
  
  if (p->v.value->type != GRECS_TYPE_STRING)
    {
      grecs_error (&p->locus, 0, "must be scalar");
      return 1;
    }

  *pret = p;
  return 0;
}

int
pies_config_provider (struct grecs_node *node)
{
  char const *name;
  pies_identity_mechanism_t mp;
  struct pies_identity_provider *prov;
  struct grecs_node *p;
  
  if (node->v.value->type != GRECS_TYPE_STRING)
    {
      grecs_error (&node->locus, 0, "value must be scalar");
      return 1;
    }

  name = node->v.value->v.string;

  if (get_string_node (node, "type", &p))
    return 1;

  mp = pies_identity_mechanism_lookup (p->v.value->v.string);
  if (!mp)
    {
      grecs_error (&p->locus, 0, "no such mechanism");
      return 1;
    }

  prov = grecs_calloc (1, sizeof (*prov));
  prov->name = grecs_strdup (name);
  prov->mech = mp;
  prov->locus = node->locus;

  if (mp->configure && mp->configure (node, prov))
    {
      grecs_error (&node->locus, 0, "provider configuration failed");
      //FIXME: memory leak
      return 1;
    }
  
  if (!identity_provider_list)
    identity_provider_list = grecs_list_create ();
  grecs_list_append (identity_provider_list, prov);
  
  return 0;
}
  
char const *
pies_identity_provider_name (pies_identity_provider_t p)
{
  return p->name;
}
