// the observer test
//
#include "Utilities/Configuration/interface/Architecture.h"
#include<cstdlib>

#include "Utilities/Notification/interface/LazyObserver.h"
#include "Utilities/Notification/interface/Observer.h"
#include "Utilities/Notification/interface/Dispatcher.h"
#include <iostream>

#include <Utilities/GenUtil/interface/CMSTimers.h> 
#include<vector>
#include<algorithm>
#include "Utilities/GenUtil/interface/CMSexception.h"
#include "Utilities/GenUtil/interface/own_ptr.h"

typedef StopWatch os_stopwatch;

/// the "event" class
class E {

public:
  E(int i):_id(i){}

  int id() const {return _id;}
  void setId(int i) { _id=i;}

private:
  int _id;

};

/// the "event" class
class E1 {

public:
  E1(int i):_id(i){}

  int id() const {return _id;}
  void setId(int i) { _id=i;}

private:
  int _id;

};


struct EEqual {
  // bool operator() (E const * e1, E const * e2) const { return e1->id()==e2->id();}
  bool operator() (E const * e1, E const * e2) const { return e1==e2;}
};

/// the dispatcher...
typedef Dispatcher<E*> ED;
typedef Dispatcher<E1*> ED1;

/// the observers
typedef Observer<E*> EO;
typedef LazyObserver<E*> ELO;
// typedef ActiveObserver<E*> EAO;


/// a concrete obsever
class O1 : private EO {

public:
  O1(): _count(0) {init();}

  void upDate(E* ev) { if (ev!=0) ++_count;}

  int count()  const {return _count;}

private:

  int _count;

};

/// a buggy obsever
class Bug : private EO {
  
public:
  Bug(): _count(0) {init();}
  
  void upDate(E* ev) { 
    if (ev!=0) ++_count;
    if ( (_count%10)==2) throw Genexception("Bug");
  }
  
  int count()  const {return _count;}
  
private:
  
  int _count;
  
};

/// hello obsever
class Hello : private Observer<E*> {

public:
  Hello(int i=0) :id(i) {init();}

  void upDate(E* ev) { 
    if (ev==0) return; 
    cout << "Hello " 
	 << id << " " 
	 << ev->id() << endl;
  }

private:
  int id;

};

/// hello obsever
class Hello1 : private Observer<E1*> {

public:
  Hello1(int i=0) :id(i) {init();}

  void upDate(E1* ev) { 
    if (ev==0) return; 
    cout << "Hello " 
	 << id << " " 
	 << ev->id() << endl;
  }

private:
  int id;

};

/// a hello lazy observer
class LHello : protected LazyObserver<E*> {

public:
  LHello(int i=0) :id(i){}

  void lazyUpDate(E* ev) {
    if (ev==0) return; 
    cout << "Lazy Hello " 
	 << id << " " 
	 << ev->id() << endl;
  }

  void act() { check();}

private:
  int id;


};

/// a concrete lazy observer
class LOB : protected ELO {

public:
  LOB(): _count(0) {}

  void lazyUpDate(E* ev) { if (ev==0) return; if (ev->id()%2) ++_count;}

  int count() const {return _count;}

  bool compare(E* ev) {check(); return ev==observed();}

protected:

  int _count;

};

// a concrete bizarre LO
class LO1 : public LOB {

public:

  bool compare(E* ev) {check(); return ev==observed()&&lo.compare(ev);}

private:

  LOB lo;
};



/// a concrete active observer
class AO1 : private ELO {
//class AO1 : public EAO {

public:
  AO1(): _count(0) {}

  void lazyUpDate(E* ev) { if (ev==0) return; if ((ev->id()%3)==0) ++_count;}

  int count() const {return _count;}

  bool compare(E* ev) {check(); return ev==observed();}

private:

  int _count;

};

/// another concrete observer (with two lazy enclosed)
class O2 : private EO {

public:
  O2(): _count(0), lo1(new LO1) { init(); }

  ~O2() {}

  void upDate(E* ev) { if (ev==0) return; if(lo1->compare(ev)&&ao1.compare(ev)) ++_count;}

  int count()  const {return _count;}

  int lo1Count()  const {return lo1->count();}
  int ao1Count()  const {return ao1.count();}

private:

  int _count;

  own_ptr<LO1> lo1;
  AO1 ao1;

};

/// a concrete lazy observer with error.
class LOER : protected ELO {

public:
  LOER(): _count(0) {}

  void lazyUpDate(E* ev) { if (ev==0) return; compare(ev);
  if (ev->id()%2) ++_count;}

  int count() {check(); return _count;}

  bool compare(E* ev) {check(); return ev==observed();}

protected:

  int _count;

};


