#ifndef _CFR_VALUES_H_
#define _CFR_VALUES_H_

#include <memory>

#include "cfr_value_type.h"

using namespace std;

class BettingTree;
class Buckets;
class CardAbstraction;
class Node;
class Reader;
class Writer;

#define EJC 1

class CFRValues {
public:
  CFRValues(const bool *players, bool sumprobs, bool *streets,
	    const BettingTree *betting_tree, unsigned int root_bd,
	    unsigned int root_bd_st, const CardAbstraction &card_abstraction,
	    const unsigned int *num_buckets, const bool *compressed_streets);
  ~CFRValues(void);
  bool Chars(unsigned int p, unsigned int st) const {
    return c_values_ && c_values_[p] && c_values_[p][st];
  }
  bool Shorts(unsigned int p, unsigned int st) const {
    return s_values_ && s_values_[p] && s_values_[p][st];
  }
  bool Ints(unsigned int p, unsigned int st) const {
    return i_values_ && i_values_[p] && i_values_[p][st];
  }
  bool Doubles(unsigned int p, unsigned int st) const {
    return d_values_ && d_values_[p] && d_values_[p][st];
  }
  void Values(unsigned int p, unsigned int st, unsigned int nt,
	      unsigned char **c_values) const {
    *c_values = c_values_[p][st][nt];
  }
  void Values(unsigned int p, unsigned int st, unsigned int nt,
	      unsigned short **s_values) const {
    *s_values = s_values_[p][st][nt];
  }
  void Values(unsigned int p, unsigned int st, unsigned int nt,
	      int **i_values) const {
    *i_values = i_values_[p][st][nt];
  }
  void Values(unsigned int p, unsigned int st, unsigned int nt,
	      double **d_values) const {
    *d_values = d_values_[p][st][nt];
  }
  // Called by expand_strategy
  void SetValues(Node *node, unsigned char *c_values);
  void SetValues(Node *node, unsigned short *s_values);
  void SetValues(Node *node, int *i_values);
  void SetValues(Node *node, double *d_values);
  // This can be used for either regrets or sumprobs
  void Probs(unsigned int p, unsigned int st, unsigned int nt,
	     unsigned int offset, unsigned int num_succs, unsigned int dsi,
	     double *probs) const;
  // Get the current strategy from regrets generated by an FTL
  // (follow-the-leader) system.  The best action has a regret of zero
  // and gets a probability of 1.0.  All other actions have regrets > 0,
  // and get probabilities of 0.  There should always be (at least) one
  // action with regret 0.  We assign the entire probability mass to the
  // first such action.
  double FTLCurrentProb(unsigned int p, unsigned int st, unsigned int nt,
			unsigned int offset, unsigned int s, 
			unsigned int num_succs) const {
    if (num_succs == 1) return 1.0;
    if (c_values_ && c_values_[p] && c_values_[p][st]) {
      unsigned int s1;
      for (s1 = 0; s1 < num_succs; ++s1) {
	if (c_values_[p][st][nt][offset + s1] == 0) {
	  if (s1 == s) return 1.0;
	  else         return 0;
	}
      }
      fprintf(stderr, "No action with zero regret?!?\n");
      exit(-1);
    } else if (s_values_ && s_values_[p] && s_values_[p][st]) {
      unsigned int s1;
      for (s1 = 0; s1 < num_succs; ++s1) {
	if (s_values_[p][st][nt][offset + s1] == 0) {
	  if (s1 == s) return 1.0;
	  else         return 0;
	}
      }
      fprintf(stderr, "No action with zero regret?!?\n");
      exit(-1);
    } else if (i_values_ && i_values_[p] && i_values_[p][st]) {
      unsigned int s1;
      for (s1 = 0; s1 < num_succs; ++s1) {
	if (i_values_[p][st][nt][offset + s1] == 0) {
	  if (s1 == s) return 1.0;
	  else         return 0;
	}
      }
      fprintf(stderr, "No action with zero regret?!?\n");
      exit(-1);
    } else {
      // Assume integer or char regrets for now
      fprintf(stderr, "Expected integer or char regrets\n");
      exit(-1);
    }
  }
  void AllocateAndClearChars(Node *node, unsigned int only_p);
  void AllocateAndClearShorts(Node *node, unsigned int only_p);
  void AllocateAndClearInts(Node *node, unsigned int only_p);
  void AllocateAndClearDoubles(Node *node, unsigned int only_p);
  void DeleteBelow(Node *node);
  bool ReadNode(Node *node, Reader *reader, void *decompressor,
		unsigned int num_holdings, CFRValueType value_type,
		unsigned int offset);
  void Read(const char *dir, unsigned int it, Node *root,
	    const string &action_sequence, unsigned int only_p);
  void DeleteWriters(Writer ***writers, void ***compressors) const;
  Writer ***InitializeWriters(const char *dir, unsigned int it,
			      const string &action_sequence,
			      unsigned int only_p,
			      void ****compressors) const;
  void WriteNode(Node *node, Writer *writer, void *compressor,
		 unsigned int num_holdings, unsigned int offset) const;
  void Write(Node *node, Writer ***writers, void ***compressors,
	     bool ***seen) const;
  void Write(const char *dir, unsigned int it, Node *root,
	     const string &action_sequence, unsigned int only_p) const;
  void MergeInto(const CFRValues &subgame_values, unsigned int root_bd,
		 Node *full_root, Node *subgame_root, const Buckets &buckets,
		 unsigned int final_st);
  void ReadSubtreeFromFull(const char *dir, unsigned int it,
			   Node *full_root, Node *full_subtree_root,
			   Node *subtree_root,
			   const string &root_action_sequence,
			   unsigned int *num_full_holdings,
			   unsigned int only_p);
  bool Players(unsigned int p) const {return players_[p];}
  unsigned int NumNonterminals(unsigned int p, unsigned int st) const {
    return num_nonterminals_[p][st];
  }

protected:
  void AllocateAndClear(Node *node, CFRValueType value_type,
			unsigned int only_p);
  Reader *InitializeReader(const char *dir, unsigned int p, unsigned int st,
			   unsigned int it, const string &action_sequence,
			   unsigned int root_bd_st, unsigned int root_bd,
			   CFRValueType *value_type);
  void InitializeValuesForReading(unsigned int p, unsigned int st,
				  unsigned int nt, Node *node,
				  CFRValueType value_type);
  void Read(Node *node, Reader ***readers, void ***decompressors,
	    CFRValueType **value_types, unsigned int only_p);
  void MergeInto(Node *full_node, Node *subgame_node, unsigned int root_bd_st,
		 unsigned int root_bd, const CFRValues &subgame_values,
		 const Buckets &buckets, unsigned int final_st);
  void ReadSubtreeFromFull(Node *full_node, Node *full_subtree_root,
			   Node *subtree_node, Node *subtree_root,
			   Reader ***readers, void ***decompressors,
			   CFRValueType **value_types,
			   unsigned int *num_full_holdings,
			   unsigned int only_p, bool in_subtree);
  
  unique_ptr<bool []> players_;
  bool sumprobs_;
  unique_ptr<bool []> streets_;
  unsigned int **num_nonterminals_;
  unsigned int root_bd_st_;
  unsigned int root_bd_;
  unique_ptr<unsigned int []> bucket_thresholds_;
  // Index by p1 and street.
  unsigned int **num_card_holdings_;
  unsigned int **num_bucket_holdings_;
  // Index by p1, street, NT and index
  // Index could be board/hole-card-pair/succ, or bucket/succ
  unsigned char ****c_values_;
  unsigned short ****s_values_;
  int ****i_values_;
  double ****d_values_;
  unique_ptr<bool []> compressed_streets_;
#ifdef EJC
  long long int **new_distributions_;
#else
  int64_t **new_distributions_;
#endif
};

#endif
