
#include <systemc.h>

// Tests for 2.0.1 extended coding styles, illustrating that SystemC is a class library, not a syntax

static int global_count = 0;

SC_MODULE(Mod)
{
  SC_CTOR(Mod)
  {
    sc_assert(std::string(this->kind()) == "sc_module");
  }
};



SC_MODULE(Mod0)
{
  SC_CTOR(Mod0)
  {
    sc_assert(std::string(basename()) == "mod0");
    SC_METHOD(method);
    dont_initialize();
    method();             //// Calling an SC_METHOD member function directly DOULOS009

    for (int i = 0; i < 3; i++)
    {
      SC_THREAD(thread);  //// Registering the same function multiple times DOULOS046
                          //// Get warnings re-defining sc_object names, multiple threads created
    }
    SC_THREAD(thread);
    SC_THREAD(thread);  //// Compile-time error - 'threadhandle' redefinition

    f();
  }
  void method(void) {
    sc_assert(sc_get_current_process_handle().proc_kind() == SC_METHOD_PROC_);
  ++ global_count;
  }
  void thread(void) {
  sc_assert(sc_get_current_process_handle().proc_kind() == SC_THREAD_PROC_);
  ++ global_count;
  }

  void f() { SC_THREAD(g); } //// Process registered in member function called from constructor DOULOS007
  void g()
  {
    sc_assert(std::string(sc_get_current_process_handle().name()) == "top.mod0.g");
  ++ global_count;
  }
};


struct Chan               //// Pseudo-channel used for port-less interprocess communication
                          //// but not derived from sc_module, sc_prim_channel or sc_interface DOULOS048
{
  void write(int i) { wait(10, SC_NS); data = i; e.notify(); }
  void read(int& i) { wait(e); i = data; }
  int data;
  bool wr, re;
  sc_event e;
};

struct MyMod: Mod         //// Class derived from an SC_MODULE DOULOS050
{
  sc_in<bool> p;

  MyMod(sc_module_name n)
  : Mod(n)
  {
    SC_THREAD(p1);
    SC_THREAD(p2);
  }

  Chan ch;                //// Instance of pseudo-channel DOULOS048
  void p1() { ch.write(333); }
  void p2() { int i; ch.read(i); sc_assert(i == 333); }

  SC_HAS_PROCESS(MyMod);
};



struct C0: sc_module, virtual sc_interface //// Combining channel and interface in one class DOULOS015
{
  void write(int i) { data = i; }
  void read(int& i) { i = data; }
  int data;

  SC_CTOR(C0)
  {
    SC_THREAD(action);
  }
  void action() {
  sc_assert(std::string(sc_get_current_process_handle().get_process_object()->basename()) == "action");
  }
};


struct C1: virtual public sc_interface //// Combining channel and interface in one class DOULOS015
{
  void write(int i) { data = i; }
  void read(int& i) { i = data; }
  int data;
};


struct I_F: virtual public sc_interface
{
  virtual void method() = 0;
};

struct C2: I_F            //// Channel derived from neither sc_module nor sc_prim_channel DOULOS049
{
  void method() {}
};

struct C3: sc_object, I_F //// Channel derived from sc_object only
                          //// (and derived from neither sc_module nor sc_prim_channel) DOULOS049
{
  void method() {}
};


void check_form_of_suffix(std::string s)
{
  std::string charset = "0123456789";
  while (!s.empty())
  {
    sc_assert(s[0] == '_');
    s = s.substr(1);
    sc_assert(!s.empty());
    do
    {
      sc_assert(charset.find(s[0]) < charset.size());
      s = s.substr(1);
    } while (!s.empty() && (s[0] != '_'));
  }
}

struct Modtype: sc_module
{
  Modtype(sc_module_name n = sc_gen_unique_name("Modtype"))
  {
    if (global_count == 0)
      sc_assert(std::string(basename()) == "mt");
    else
    {
      std::string s = std::string(basename());
      sc_assert(s.substr(0,7) == "Modtype");
      sc_assert(s.size() > 7);
      check_form_of_suffix(s.substr(7));
    }
    ++ global_count;
  }
  void dump() {
    sc_assert(std::string(sc_get_current_process_handle().get_process_object()->basename()) == "thread");
    ++ global_count;
  }
};

