# Nog te doen:
# error-handling (onerror)
# meerdere interpreters


# wrapfort.code --
#     Code fragments (templates) for generating a wrapper
#
array set ctype {void void real float double double integer long logical long string "char *"
                 integer-array  long real-array  float double-array  double
                 integer-vector long real-vector float double-vector double
                 integer-matrix long real-matrix float double-matrix double}

set header "
/* Wrapper for ROUTINE - Tcl-command CMDNAME */
#ifdef FTN_UNDERSCORE
#   define ROUTINE ROUTINE_
#endif
#ifdef FTN_ALL_CAPS
#   define ROUTINE ALLCAPS
#endif
void __stdcall ROUTINE(); /* Important! */
static int c__ROUTINE( ClientData client_data, Tcl_Interp *interp,
                       int objc, struct Tcl_Obj * CONST objv\[\] ) {
    int _rc_ = TCL_OK ;"

set declaration(integer)        {    long NAME;}
set declaration(real)           {    float NAME;}
set declaration(double)         {    double NAME;}
set declaration(string)         {    char NAME[80]; int length__NAME;}
set declaration(logical)        {    long NAME;}
set declaration(integer-vector) {    long *NAME; int size__NAME;}
set declaration(integer-array)  {    long *NAME; int size__NAME;}
set declaration(integer-matrix) {    long *NAME; int size_inner__NAME, size_outer__NAME;}
set declaration(real-vector)    {    float *NAME; int size__NAME;}
set declaration(real-array)     {    float *NAME; int size__NAME;}
set declaration(real-matrix)    {    float *NAME; int size_inner__NAME, size_outer__NAME;}
set declaration(double-vector)  {    double *NAME; int size__NAME;}
set declaration(double-array)   {    double *NAME; int size__NAME;}
set declaration(double-matrix)  {    double *NAME; int size_inner__NAME, size_outer__NAME;}

set initialisation(check)     {
    if ( objc != COUNT ) {
        Tcl_SetResult( interp, "Wrong number of arguments", NULL );
        return TCL_ERROR;
    }}

set initialisation(integer)     {
    if ( Tcl_GetLongFromObj( interp, objv[COUNT], &NAME ) != TCL_OK ) {
        Tcl_SetResult( interp, "Argument COUNT must be integer", NULL );
        return TCL_ERROR;
    }}

set initialisation(integer-array)     {
    if ( WrapCopyIntListToArray( interp, objv[COUNT], &NAME, &size__NAME ) != TCL_OK ) {
        Tcl_SetResult( interp, "Argument COUNT must be a list of integers", NULL );
        return TCL_ERROR;
    }}

set initialisation(real)     {
    {
        double value;
        if ( Tcl_GetDoubleFromObj( interp, objv[COUNT], &value ) != TCL_OK ) {
            Tcl_SetResult( interp, "Argument COUNT must be a double precision real", NULL );
            return TCL_ERROR;
        } else {
            NAME = (float) value;
        }
    }}

set initialisation(real-array)     {
    if ( WrapCopyDoubleListToRealArray( interp, objv[COUNT], &NAME, &size__NAME ) != TCL_OK ) {
        Tcl_SetResult( interp, "Argument COUNT must be a list of double precision reals", NULL );
        return TCL_ERROR;
    }}

set initialisation(double)     {
    if ( Tcl_GetDoubleFromObj( interp, objv[COUNT], &NAME ) != TCL_OK ) {
        Tcl_SetResult( interp, "Argument COUNT must be a double precision real", NULL );
        return TCL_ERROR;
    }}

set initialisation(double-array)     {
    if ( WrapCopyDoubleListToArray( interp, objv[COUNT], &NAME, &size__NAME ) != TCL_OK ) {
        Tcl_SetResult( interp, "Argument COUNT must be a list of double precision reals", NULL );
        return TCL_ERROR;
    }}

set initialisation(external)   {
    saved_interp = interp ;
    if ( NAME_tclprocs == NULL ) {
        NAME_tclprocs = Tcl_NewListObj( 0, NULL );
        Tcl_IncrRefCount(NAME_tclprocs);
    }
    if ( Tcl_ListObjAppendElement( interp, NAME_tclprocs, objv[COUNT]) != TCL_OK ) {
        Tcl_SetResult( interp, "Name of Tcl command could not be stored for later use", NULL );
        return TCL_ERROR;
    }}

#
# TODO!
#
set initialisation(string)     {
    if ( Tcl_GetDoubleFromObj( interp, objv[COUNT], &NAME ) != TCL_OK ) {
        Tcl_SetResult( interp, "Argument COUNT must be a double precision real", NULL );
        return TCL_ERROR;
    }}

set cleanup(close)   "    return _rc_;
}"

