#ifndef O815_H
#define O815_H

#include <string>
#include <vector>
#include <stdlib.h>
#include <getopt.h>

#include "latlib/paraq.h"
#include "latlib/writeout.h"
#include "latlib/hypercache.h"

using namespace std;

class obs;

class o815 {
 public:

  class obs {
  public:
    obs(const string& _obsid, const string& _datadesc, o815 *_O815, const int& _obsmemSize, const string& _obsidpostfix="");
    void finish();
    void meas(bool loadedobs, const int& nthmeas);
    void start();
    string getDataDesc() { return datadesc; };
    int ocid;
    string obsid;
    string obsidpostfix;
  private:
    virtual void _meas(bool loadedobs, const int& nthmeas)=0;
    virtual void _finish()=0;
    virtual void _start()=0;
  protected:
    o815 *O815;
    char *obsMem;
    string simid, datadesc;
    ostream *out, *log;
    paraq* paraQ;
  };

  class sim {
  public:
    sim(o815 *_O815, const int& _confmemSize);
    ~sim ();
    void nextConfig();
    char *confMem;
    virtual void _newParas()=0;
    int nequi, nskip;
    void resetConfig();
  private:
    virtual void _makeSweep()=0;
    virtual void _resetConfig()=0;
    char *startConfiguration;
  protected:
    o815 *O815;
    ostream *log;
    int confmemSize;
  };

  struct {
    int nmeas;
    int nskip;
    int nequi;
    int lsize[2];
    pair<string,int> obscache;
    pair<string,int> confcache;
    string outdir;
    int idonly;
    int showjobnum;
    string startconfig;
  } comargs;

  struct comoption {
    string name;
    int has_arg;
    int *flag;
    int val;
    string optdesc;
    string argdesc;
  };

  o815(int argc, char **argv, const string& _programid, comoption specOps[]=NULL, void (*helpHeader)()=NULL);
  void postParaInit();
  ~o815();
  paraq *paraQ;
  writeout *out;
  vector<obs*> observables;
  sim* Sim;
  void mainLoop();
  string headMaster( bool hashedrange=false );
  void addPara(const string& paraid, const double& paraDefault);
  void addComOption(const char* name, int has_arg, int *flag, int val, const char* optdesc, const char* argdesc);
  vector< pair<int,char*> > parsedSpecOps;
  vector<char*> lonelyArgs;

private:
  vector<comoption> comOptions;
  int numprocs, rank;
  static void listArg(int *target, int tlen, char *listarg);
  void parseArgs(int argc, char **argv, comoption specOps[]);
  string programid;
  int nextParas();
  comoption* getOptionByVal(int val);
  static void readCacheArgs(const string& arg, string& cachedir, int& cachemode);
  unsigned long hash(const string& str);
};

#endif
