/* foreign3.c
 * Copyright 1984-2017 Cisco Systems, Inc.
 * 
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 * 
 * http://www.apache.org/licenses/LICENSE-2.0
 * 
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

#include <stdio.h>
#include <stdlib.h>

#ifndef _WIN32
#include <string.h>
#endif

#ifdef _WIN32
#  define SCHEME_IMPORT
#  include <windows.h>
#  include "scheme.h"
#  undef EXPORT
#  define EXPORT extern __declspec (dllexport)
#else
#include "scheme.h"
#endif

/* Standard in C99, but widely available in environments for earlier C dialects: */
#include <stdbool.h>

EXPORT int chk_data(void) {
    static char c[10]="ABCDEFGH";

    return('A' == c[0] && 'B' == c[1] && 'C' == c[2] && 'D' == c[3] &&
           'E' == c[4] && 'F' == c[5] && 'G' == c[6] && 'H' == c[7]);
}

EXPORT int chk_bss(void) {
    static int j[2000];
    int i;

    for (i=0; i<2000; i++) if (j[i] != 0) break;

    return i == 2000;
}

EXPORT int chk_malloc(void) {
    int *j, i;

    j = (int *)malloc(2000 * sizeof(int));

    for (i=0; i<2000; i++) j[i] = 0;

    for (i=0; i<2000; i++) if (j[i] != 0) break;

    free(j);

    return i == 2000;
}

EXPORT float sxstos(float x, float y) {
    return x * y;
}

EXPORT float singlesum12(float x1, float x2, float x3, float x4,
                         float x5, float x6, float x7, float x8,
                         float x9, float x10, float x11, float x12) {
    return x1 + x2 + x3 + x4 + x5 + x6 + x7 + x8 + x9 + x10 + x11 + x12;
}

/* these are taken from SYSTEM V Application Binary Interface
 * MIPS Processor Supplement, 1991
 * page 3-21
 */   

EXPORT double d1d2(double d1, double d2) {
    return d1 + d2;
}
EXPORT double s1s2(float s1, float s2) {
    return s1 + s2;
}
EXPORT double s1d1(float s1, double d1) {
    return s1 + d1;
}
EXPORT double d1s1(double d1, float s1) {
    return d1 + s1;
}
EXPORT double n1n2n3n4(int n1, int n2, int n3, int n4) {
    return (double)(n1 + n2 + n3 + n4);
}
EXPORT double d1n1d2(double d1, int n1, double d2) {
    return d1 + n1 + d2;
}
EXPORT double d1n1n2(double d1, int n1, int n2) {
    return d1 + n1 + n2;
}
EXPORT double s1n1n2(float s1, int n1, int n2) {
    return s1 + n1 + n2;
}
EXPORT double n1n2n3d1(int n1, int n2, int n3, double d1) {
    return n1 + n2 + n3 + d1;
}
EXPORT double n1n2n3s1(int n1, int n2, int n3, float s1) {
    return n1 + n2 + n3 + s1;
}
EXPORT double n1n2d1(int n1, int n2, double d1) {
    return n1 + n2 + d1;
}
EXPORT double n1d1(int n1, double d1) {
    return n1 + d1;
}
EXPORT double s1s2s3s4(float s1, float s2, float s3, float s4) {
    return s1 + s2 + s3 + s4;
}
EXPORT double s1n1s2n2(float s1, int n1, float s2, int n2) {
    return s1 + n1 + s2 + n2;
}
EXPORT double d1s1s2(double d1, float s1, float s2) {
    return d1 + s1 + s2;
}
EXPORT double s1s2d1(float s1, float s2, double d1) {
    return s1 + s2 + d1;
}
EXPORT double n1s1n2s2(int n1, float s1, int n2, float s2) {
    return n1 + s1 + n2 + s2;
}
EXPORT double n1s1n2n3(int n1, float s1, int n2, int n3) {
    return n1 + s1 + n2 + n3;
}
EXPORT double n1n2s1n3(int n1, int n2, float s1, int n3) {
    return n1 + n2 + s1 + n3;
}

/* a few more for good measure */
EXPORT double d1d2s1s2(double d1, double d2, float s1, float s2) {
    return d1 + d2 + s1 + s2;
}
EXPORT double d1d2n1n2(double d1, double d2, int n1, int n2) {
    return d1 + d2 + n1 + n2;
}
EXPORT double s1d1s2s3(float s1, double d1, float s2, float s3) {
    return s1 + d1 + s2 + s3;
}

