// Example with virtual and non-virtual functions,
// subclasses, and overriding implemented in C

// CSE 333 lecture demo, 3/16.  HP

// An object is a struct with instance variables and
// (since there are virtual functions) a pointer to
// the class vtable.

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

//////////// Thing ////////////

// Typedefs for Thing class objects and vtable

typedef struct thing        Thing;
typedef struct thing_vtable Thing$vtable;

// Thing object layout
struct thing {
  Thing$vtable *vtbl;       // vtable pointer
  int x_;                   // instance variable
};

// Thing member functions - setX and getVal are virtual
void Thing$setX(Thing *this, int x) { this->x_ = x; }
int  Thing$getVal(Thing* this)      { return this->x_; }
int  Thing$getNum(Thing* this)      { return this->x_; }

// Thing vtable layout -- pointers to virtual functions
struct thing_vtable {
  void   (*setX)(Thing*, int);
  int  (*getVal)(Thing*);
};

// Thing vtable (initialized global data)
static Thing$vtable Thing$vtbl = { &Thing$setX, &Thing$getVal };

// Combination new+constructor for Thing objects
// return a new Thing with given x value
Thing *Thing$new(int x) {
  // allocate object
  Thing *this = (Thing *)malloc(sizeof(Thing));

  // initialize vtable
  this->vtbl = &Thing$vtbl;

  // initialize field
  this->x_ = x;

  // return pointer to new object
  return this;
}

//////////// Widget (subclass of Thing)  ////////////

// Typedefs for Widget subclass objects and vtable
typedef struct widget        Widget;
typedef struct widget_vtable Widget$vtable;

// Widget object layout
struct widget {
  Widget$vtable *vtbl;   // vtable pointer
  int x_;                // superclass instance variable
  int y_;                // widget instance variable
};

// Widget member functions - setY and getVal are virtual
void Widget$setY(Widget *this, int y) { this->y_ = y; }
int  Widget$getVal(Widget *this)      { return this->y_; }
int  Widget$getNum(Widget *this)      { return this->y_; }

// Widget vtable layout - pointers to virtual functions
// First two entries match order in superclass Thing
struct widget_vtable {
  void   (*setX)(Thing*, int);   // inherited
  int  (*getVal)(Widget*);       // override
  void   (*setY)(Widget*, int);  // new in Widget
};

// Widget vtable (initialized global data)
static Widget$vtable Widget$vtbl = { &Thing$setX, &Widget$getVal, &Widget$setY };

// Combination new+constructor for Widget objects
// return a new Widget with given x and y values
Widget *Widget$new(int x, int y) {
  // allocate object
  Widget *this = (Widget *)malloc(sizeof(Widget));

  // initialize vtable
  this->vtbl = &Widget$vtbl;

  // initialize fields
  this->x_ = x;
  this->y_ = y;

  // return pointer to new object
  return this;
}

//////////// main ////////////

int main() {
  Thing  *t1 = Thing$new(17);
  Widget *w  = Widget$new(42, 333);
  Thing  *t2 = (Thing*)w;

  // virtual function calls
  int n1 = t1->vtbl->getVal(t1);  // t1->getVal();
  int n2 = t2->vtbl->getVal(t2);  // t2->getVal(); (same object as w)
  int n3 = w->vtbl->getVal(w);    // w->getVal();  (same object as t2)
  printf("t1 val = %d, t2 val = %d, w val = %d\n", n1, n2, n3);

  // non-virtual function calls
  n1 = Thing$getNum(t1);    // t1->getNum();
  n2 = Thing$getNum(t2);    // t2->getNum(); (same object as w)
  n3 = Widget$getNum(w);    // w->getNum();  (same object as t2)
  printf("t1 num = %d, t2 num = %d, w num = %d\n", n1, n2, n3);

  return 0;  
}