typedef sc_signal<Modtype*> MS; //// Pointer-to-module used as the type of a signal DOULOS051



template<class T>
struct S
{
  T t;
};



SC_MODULE(Moda)
{
  sc_port<C0> p0;
  sc_port<C1> p1;
  sc_port<C2> p2;
  sc_port<C3> p3;
  sc_port<MS> p4;

  Modtype mt;

  SC_CTOR(Moda): mt("mt")
  {
  sc_assert(std::string(name()) == "top.moda");
    SC_THREAD(thread);
  }
  void thread();
};

SC_MODULE(Modb)
{
  sc_port<C0> p0;
  sc_port<C1> p1;
  sc_port<C2> p2;
  sc_port<C3> p3;
  sc_port<MS> p4;

  SC_CTOR(Modb)
  {
  sc_assert(std::string(name()) == "top.modb");
    SC_THREAD(thread);
    SC_THREAD(funny);
  }
  void thread();
  void funny();
};

void Moda::thread()
{
  p1->write(999);
  p2->method();
  p3->method();
  p4->write(&mt);
}
void Modb::thread()
{
  wait(SC_ZERO_TIME);
  int i; p1->read(i);
  sc_assert(i == 999);
  p2->method();
  p3->method();
  wait(SC_ZERO_TIME);
  (p4->read())->dump();
}

void Modb::funny()  { C1 c1; C2 c2; C3 c3; }




struct Link
{
  Link *link;
  sc_module      *m;       //// Pointer-to-module DOULOS006
  sc_signal<int> *s;       //// Pointer-to-signal
  sc_in<int>     *p;       //// Pointer-to-port DOULOS008
};

SC_MODULE(Top)
{

  SC_MODULE(Nested)        //// Nested modules DOULOS005
  {
    sc_in<int> *pp;        //// Pointer-to-port DOULOS008

    SC_CTOR(Nested)
    {
      SC_METHOD(action);

      pp = new sc_in<int>; //// Dynamic port instantiation DOULOS008

      sensitive << *pp;    //// Sensitivity separated from SC_METHOD DOULOS011
    }

  void action() { op = sc_min(3, (*pp).read() + 1); }

    sc_out<int> op;        //// Out-of-order declaration DOULOS052
  };


  Nested n;
  Link* link;
  sc_signal<bool> b;
  sc_signal<int> *sig;      //// Pointer-to-signal

  MyMod mymod;
  Moda moda;
  Modb modb;
  C0 c0;
  C1 c1;
  C2 c2;
  C3 c3;

  S<Modtype> Sm;            //// Using sc_module as a template parameter DOULOS051
  MS ms;                    //// Pointer-to-module used as the type of a signal DOULOS051

  Mod0 mod0;

  SC_CTOR(Top)
  : n("n"), mymod("mymod"), moda("moda"), modb("modb"), c0("c0"), mod0("mod0")
  {
    link = new Link;
    link->m = new Mod("mod_1");
    link->p = new sc_in<int>;
    link->link = new Link;
    link->link->m = new Mod("mod_2");  //// Buried dynamic module instantiation DOULOS002
    link->link->s = new sc_signal<int>;//// Buried dynamic channel instantiation
    link->link->p = new sc_in<int>;    //// Buried dynamic port instantiation DOULOS008

    sig = new sc_signal<int>;          //// Dynamic channel instantiation
    n.op(*sig);
    (*(n.pp)).bind(*sig);              //// Binding dynamically allocated port DOULOS008

    mymod.p(b);

    moda.p0(c0);
    moda.p1(c1);
    moda.p2(c2);
    moda.p3(c3);
    moda.p4(ms);

    modb.p0(c0);
    modb.p1(c1);
    modb.p2(c2);
    modb.p3(c3);
    modb.p4(ms);
  }

};


int sc_main(int argc, char* argv[])
{
  cout << "Should be silent except for some renaming warnings..." << endl;

  sc_signal<int> s;
  Top top("top");
  top.link->p->bind(s);
  top.link->link->p->bind(s);         //// Binding dynamically allocated port DOULOS008

  sc_start();
  sc_assert(global_count == 10);
  sc_assert(top.sig->read() == 3);

  cout << endl << "Success" << endl;
  return 0;
}