/// a concrete simple lazy observer
class LZO : protected ELO {

public:
  //
  LZO(): _count(0) {}

  // copy constructor
  // LZO( const LZO & l) : ELO(l) {}

  void lazyUpDate(E* ev) { if (ev==0) return; ++_count;}

  inline int count() {check(); return _count;}
  inline int realCount() const { return _count;}

protected:

  int _count;

};


// an observer with 2000 lazy called "rate"% of the time
class O2000 : private EO {
  
public:
  typedef vector<LZO> VLZO;

  O2000(): rate(100.), _count(0), tot_(0), lo(2000) { 
    init(); 
  }
  
  ~O2000() { }

  void setRate(float r) { rate=r;}
  
  void upDate(E* ev) { 
    if (ev==0) return;
    vector<LZO>::iterator p=lo.begin()+(unsigned int)(rate*drand48());
    vector<LZO>::iterator e=lo.end();
    for (; p<e; p+= (unsigned int)(rate*drand48()) ) {
      // _count = max(_count,(*p).count());
      if ((*p).count()>_count) _count = (*p).count();
      tot_++;
    }
  }
  
  inline int count()  const {return _count;}
  inline int tot()  const {return tot_;}
  inline int dump() const {
    VLZO::const_iterator p=lo.begin();
    VLZO::const_iterator e=lo.end();
    int c=0;
    for (; p!=e; p++) c = max(c,(*p).realCount());
    return c;
  }
  
private:
  
  float rate;
  int _count;
  int tot_;
  
  VLZO lo;
  
};


// a nasty observer 
class NYO: private EO {
  
public:
  void upDate(E* ev) { 
    if (ev==0) return;
    O2000 * a1 = new O2000;
    O2000 * a2 = new O2000;
    O2000 * a3 = new O2000;
    delete a3;
    { O2000 t;}
    delete a1;
    O2 * p1 = new O2; 
    O2 * p2 = new O2; 
    O2 * p4 = new O2; 
    delete p2;

    delete a2;

    delete p1;
    delete p4;

  }


};

#include "Utilities/Notification/interface/WatchedDispatcher.h"


// a watchDog
template<class T>
class TmyWD : private DispatcherWatcher<T*> {

 public:
  TmyWD(){}
  virtual ~TmyWD(){}
  void on() { stat=true;}
  void off() { stat=false;}

 private:
  virtual bool watch(T * ev) {
    return stat;
  }

 private:
  bool stat;
};

typedef TmyWD<E> myWD;
typedef TmyWD<E1> myWD1;

#include "Utilities/Notification/interface/ParentDispatcher.h"

/// a sub event
class CE : public E {
public:

  CE(int i) : E(i){}

};

typedef Dispatcher<CE*> CED;

/// a concrete observer
class CO1 : private Observer <CE*> {

public:
  CO1(): _count(0) {init();}

  void upDate(CE* ev) { 
    if (ev!=0) ++_count;
    cout << "hello i'm a co1 " << count() << endl;}

  int count()  const {return _count;}

private:

  int _count;

};

/// hello obsever
class HelloC : private Observer<CE*> {

public:
  HelloC(int i=0) :id(i) {init();}

  void upDate(CE* ev) { 
    if (ev==0) return; 
    cout << "Hello Children "
	 << id << " " 
	 << ev->id() << endl;
  }

private:
  int id;

};

/// and its obsevers


/// a dumper utility
struct BaseDump {
  // static int dumpIt(BaseDump & it) {it.dump(); return 0;}
  static int dumpIt(own_ptr<BaseDump> & it) {it->dump(); return 0;}
  // static int dumpIt(own_ptr<BaseDump> * it) {(*it)->dump(); return 0;}
  virtual ~BaseDump(){}
  virtual void dump() const =0;
};

template<class T>
struct Dump : public BaseDump {
  Dump(T const * t) : _t(t) {} 
  virtual ~Dump() {delete _t;}
  void dump() const;
  T const * _t;
};

template<> void Dump<O1>::dump() const { cout << _t->count() << " ";}
template<> void Dump<O2>::dump() const { cout << _t->count() << ":"
				   << _t->lo1Count() << ":"
				   << _t->ao1Count() << " ";}

#include <vector>
#include <algorithm>
#include <functional>

template<class T>
struct PrintCount { 
  void operator()(const T) const { cout << t->count() << " ";}
};

template<class T>
int  printCount(T t) { cout << t->count() << " "; return 0;}

// void printCount(const O2 * t) { cout << t->count() << " ";}

class TrivialDispatcherUI {
public: 
  TrivialDispatcherUI(const string & sl) {
    BaseDispatcher::streams().on(sl);
    cout << "Dispatcher verbosity set to " << sl << endl;
  }
  
  
};


