#ifndef HMM_HH
#define HMM_HH

#include "HMMLogger.hh"
#include "ssdbg.hh"
#include <vector>
#include <string>

#define HMM_MAX_NAMELEN 32
#define HMM_KEEP_STATES 10

// maximum number of times to print out the error about zero state probability
#define HMM_MAX_NOSTATE_ERR 10

/****************************************************************************
 **
 ** Abstract:
 **
 ** This class just provides the data structures and methods for
 ** keeping track of and updating state in a simple HMM which has
 ** already been entrained.  We therefore have a vector of possible
 ** "observations" (e.g. clusters), each associated with a probability
 ** of being indicative of a particular state.  We also have a vector
 ** of transition probabilities from each state, and vectors
 ** representing the current and previous probabilities of being in
 ** each state.  When a new observation comes along, we update this
 ** posterior state distribution according to,
 **
 ** p_t(s_i) = a_j * p(s_i|Obs)* \sum_j p(s_i|s_j)*p_{t-1}(s_j)
 ** 
 ** NOTE: the first state added is the initial state (starting probability is
 ** one, all other states are probability zero at the start).
 **
 ****************************************************************************/

// Just in case we want to keep track of probabilities with some other
// container.  Also, observations are currently a cluster number, which
// is just an integer index.
typedef double prob_t;
typedef size_t obs_t;
typedef size_t state_t;

// First we define a class which just handles a single state.  The
// only really tricky thing here is that the vector of conditional
// probabilities is sparse, so we keep track of both the index and the
// probability
class HMMState{
public:
  typedef struct{
    obs_t obs;
    prob_t prob;
  }cp_t;
  typedef std::vector<cp_t>::iterator cpi_t;
  HMMState(char *name,prob_t initprob);
  ~HMMState();

  // methods for extracting values
  prob_t prev(int i); // ith single previous value
  prob_t maxprev(); // max over saved previous values
  prob_t cur();
  char *name();
  prob_t trans(state_t tostate);
  prob_t cond(obs_t obs);

  // methods for modifying values.
  void settrans(state_t tostate,prob_t tp);
  void setcond(obs_t cob,prob_t cpr);
  bool newcur(prob_t newprob);  // returns (new != prev) after updating new and cur
  void reset(prob_t newprob);  // set all prevs as well as cur
private:
  char *statename;                // Name of state.
  prob_t curprob;                 // Estimated probability of being in this state.
  prob_t prevprob[HMM_KEEP_STATES]; // Previous estimates from most recent back
  std::vector<prob_t> transprob;  // Marginal probability of transition to given state
  std::vector<cp_t> condprob;     // Conditional prob of being in this state given indexed obs
  cpi_t findcp(obs_t ob);
}; // HMMState
  
class HMM{
public:
  HMM(char *name);
  ~HMM();

  void addstate(char *name); // Add a state to the vector
  prob_t stateprev(state_t st,int i); // get the ith prev value
  prob_t maxprev(state_t st); // max over saved previous values
  prob_t statecur(state_t st); // get cur value
  bool updatelog(); // has there been a change since the last time this was called?
  obs_t curobs();
  void dumpstate(); // Syslog dump of the current state
  void update(obs_t obs); // update state given new observation
  void dumpstate(); // Syslog dump of the current state
  void loadConfig(char *transcfgname,char *condcfgname,std::vector<string> *statenames,int maxobs);
  int HMM::findstate(char *name);
  void reset(); // Reset the state to initial values.

private:
  std::vector<HMMState*> m_statelist;
  HMMLogger *m_logger;
  char *m_name;
  obs_t m_curobs,m_prevobs;
  int num_nostate_errs;
  bool change_not_logged;

  // Used in loadConfig
  void dumptrans(); // Syslog dump of the transition matrix
}; // HMM

#endif // HMM