/* support for testing foreign-callable */
EXPORT ptr Sinvoke2(ptr code, ptr x1, iptr x2) {
    return (*((ptr (*)(ptr, iptr))Sforeign_callable_entry_point(code)))(x1, x2);
}

EXPORT ptr Sargtest(iptr f, int x1, int x2, iptr x3, double x4, float x5, char *x6) {
    return (*((ptr (*)(int, int, iptr, double, float, char *))f))(x1, x2, x3, x4, x5, x6);
}

EXPORT ptr Sargtest2(iptr f, short x1, int x2, char x3, double x4, short x5, char x6) {
    return (*((ptr (*)(short, int, char, double, short, char))f))(x1, x2, x3, x4, x5, x6);
}

EXPORT int Srvtest_int32(ptr code, ptr x1) {
    return (*((int (*)(ptr))Sforeign_callable_entry_point(code)))(x1);
}

EXPORT unsigned Srvtest_uns32(ptr code, ptr x1) {
    return (*((unsigned (*)(ptr))Sforeign_callable_entry_point(code)))(x1);
}

EXPORT float Srvtest_single(ptr code, ptr x1) {
    return (*((float (*)(ptr))Sforeign_callable_entry_point(code)))(x1);
}

EXPORT double Srvtest_double(ptr code, ptr x1) {
    return (*((double (*)(ptr))Sforeign_callable_entry_point(code)))(x1);
}

EXPORT char Srvtest_char(ptr code, ptr x1) {
    return (*((char (*)(ptr))Sforeign_callable_entry_point(code)))(x1);
}

EXPORT bool Srvtest_stdbool(ptr code, ptr x1) {
    return (*((bool (*)(ptr))Sforeign_callable_entry_point(code)))(x1);
}

#ifdef _WIN32
EXPORT int __stdcall sum_stdcall(int a, int b) {
    return a + b;
}

EXPORT ptr Sinvoke2_stdcall(ptr code, ptr x1, iptr x2) {
    return (*((ptr (__stdcall *)(ptr, iptr))Sforeign_callable_entry_point(code)))(x1, x2);
}

typedef int (__stdcall *comfunc) (void *, int);
typedef struct { comfunc *vtable; int data; } com_instance_t;

static comfunc com_vtable[2];
static com_instance_t com_instance;

extern int __stdcall com_method0(void *inst, int val) {
  return ((com_instance_t *)inst)->data = val;
}

extern int __stdcall com_method1(void *inst, int val) {
  return val * 2 + ((com_instance_t *)inst)->data;
}

EXPORT com_instance_t *get_com_instance(void) {
  com_instance.vtable = com_vtable;
  com_vtable[0] = com_method0;
  com_vtable[1] = com_method1;
  com_instance.data = -31;
  return &com_instance;
}
#endif /* _WIN32 */

/* foreign_callable example adapted from foreign.stex */
typedef void (*CB)(char);

static CB callbacks[256];

EXPORT void cb_init(void) {
   int i;

   for (i = 0; i < 256; i += 1)
       callbacks[i] = (CB)0;
}

EXPORT void register_callback(char c, iptr cb) {
    callbacks[(int)c] = (CB)cb;
}

EXPORT void event_loop(char *s) {
    char buf[10];
    CB f; char c;

  /* create a local copy, since s points into an unlocked Scheme string */
    strncpy(buf, s, 9);
    buf[9] = '0';
    s = buf;
    for (;;) {
        c = *s++;
        if (c == 0) break;
        f = callbacks[(int)c];
        if (f != (CB)0) f(c);
    }
}

EXPORT void call_twice(void (*foo)(int), int x, int y) {
  foo(x);
  foo(y);
}

EXPORT void unlock_callback(int (* f)(int)) {
  Sunlock_object(Sforeign_callable_code_object(f));
}

EXPORT int call_and_unlock(int (* f)(int), int arg) {
  int ans = f(arg);
  Sunlock_object(Sforeign_callable_code_object(f));
  return ans;
}

EXPORT void init_lock (uptr *u) {
  INITLOCK(u);
}

EXPORT void spinlock (uptr *u) {
  SPINLOCK(u);
}

EXPORT void unlock (uptr *u) {
  UNLOCK(u);
}

EXPORT int locked_incr (uptr *u) {
  int ret;
  LOCKED_INCR(u, ret);
  return ret;
}

EXPORT int locked_decr (uptr *u) {
  int ret;
  LOCKED_DECR(u, ret);
  return ret;
}