struct Fake{};
class OFake : private Observer<Fake *> {
public:
  OFake() { init();}
  void upDate(Fake*){}
};
int main() {

  {
    cout << "empty dispatcher" << endl;
    os_stopwatch stopwatch;
    Fake fake;

    Dispatcher<Fake*> & dfake = *Dispatcher<Fake*>::instance();
    int nev = 10000; 
    stopwatch.start();
    for(int ie=0;ie<nev; ie++) { 
      Dispatcher<Fake*>::instance()->newEvent(&fake);
    }
    stopwatch.stop();
    cout << "time for " << nev << " fake events " << stopwatch.lap() << endl;

    stopwatch.reset();
    stopwatch.start();
    for(int ie=0;ie<nev; ie++) { 
      dfake.newEvent(&fake);
    }
    stopwatch.stop();
    cout << "time for " << nev << " fake events " << stopwatch.lap() << endl;

    OFake of1;
    stopwatch.reset();
    stopwatch.start();
    for(int ie=0;ie<nev; ie++) { 
      dfake.newEvent(&fake);
    }
    stopwatch.stop();
    cout << "time for " << nev << " fake events 1o " << stopwatch.lap() << endl;


    stopwatch.reset();
    stopwatch.start();
    for(int ie=0;ie<nev; ie++) {
      Fake * pf = new Fake;
      dfake.newEvent(pf);
      delete pf;
    }
    stopwatch.stop();
    cout << "time for " << nev << " new fake events " << stopwatch.lap() << endl;

  }


  // typedef vector<O1*> VO1;
  typedef vector<own_ptr<BaseDump> > VO1;
  


  {
    TrivialDispatcherUI("debug");
    cout << "\nbasic test\n" <<endl;
    Hello o1(1);
    LHello lo1(1);
    lo1.act();
    E * ep = new E(44);
    ED::instance()->newEvent(ep);
    lo1.act();
    Hello o2(2);
    LHello lo2(2);
    lo1.act();

    delete ep; ep = new E(45);
    ED::instance()->newEvent(ep);
    lo1.act();
    {
      Hello o3(3);
      LHello lo3(3);
      lo3.act();
      LHello lo4(4);
     }
    delete ep; ep = new E(46);
    ED::instance()->newEvent(ep);
    lo1.act();
    lo2.act();
    delete ep;
    TrivialDispatcherUI("test");
    ED::instance()->newEvent(0);
  }

  cout << endl;

  { 
    cout << "\nexception test" << endl;
    Bug b1;
    for ( int ie=0; ie!=100; ie++ ) { 
      E * ep = new E(ie);
      try {
	ED::instance()->newEvent(ep);
      } catch (Genexception & ce) {
	cout << ce.what() << endl;
      }
      delete ep;
    }
    cout << "bug count " << b1.count() << endl << endl;
    ED::instance()->newEvent(0);
  }



  //  build N O1
  const int NO1 = 1000;
  VO1 vo1;
  int i;
  for (i=0; i<NO1; ++i) {vo1.push_back(VO1::value_type(new Dump<O1>(new O1())));};
  //  build N O2
  const int NO2 = 1000;
  for (i=0; i<NO2; ++i) {vo1.push_back(VO1::value_type(new Dump<O2>(new O2())));};

  for_each(vo1.begin(),vo1.end(),BaseDump::dumpIt);
  cout <<endl;

  {
    cout << "\ntesting the nasty one\n" << endl;
    NYO nasty;
    E * ep = new E(44);
    ED::instance()->newEvent(ep);
    {NYO nasty2;}
    delete ep; ep = new E(45);
    ED::instance()->newEvent(ep);
    delete ep;
    ED::instance()->newEvent(0);
  }

    cout << "\nLong loop \n" << endl;
  // event loop
  int Nev = 2000;

  int ie=0;
  E theEvent(0);
  E * ep;

  os_stopwatch stopwatch;
  stopwatch.start();

  while(++ie<=Nev) { 
    theEvent.setId(ie);
    ep = new E(ie);
    ED::instance()->newEvent(ep);
    delete ep;
  }


  stopwatch.stop();
  cout << "time for " << Nev << " events " << stopwatch.lap() << endl;

  for_each(vo1.begin(),vo1.end(),ptr_fun(BaseDump::dumpIt));
  cout <<endl;
  //  for_each(vo1.begin(),vo1.end(),ptr_fun(printCount));
  // cout <<endl;

  // delete
  vo1.clear();
  

  // time again

  // TrivialDispatcherUI("debug");
  O2000 a0;

  cout <<"timing 2000 LO randomply called" << endl;

  cout << "\n        rate 1/100" << endl;
  a0.setRate(100.);
  stopwatch.reset();
  stopwatch.start();
  ie=0;
  Nev = 20000;
  while(++ie<=Nev) { 
    theEvent.setId(ie);
    ep = new E(ie);
    ED::instance()->newEvent(ep);
    delete ep;
  }
  stopwatch.stop();
  cout << "time for " << Nev << " events " << stopwatch.lap() << endl;
  cout << a0.count() << " " <<  a0.tot() 
       << " " <<  a0.dump() << endl <<endl;

  cout << "\n     rate 1/10" << endl;
  a0.setRate(10);
  stopwatch.reset();
  stopwatch.start();
  ie=0;
  Nev = 20000;
  while(++ie<=Nev) { 
    theEvent.setId(ie);
    ep = new E(ie);
    ED::instance()->newEvent(ep);
    delete ep;
  }
  stopwatch.stop();
  cout << "time for " << Nev << " events " << stopwatch.lap() << endl;
  cout << a0.count() << " " <<  a0.tot() 
       << " " <<  a0.dump() << endl <<endl;



  // check error report

  own_ptr<LOER> loer(new LOER());
  
  ep = new E(1000);
  ED::instance()->newEvent(ep);
  cout << loer->count() << endl;

  // check verbosity control

  TrivialDispatcherUI("debug");
  { own_ptr<O1> p7(new O1());}
  { own_ptr<O2> p7(new O2());}
  TrivialDispatcherUI("test");
  { own_ptr<O1> p7(new O1());}
  { own_ptr<O2> p7(new O2());}
  TrivialDispatcherUI("silent");
  { own_ptr<O1> p7(new O1());}
  { own_ptr<O2> p7(new O2());}

  delete ep; ep =0;


  TrivialDispatcherUI("debug");

  {
    ep = new E(1001);
    ED::instance()->newEvent(ep);
    cout << "\n working with watchDog " <<  endl;
    
    //  build N O1
    const int NO1 = 2;
    VO1 vo1;
    int i;
    for (i=0; i<NO1; ++i) {vo1.push_back(VO1::value_type(new Dump<O1>(new O1())));};
    //  build N O2
    const int NO2 = 2;
    for (i=0; i<NO2; ++i) {vo1.push_back(VO1::value_type(new Dump<O2>(new O2())));};
    
    for_each(vo1.begin(),vo1.end(),BaseDump::dumpIt);
    cout <<endl;
    
    myWD * awd = new myWD();
    awd->off();
    ED::instance()->newEvent(ep);
    for_each(vo1.begin(),vo1.end(),BaseDump::dumpIt);cout <<endl;
    awd->on();
    ED::instance()->newEvent(ep);
    for_each(vo1.begin(),vo1.end(),BaseDump::dumpIt);cout <<endl;
    ED::instance()->newEvent(ep);
    for_each(vo1.begin(),vo1.end(),BaseDump::dumpIt);cout <<endl;
   
    delete awd;
    ED::instance()->newEvent(ep);
    for_each(vo1.begin(),vo1.end(),BaseDump::dumpIt);cout <<endl;

    awd = new myWD();    awd->on();
    cout << "\n working with a Parent" <<endl;
    CO1 c;
    ED::instance()->newEvent(ep);
    for_each(vo1.begin(),vo1.end(),BaseDump::dumpIt);cout <<endl;
    CE * cep = new CE(44);
    CED::instance()->newEvent(cep);
    for_each(vo1.begin(),vo1.end(),BaseDump::dumpIt);cout <<endl;

    // ChildDispatcher<E*,CE*>::create();
    // ParentDispatcher<CE*,E*>::create();
    ParentDispatcher<CE*,E*> parent();

    Hello hhh(99);
    HelloC hhhc(99);

    ED::instance()->newEvent(ep);
    for_each(vo1.begin(),vo1.end(),BaseDump::dumpIt);cout <<endl;
    { Hello hh(98); HelloC hhc(98); }
    CED::instance()->newEvent(cep);
    { Hello hh(98); HelloC hhc(98); }
    for_each(vo1.begin(),vo1.end(),BaseDump::dumpIt);cout <<endl;
    CED::instance()->newEvent(cep);
    for_each(vo1.begin(),vo1.end(),BaseDump::dumpIt);cout <<endl;
    { Hello hh(98); HelloC hhc(98); }
    delete ep; ep =0;
    ED::instance()->newEvent(0);
    delete cep;
    CED::instance()->newEvent(0);
    delete awd;
  }

  {
    cout << "test DispatcherWatcher" << endl;
    own_ptr<myWD1> awd(new myWD1());
    awd->on();
    Hello1 h1;
    own_ptr<E1> ep1(new E1(44));
    ED1::instance()->newEvent(ep1.get());
    
  }

  return 0;

}








