/*
 * Copyright (c) 2007 Wayne Meissner. All rights reserved.
 *
 * For licensing, see LICENSE.SPECS
 */

#include <stdio.h>
#include <stdlib.h>
#include <stdbool.h>
#include <stdint.h>
#include <string.h>
#include <stdarg.h>

typedef char s8;
typedef short s16;
typedef int s32;
typedef long long s64;
typedef float f32;
typedef double f64;

typedef struct bugged_struct {
  unsigned char visible;
  unsigned int x;
  unsigned int y;
  short rx;
  short ry;
  unsigned char order;
  unsigned char size;
} bugged_struct_t;

unsigned int
bugged_struct_size() {
    return sizeof(bugged_struct_t);
}

struct test1 {
    char b;
    short s;
    int i;
    long long j;
    long l;
    float f;
    double d;
    char string[32];
};

struct struct_with_array {
    char c;
    int a[5];
};

struct nested {
    int i;
};

struct container {
    char first;
    struct nested s;
};

int
struct_align_nested_struct(struct container* a) { return a->s.i; }

void*
struct_field_array(struct struct_with_array* s) { return &s->a; }

struct container*
struct_make_container_struct(int i)
{
    static struct container cs;
    memset(&cs, 0, sizeof(cs));
    cs.first = 1;
    cs.s.i = i;
    return &cs;
}

#define T(x, type) \
    type struct_field_##type(struct test1* t) { return t->x; } \
    struct type##_align { char first; type value; }; \
    type struct_align_##type(struct type##_align* a) { return a->value; }

T(b, s8);
T(s, s16);
T(i, s32);
T(j, s64);
T(f, f32);
T(d, f64);
T(l, long);

void
struct_set_string(struct test1* t, char* s)
{
    strcpy(t->string, s);
}

struct test1*
struct_make_struct(char b, short s, int i, long long ll, float f, double d)
{
    static struct test1 t;
    memset(&t, 0, sizeof(t));
    t.b = b;
    t.s = s;
    t.i = i;
    t.j = ll;
    t.f = f;
    t.d = d;
    return &t;
}

typedef int (*add_cb)(int a1, int a2);
typedef int (*sub_cb)(int a1, int a2);
struct test2 {
    add_cb  add_callback;
    sub_cb  sub_callback;
};

int
struct_call_add_cb(struct test2* t, int a1, int a2)
{
    return t->add_callback(a1, a2);
}

int
struct_call_sub_cb(struct test2* t, int a1, int a2)
{
    return t->sub_callback(a1, a2);
}


struct struct_with_array*
struct_make_struct_with_array(int a_0, int a_1, int a_2, int a_3, int a_4)
{
    static struct struct_with_array s;

    memset(&s, 0, sizeof(s));

    s.a[0] = a_0;
    s.a[1] = a_1;
    s.a[2] = a_2;
    s.a[3] = a_3;
    s.a[4] = a_4;

    return &s;

}

struct s8s32 {
    char s8;
    int s32;
};

struct s8s32
struct_return_s8s32()
{
    struct s8s32 s;
    s.s8 = 0x7f;
    s.s32 = 0x12345678;

    return s;
}

struct s8s32
struct_s8s32_set(char s8, int s32)
{
    struct s8s32 s;

    s.s8 = s8;
    s.s32 = s32;

    return s;
}

int
struct_s8s32_get_s8(struct s8s32 s)
{
    return s.s8;
}

int
struct_s8s32_get_s32(struct s8s32 s)
{
    return s.s32;
}

struct s8s32
struct_s8s32_ret_s8s32(struct s8s32 s)
{
    return s;
}

// Pass a struct and an int arg, ensure the int arg is passed correctly
int
struct_s8s32_s32_ret_s32(struct s8s32 s, int s32)
{
    return s32;
}

// Pass a struct and a long long arg, ensure the long long arg is passed correctly
long long
struct_s8s32_s64_ret_s64(struct s8s32 s, long long s64)
{
    return s64;
}

// Pass a struct and a long long arg, ensure the long long arg is passed correctly
int
struct_s32_ptr_s32_s8s32_ret_s32(int s32a, void *ptr, int s32b, struct s8s32 s)
{
    if (ptr != NULL) *(struct s8s32 *) ptr = s;
    return s.s32;
}

// Pass a char *, copy into buffer length struct
struct struct_string {
    char *bytes;
    int len;
};

struct struct_string
struct_varargs_ret_struct_string(int len, ...)
{
    struct struct_string ss;
    va_list vl;
    char* cp = NULL;

    va_start(vl, len);

    ss.len = len;
    ss.bytes = va_arg(vl, char *);
    if (ss.bytes != NULL) {
        cp = malloc(strlen(ss.bytes) + 1);
        strcpy(cp, ss.bytes);
        ss.bytes = cp;
    }

    va_end(vl);

    return ss;
}

