// examples/modules translated by mycpp

// BEGIN mycpp output

#include "mycpp/runtime.h"

GLOBAL_STR(str0, "CONST module1");
GLOBAL_STR(str1, "func1");
GLOBAL_STR(str2, "cat");
GLOBAL_STR(str3, "CONST module2");
GLOBAL_STR(str4, "func2");
GLOBAL_STR(str5, "white");
GLOBAL_STR(str6, "brown");
GLOBAL_STR(str7, "result = %d");
GLOBAL_STR(str8, "%s dog: meow");
GLOBAL_STR(str9, "%s sphinx");
GLOBAL_STR(str10, "abstract");

namespace module1 {  // forward declare

  class Cat;

}  // forward declare namespace module1

namespace module2 {  // forward declare


}  // forward declare namespace module2

namespace modules {  // forward declare

  class Dog;
  class Sphinx;

}  // forward declare namespace modules

namespace module1 {  // declare

extern BigStr* CONST1;
void func1();
int fortytwo();
class Cat {
 public:
  Cat();
  virtual void Speak();
  virtual void AbstractMethod();
  
  static constexpr uint32_t field_mask() {
    return kZeroMask;
  }

  static constexpr ObjHeader obj_header() {
    return ObjHeader::ClassFixed(field_mask(), sizeof(Cat));
  }

  DISALLOW_COPY_AND_ASSIGN(Cat)
};


}  // declare namespace module1

namespace module2 {  // declare

extern BigStr* CONST2;
void func2();

}  // declare namespace module2

namespace modules {  // declare

void run_tests();
void run_benchmarks();
class Dog {
 public:
  Dog(BigStr* color);
  void Speak();
  BigStr* color;

  static constexpr ObjHeader obj_header() {
    return ObjHeader::ClassScanned(1, sizeof(Dog));
  }

  DISALLOW_COPY_AND_ASSIGN(Dog)
};

class Sphinx : public ::module1::Cat {
 public:
  Sphinx(BigStr* color);
  virtual void Speak();
  virtual void AbstractMethod();

  BigStr* color;
  
  static constexpr uint32_t field_mask() {
    return ::module1::Cat::field_mask()
         | maskbit(offsetof(Sphinx, color));
  }

  static constexpr ObjHeader obj_header() {
    return ObjHeader::ClassFixed(field_mask(), sizeof(Sphinx));
  }

  DISALLOW_COPY_AND_ASSIGN(Sphinx)
};


}  // declare namespace modules

namespace module1 {  // define

BigStr* CONST1 = str0;

void func1() {
  mylib::print_stderr(str1);
  mylib::print_stderr(module2::CONST2);
}

int fortytwo() {
  return 42;
}

Cat::Cat() {
  ;  // pass
}

void Cat::Speak() {
  mylib::print_stderr(str2);
}

void Cat::AbstractMethod() {
  FAIL(kNotImplemented);  // Python NotImplementedError
}

}  // define namespace module1

namespace module2 {  // define

BigStr* CONST2 = str3;

void func2() {
  mylib::print_stderr(str4);
  mylib::print_stderr(module1::CONST1);
}

}  // define namespace module2

namespace modules {  // define

using module2::func2;

void run_tests() {
  modules::Dog* dog = nullptr;
  module1::Cat* cat = nullptr;
  modules::Sphinx* cat2 = nullptr;
  StackRoot _root0(&dog);
  StackRoot _root1(&cat);
  StackRoot _root2(&cat2);

  module1::func1();
  func2();
  dog = Alloc<Dog>(str5);
  dog->Speak();
  cat = Alloc<module1::Cat>();
  cat->Speak();
  cat2 = Alloc<Sphinx>(str6);
  cat2->Speak();
  cat = cat2;
  cat->Speak();
  cat->AbstractMethod();
}

void run_benchmarks() {
  int i;
  int n;
  int result;
  i = 0;
  n = 2000000;
  result = 0;
  while (i < n) {
    result += module1::fortytwo();
    i = (i + 1);
  }
  mylib::print_stderr(StrFormat("result = %d", result));
}

Dog::Dog(BigStr* color) {
  this->color = color;
}

void Dog::Speak() {
  mylib::print_stderr(StrFormat("%s dog: meow", this->color));
}

Sphinx::Sphinx(BigStr* color) : ::module1::Cat() {
  this->color = color;
}

void Sphinx::Speak() {
  mylib::print_stderr(StrFormat("%s sphinx", this->color));
}

void Sphinx::AbstractMethod() {
  mylib::print_stderr(str10);
}

}  // define namespace modules

int main(int argc, char **argv) {
  gHeap.Init();

  char* b = getenv("BENCHMARK");
  if (b && strlen(b)) {  // match Python's logic
    fprintf(stderr, "Benchmarking...\n");
    modules::run_benchmarks();
  } else {
    modules::run_tests();
  }

  gHeap.CleanProcessExit();
}