set cleanup(integer)        "/* Nothing to be done for: NAME */"
set cleanup(integer-array)  "    free(NAME);"
set cleanup(integer-vector) "    free(NAME);"
set cleanup(integer-matrix) "    free(NAME);"
set cleanup(real)           "/* Nothing to be done for: NAME */"
set cleanup(real-array)     "    free(NAME);"
set cleanup(real-vector)    "    free(NAME);"
set cleanup(real-matrix)    "    free(NAME);"
set cleanup(double)         "/* Nothing to be done for: NAME */"
set cleanup(double-array)   "    free(NAME);"
set cleanup(double-vector)  "    free(NAME);"
set cleanup(double-matrix)  "    free(NAME);"

set cleanup(external)       {
   {int length ;
    if ( Tcl_ListObjLength( interp, NAME_tclprocs, &length ) != TCL_OK ||
         Tcl_ListObjReplace( interp, NAME_tclprocs, length-1, length-1, 0, NULL ) != TCL_OK ) {
        Tcl_SetResult( interp, "Argument COUNT could not be cleaned up", NULL );
        return TCL_ERROR;
    }
   }
}

set result(integer)  {
    Tcl_SetObjResult( interp, Tcl_NewLongObj(NAME) ) ;
}
set result(real)  {
    Tcl_SetObjResult( interp, Tcl_NewDoubleObj((double)NAME) ) ;
}
set result(double)  {
    Tcl_SetObjResult( interp, Tcl_NewDoubleObj(NAME) ) ;
}
set result(integer-array)  {
    {
        Tcl_Obj *result;
        if ( WrapCopyIntArrayToList( interp, NAME, size__NAME, &result ) != TCL_OK ) {
            Tcl_SetResult( interp, "Can not copy array to Tcl result", NULL );
            return TCL_ERROR;
        } else {
            Tcl_SetObjResult( interp, result ) ;
        }
    }}
set result(real-array)  {
    {
        Tcl_Obj *result;
        if ( WrapCopyRealArrayToDoubleList( interp, NAME, size__NAME, &result ) != TCL_OK ) {
            Tcl_SetResult( interp, "Can not copy array to Tcl result", NULL );
            return TCL_ERROR;
        } else {
            Tcl_SetObjResult( interp, result ) ;
        }
    }}
set result(double-array)  {
    {
        Tcl_Obj *result;
        if ( WrapCopyDoubleArrayToList( interp, NAME, size__NAME, &result ) != TCL_OK ) {
            Tcl_SetResult( interp, "Can not copy array to Tcl result", NULL );
            return TCL_ERROR;
        } else {
            Tcl_SetObjResult( interp, result ) ;
        }
    }}

#
# Second part: Fortran calling Tcl
#

