#include <stdio.h>
#include <unistd.h>

#include <ucontext.h>

ucontext_t context [10];
char stack [10][SIGSTKSZ*100];

int context_handle=-1;

typedef void* (*func_t)(void *);
typedef int (*func2_t)(const char *);

func2_t outer (int a, int b)
{
// Start RT_WCLOSURE1 overhead

  ucontext_t outer_context, *inner_context = &context [++context_handle];
  func2_t rval;
  volatile int done = 0;

  func2_t inner (ucontext_t *outer_contextp, func2_t *rvalp)
  {
// End RT_WCLOSURE1 overhead

    int _a = a;
    int _b = b;
    int c = a + b;
    int d = a * b;
    int func1 (const char *who)
    {
      printf ("Hello %s from %s\n", who, __func__);
      fflush (stdout);
      _a++;
      _b++;
      printf ("Hello from %s (%d,%d)\n", __func__, _a, _b);


      return c + d;
    }
// Start RT_WCLOSURE2 overhead
    func2_t proxy (void)
    {
// End RT_WCLOSURE2 overhead

      printf ("Hello from %s (%d,%d)\n", __func__, a, b);
      return func1;


// Start RT_WCLOSURE3 overhead
    }
    *rvalp = proxy ();
    setcontext (outer_contextp);
    return NULL;
  }
  inner_context->uc_link = &outer_context;
  inner_context->uc_stack.ss_sp = stack [context_handle];
  inner_context->uc_stack.ss_size = sizeof (stack [context_handle]);
  getcontext (inner_context);

  makecontext (inner_context, (void (*) (void)) inner, 2, &outer_context, &rval);

  getcontext (&outer_context);
  if (!done)
  {
    done = 1;
    setcontext (inner_context);
  }
  return rval;
// End RT_WCLOSURE3 overhead
}

void get_them (func2_t *f1, func2_t *f2)
{
  *f1 = outer (20, 30);
  *f2 = outer (30, 40);
}

int main (int arch, const char **argv)
{
  func2_t f1;
  func2_t f2;

  get_them (&f1, &f2);

  printf ("returned = %p\n", f1);
  printf ("returned = %p\n", f2);

  printf ("%d\n", f1 ("cow"));
  printf ("%d\n", f2 ("bull"));
  printf ("%d\n", f1 ("cow"));
  printf ("%d\n", f2 ("bull"));
  printf ("%d\n", f1 ("cow"));
  printf ("%d\n", f2 ("bull"));
  printf ("%d\n", f1 ("cow"));
  printf ("%d\n", f2 ("bull"));

  return 0;
}
