/****************************************************************************
 * Copyright 2018-2020,2021 Thomas E. Dickey                                *
 * Copyright 2008-2012,2016 Free Software Foundation, Inc.                  *
 *                                                                          *
 * Permission is hereby granted, free of charge, to any person obtaining a  *
 * copy of this software and associated documentation files (the            *
 * "Software"), to deal in the Software without restriction, including      *
 * without limitation the rights to use, copy, modify, merge, publish,      *
 * distribute, distribute with modifications, sublicense, and/or sell       *
 * copies of the Software, and to permit persons to whom the Software is    *
 * furnished to do so, subject to the following conditions:                 *
 *                                                                          *
 * The above copyright notice and this permission notice shall be included  *
 * in all copies or substantial portions of the Software.                   *
 *                                                                          *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS  *
 * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF               *
 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.   *
 * IN NO EVENT SHALL THE ABOVE COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,   *
 * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR    *
 * OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR    *
 * THE USE OR OTHER DEALINGS IN THE SOFTWARE.                               *
 *                                                                          *
 * Except as contained in this notice, the name(s) of the above copyright   *
 * holders shall not be used in advertising or otherwise to promote the     *
 * sale, use or other dealings in this Software without prior written       *
 * authorization.                                                           *
 ****************************************************************************/

/***************************************************************************
*                                                                          *
*  Author : Juergen Pfeifer                                                *
*                                                                          *
***************************************************************************/

#include "form.priv.h"

MODULE_ID("$Id: fty_generic.c,v 1.15 2021/03/27 23:49:53 tom Exp $")

/*
 * This is not a full implementation of a field type, but adds some
 * support for higher level languages with some restrictions to interop
 * with C language. In particular, the collection of arguments for the
 * various fieldtypes is not based on the vararg C mechanism, but on a
 * iterator based callback mechanism that allows the high level language
 * to provide the arguments as a structure. Most languages have mechanisms
 * to layout structures so that they can be passed to C.
 *
 * The languages can register a new generic fieldtype dynamically and store
 * a handle (key) to the calling object as an argument. Together with that
 * it can register a freearg callback, so that the high level language
 * remains in control of the memory management of the arguments they pass.
 * The design idea is, that the high-level language - typically a OO
 * language like C# or Java, uses its own dispatching mechanisms
 * (polymorphism) to call the proper check routines responsible for the
 * argument type. So these language implement typically only one generic
 * fieldtype they register with the forms library using this call.
 *
 * For that purpose we have extended the fieldtype structure by a new element
 * that gets the arguments from a single struct passed by the caller.
 *
 */
#if NCURSES_INTEROP_FUNCS

/*---------------------------------------------------------------------------
|   Facility      :  libnform
|   Function      :  static void *Generic_This_Type( void * arg )
|
|   Description   :  We interpret the passed arg just as a handle the
|                    calling language uses to keep track of its allocated
|                    argument structures. We can simply copy it back.
|
|   Return Values :  Pointer to argument structure
+--------------------------------------------------------------------------*/
static void *
Generic_This_Type(void *arg)
{
  return (arg);
}

/*---------------------------------------------------------------------------
|   Facility      :  libnform
|   Function      :  FIELDTYPE *_nc_generic_fieldtype(
|                       bool (* const field_check)(FIELD *,const void *),
|                       bool (* const char_check) (int, const void *),
|   		        bool (*const next)(FORM*,FIELD*,const void*),
|		        bool (*const prev)(FORM*,FIELD*,const void*),
|                       void (*freecallback)(void*))
|
|   Description   :  Create a new fieldtype. The application programmer must
|                    write a field_check and a char_check function and give
|                    them as input to this call. A callback to allow the
|                    release of the allocated memory must also be provided.
|                    For generic field types, we provide some more
|                    information about the field as parameters.
|
|                    If an error occurs, errno is set to
|                       E_BAD_ARGUMENT  - invalid arguments
|                       E_SYSTEM_ERROR  - system error (no memory)
|
|   Return Values :  Fieldtype pointer or NULL if error occurred
+--------------------------------------------------------------------------*/
FORM_EXPORT(FIELDTYPE *)
_nc_generic_fieldtype(bool (*const field_check) (FORM *, FIELD *, const void *),
		      bool (*const char_check) (int, FORM *, FIELD *, const
						void *),
		      bool (*const next) (FORM *, FIELD *, const void *),
		      bool (*const prev) (FORM *, FIELD *, const void *),
		      void (*freecallback) (void *))
{
  int code = E_SYSTEM_ERROR;
  FIELDTYPE *res = (FIELDTYPE *)0;

  TR_FUNC_BFR(5);

  T((T_CALLED("_nc_generic_fieldtype(%s,%s,%s,%s,%s)"),
     TR_FUNC_ARG(0, field_check),
     TR_FUNC_ARG(1, char_check),
     TR_FUNC_ARG(2, next),
     TR_FUNC_ARG(3, prev),
     TR_FUNC_ARG(4, freecallback)));

  if (field_check || char_check)
    {
      res = typeMalloc(FIELDTYPE, 1);

      if (res)
	{
	  *res = *_nc_Default_FieldType;
	  SetStatus(res, (_HAS_ARGS | _GENERIC));
	  res->fieldcheck.gfcheck = field_check;
	  res->charcheck.gccheck = char_check;
	  res->genericarg = Generic_This_Type;
	  res->freearg = freecallback;
	  res->enum_next.gnext = next;
	  res->enum_prev.gprev = prev;
	  code = E_OK;
	}
    }
  else
    code = E_BAD_ARGUMENT;

  if (E_OK != code)
    SET_ERROR(code);

  returnFieldType(res);
}