set fheader(start) {
/* Wrapper for the NAME interface */
#ifdef FTN_UNDERSCORE
#   define NAME NAME_
#endif
#ifdef FTN_ALL_CAPS
#   define NAME ALLCAPS
#endif
static Tcl_Obj *NAME_tclprocs ;
TYPE __stdcall NAME (}

set fheader(end)   ") \{"

set fdeclaration(integer)             {    long *NAME}
set fdeclaration(real)                {    float *NAME}
set fdeclaration(double)              {    double *NAME}
set fdeclaration(integer-array)       {    long *NAME}
set fdeclaration(real-array)          {    float *NAME}
set fdeclaration(double-array)        {    double *NAME}
set fdeclaration(integer-vector)      {    long *NAME}
set fdeclaration(real-vector)         {    float *NAME}
set fdeclaration(double-vector)       {    double *NAME}
set fdeclaration(integer-matrix)      {    long *NAME}
set fdeclaration(real-matrix)         {    float *NAME}
set fdeclaration(double-matrix)       {    double *NAME}

set fdeclaration(length)              {    int size__NAME = *SIZE;}

set fdecl_result(integer)             {    long NAME;}
set fdecl_result(real)                {    float NAME;}
set fdecl_result(double)              {    double NAME;}

set finitialisation(start)             {
    Tcl_Obj    *objv[NOARGS]    ;
    int         objc   = NOARGS ;
    int         error           ;
    int         length          ;
    Tcl_Interp *interp = saved_interp ;

    if ( Tcl_ListObjLength( interp, NAME_tclprocs, &length ) != TCL_OK ||
         Tcl_ListObjIndex( interp, NAME_tclprocs, length-1, &objv[0] ) != TCL_OK ) {
        Tcl_SetResult( interp, "Could not retrieve proc name", NULL );
        return DUMMY;
    }
}
set finitialisation(integer)           {    objv[IDX] = Tcl_NewLongObj(*NAME);
    Tcl_IncrRefCount(objv[IDX]);}
set finitialisation(real)              {    objv[IDX] = Tcl_NewDoubleObj((double)*NAME);
    Tcl_IncrRefCount(objv[IDX]);}
set finitialisation(double)            {    objv[IDX] = Tcl_NewDoubleObj(*NAME);
    Tcl_IncrRefCount(objv[IDX]);}

set finitialisation(integer-array)  {
    if ( WrapCopyIntArrayToList( interp, NAME, size__NAME, &objv[IDX] ) != TCL_OK ) {
        Tcl_SetResult( interp, "Can not copy array to argument IDX", NULL );
        /* TODO: clean-up! */
        return TCL_ERROR;
    }
    Tcl_IncrRefCount(objv[IDX]);}

set finitialisation(real-array)  {
    if ( WrapCopyRealArrayToDoubleList( interp, NAME, size__NAME, &objv[IDX] ) != TCL_OK ) {
        Tcl_SetResult( interp, "Can not copy array to argument IDX", NULL );
        /* TODO: clean-up! */
        return TCL_ERROR;
    }
    Tcl_IncrRefCount(objv[IDX]);}

set finitialisation(double-array)  {
    if ( WrapCopyDoubleArrayToList( interp, NAME, size__NAME, &objv[IDX] ) != TCL_OK ) {
        Tcl_SetResult( interp, "Can not copy array to argument IDX", NULL );
        /* TODO: clean-up! */
        return TCL_ERROR;
    }
    Tcl_IncrRefCount(objv[IDX]);}

set finitialisation(integer-vector) $finitialisation(integer-array)
set finitialisation(real-vector)    $finitialisation(real-array)
set finitialisation(double-vector)  $finitialisation(double-array)

set frunproc {
    {int _i_;
    for ( _i_ = 0; _i_ < objc; _i_ ++ ) {
        Tcl_IncrRefCount(objv[_i_]);
    }
    error = 0 ;
    if ( Tcl_EvalObjv( interp, objc, objv, 0 ) != TCL_OK ) {
        error = 1 ;
    }
    for ( _i_ = 0; _i_ < objc; _i_ ++ ) {
        Tcl_DecrRefCount(objv[_i_]);
    }}
}
set fresult(integer)        {
   if ( Tcl_GetLongFromObj( interp, Tcl_GetObjResult(interp), &NAME ) != TCL_OK ) {
        Tcl_AppendResult( interp, "Could not retrieve integer result", NULL ) ;
   }
}
set fresult(real)           {
  {double dbl ;
   if ( Tcl_GetDoubleFromObj( interp, Tcl_GetObjResult(interp), &dbl ) != TCL_OK ) {
        Tcl_AppendResult( interp, "Could not retrieve real result", NULL ) ;
       NAME = 0.0f ;
   } else {
       NAME = (float) dbl ;
   }
  }
}
set fresult(double)         {
   if ( Tcl_GetDoubleFromObj( interp, Tcl_GetObjResult(interp), &NAME ) != TCL_OK ) {
        Tcl_AppendResult( interp, "Could not retrieve double result", NULL ) ;
   }
}

set fresult(integer-array)     {
    if ( WrapCopyIntListIntoExistingArray( interp, Tcl_GetObjResult(interp), NAME, size__NAME ) != TCL_OK ) {
        Tcl_SetResult( interp, "Result of Tcl proc must be a list of integers", NULL );
        return TCL_ERROR;
    }}

set fresult(real-array)     {
    if ( WrapCopyDoubleListIntoExistingRealArray( interp, Tcl_GetObjResult(interp), NAME, size__NAME ) != TCL_OK ) {
        Tcl_SetResult( interp, "Result of Tcl proc must be a list of double precision reals", NULL );
        return TCL_ERROR;
    }}

set fresult(double-array)     {
    if ( WrapCopyDoubleListIntoExistingArray( interp, Tcl_GetObjResult(interp), NAME, size__NAME ) != TCL_OK ) {
        Tcl_SetResult( interp, "Result of Tcl proc must be a list of double precision reals", NULL );
        return TCL_ERROR;
    }}

set ferror {
    if ( error != 0 ) {
        ERROR
    }}

set fcleanup(integer)       {    Tcl_DecrRefCount(objv[IDX]);}
set fcleanup(real)          {    Tcl_DecrRefCount(objv[IDX]);}
set fcleanup(double)        {    Tcl_DecrRefCount(objv[IDX]);}
set fcleanup(integer-array) {    Tcl_DecrRefCount(objv[IDX]);}
set fcleanup(real-array)    {    Tcl_DecrRefCount(objv[IDX]);}
set fcleanup(double-array)  {    Tcl_DecrRefCount(objv[IDX]);}

set freturn(integer)        {    return NAME;}
set freturn(real)           {    return NAME;}
set freturn(double)         {    return NAME;}
