#ifndef LEMON_SIMANN_H
#define LEMON_SIMANN_H

namespace lemon {

  const double INFTY = 1e24;

  class SimAnnBase {
  public:
    class Controller;
  private:
    Controller *controller;
  protected:
    double curr_cost;
    double prev_cost;
    double best_cost;

    virtual void mutate() = 0;
    virtual void revert() = 0;
    virtual void saveAsBest() = 0;
  public:
    SimAnnBase() {
      curr_cost = prev_cost = best_cost = INFTY;
    }
    void setController(Controller &_controller) { controller = &_controller; }
    double getBestCost() { return best_cost; }
    void run() {
      while (controller->next()) {
        mutate();
        if (controller->accept(prev_cost - curr_cost)) {
          controller->acceptEvent();
          if (curr_cost < best_cost) {
            saveAsBest();
            controller->improveEvent();
          }
        }
        else {
          revert();
          controller->rejectEvent();
        }
      }
    }

    class Controller {
    public:
      virtual void acceptEvent() {}
      virtual void improveEvent() {}
      virtual void rejectEvent() {}
      virtual bool next() = 0;
      virtual bool accept(double cost_diff) = 0;
    };
  };

  template <typename E>
  class SimAnn : public SimAnnBase {
  private:
    E *curr_ent;
    E *prev_ent;
    E *best_ent;
  public:
    SimAnn() : SimAnnBase() {}
    void setEntity(E &Ent) {
      curr_ent = new E(Ent);
      prev_ent = new E(Ent);
      best_ent = new E(Ent);
    }
    E getBestEntity() { return *best_ent; }
    void mutate() {
      *prev_ent = *curr_ent;
      prev_cost = curr_cost;
      curr_cost = curr_ent->mutate();
    }
    void revert() {
      E *tmp = curr_ent;
      curr_ent = prev_ent;
      prev_ent = tmp;
      curr_cost = prev_cost;
    }
    void saveAsBest() {
      *best_ent = *curr_ent;
      best_cost = curr_cost;
    }
  };

  class EntitySkeleton {
  public:
    // returns the new cost
    double mutate() { return 0.0; }
  };

  template <typename E>
  class SimAnn2 : public SimAnnBase {
  private:
    E *curr_ent;
    E *best_ent;
  public:
    SimAnn2() : SimAnnBase() {}
    void setEntity(E &Ent) {
      curr_ent = new E(Ent);
      best_ent = new E(Ent);
    }
    E getBestEntity() { return *best_ent; }
    void mutate() {
      curr_ent->mutate();
    }
    void revert() {
      curr_ent->revert();
    }
    void saveAsBest() {
      *best_ent = *curr_ent;
      best_cost = curr_cost;
    }
  };

  class EntitySkeleton2 {
  public:
    // returns the new cost
    double mutate() { return 0.0; }
    // restores the entity to its previous state i.e. reverts the effects of
    // the last mutate()
    void revert() {}
  };

}

#endif