/*---------------------------------------------------------------------------
|   Facility      :  libnform
|   Function      :  static TypeArgument *GenericArgument(
|                      const FIELDTYPE* typ,
|                      int (*argiterator)(void**),
|                      int* err)
|
|   Description   :  The iterator callback must browse through all fieldtype
|                    parameters that have an argument associated with the
|                    type. The iterator returns 1 if the operation to get
|                    the next element was successful, 0 otherwise. If the
|                    iterator could move to the next argument, it fills
|                    the void* pointer representing the argument into the
|                    location provided as argument to the iterator.
|                    The err reference is used to keep track of errors.
|
|   Return Values :  Pointer to argument structure
+--------------------------------------------------------------------------*/
static TypeArgument *
GenericArgument(const FIELDTYPE *typ,
		int (*argiterator) (void **), int *err)
{
  TypeArgument *res = (TypeArgument *)0;

  if (typ != 0 && (typ->status & _HAS_ARGS) != 0 && err != 0 && argiterator != 0)
    {
      if (typ->status & _LINKED_TYPE)
	{
	  /* Composite fieldtypes keep track internally of their own memory */
	  TypeArgument *p = typeMalloc(TypeArgument, 1);

	  if (p)
	    {
	      p->left = GenericArgument(typ->left, argiterator, err);
	      p->right = GenericArgument(typ->right, argiterator, err);
	      return p;
	    }
	  else
	    *err += 1;
	}
      else
	{
	  assert(typ->genericarg != (void *)0);
	  if (typ->genericarg == 0)
	    *err += 1;
	  else
	    {
	      void *argp;
	      int valid = argiterator(&argp);

	      if (valid == 0 || argp == 0 ||
		  !(res = (TypeArgument *)typ->genericarg(argp)))
		{
		  *err += 1;
		}
	    }
	}
    }
  return res;
}

/*---------------------------------------------------------------------------
|   Facility      :  libnform
|   Function      :  int _nc_set_generic_fieldtype(
|                      FIELD* field,
|                      FIELDTYPE* ftyp,
|                      int (*argiterator)(void**))
|
|   Description   :  Assign the fieldtype to the field and use the iterator
|                    mechanism to get the arguments when a check is
|                    performed.
|
|   Return Values :  E_OK if all went well
|                    E_SYSTEM_ERROR if an error occurred
+--------------------------------------------------------------------------*/
FORM_EXPORT(int)
_nc_set_generic_fieldtype(FIELD *field,
			  FIELDTYPE *ftyp,
			  int (*argiterator) (void **))
{
  int code = E_SYSTEM_ERROR;
  int err = 0;

  if (field)
    {
      if (field && field->type)
	_nc_Free_Type(field);

      field->type = ftyp;
      if (ftyp)
	{
	  if (argiterator)
	    {
	      /* The precondition is that the iterator is reset */
	      field->arg = (void *)GenericArgument(field->type, argiterator, &err);

	      if (err)
		{
		  _nc_Free_Argument(field->type, (TypeArgument *)(field->arg));
		  field->type = (FIELDTYPE *)0;
		  field->arg = (void *)0;
		}
	      else
		{
		  code = E_OK;
		  if (field->type)
		    field->type->ref++;
		}
	    }
	}
      else
	{
	  field->arg = (void *)0;
	  code = E_OK;
	}
    }
  return code;
}

/*---------------------------------------------------------------------------
|   Facility      :  libnform
|   Function      :  WINDOW* _nc_form_cursor(
|                      FORM* form,
|                      int *pRow, int *pCol)
|
|   Description   :  Get the current position of the form cursor position
|                    We also return the field window
|
|   Return Values :  The field's Window or NULL on error
+--------------------------------------------------------------------------*/
FORM_EXPORT(WINDOW *)
_nc_form_cursor(const FORM *form, int *pRow, int *pCol)
{
  int code = E_SYSTEM_ERROR;
  WINDOW *res = (WINDOW *)0;

  if (form != 0 && pRow != 0 && pCol != 0)
    {
      *pRow = form->currow;
      *pCol = form->curcol;
      res = form->w;
      code = E_OK;
    }
  if (code != E_OK)
    SET_ERROR(code);
  return res;
}

#else
extern void _nc_fty_generic(void);
void
_nc_fty_generic(void)
{
}
#endif

/* fty_generic.c ends here */
