#ifndef INC_ACTION_CLUSTERDIHEDRAL_H
#define INC_ACTION_CLUSTERDIHEDRAL_H
#include "Action.h"
class Action_ClusterDihedral : public Action {
  public:
    Action_ClusterDihedral();
    DispatchObject* Alloc() const { return (DispatchObject*)new Action_ClusterDihedral(); }
    void Help() const;
  private:
    Action::RetType Init(ArgList&, ActionInit&, int);
    Action::RetType Setup(ActionSetup&);
    Action::RetType DoAction(int, ActionFrame&);
    void Print();
    int ReadDihedrals(std::string const&);
    class DCnode;
    class DCmask;

    std::vector<DCnode> dcarray_;  ///< Hold counts for each bin# combo.
    std::vector<DCmask> DCmasks_;  ///< Hold 4 atom mask for each dihedral
    std::vector<int> Bins_;        ///< For holding bin combo each frame
    int phibins_;
    int psibins_;
    int CUT_;
    int lastframe_;
    Topology* dcparm_;             ///< Set to first parm encountered.
    CpptrajFile* outfile_;
    CpptrajFile* framefile_; // filenames[1]
    CpptrajFile* infofile_;  // filenames[2]
    AtomMask mask_;
    DataSet* CVT_; ///< Hold # clusters vs time.
    double minimum_; ///< Value of first bin 
    int debug_;
};
// -----------------------------------------------------------------------------
class Action_ClusterDihedral::DCnode {
  public:
    DCnode() : count_(0) {}
    DCnode(std::vector<int>& binIn, int frameIn) : 
           BinIDs_(binIn), frames_(1, frameIn), count_(1) {}
    DCnode(const DCnode& rhs) : 
           BinIDs_(rhs.BinIDs_), frames_(rhs.frames_), count_(rhs.count_) {}
    DCnode& operator=(const DCnode& rhs) {
      if (this==&rhs) return *this;
      BinIDs_ = rhs.BinIDs_;
      frames_ = rhs.frames_;
      count_ = rhs.count_;
      return *this;
    }
    void Increment()        { ++count_;                 }
    void AddFrame(int fIn)  { frames_.push_back( fIn ); }
    // Want sort in descending order, so reverse '>'
    bool operator<(const DCnode& rhs) const  { return (count_ > rhs.count_);  }
    bool operator>(const DCnode& rhs) const  { return (count_ < rhs.count_);  }
    bool operator==(const DCnode& rhs) const { return (count_ == rhs.count_); }
    bool BinMatch(std::vector<int>& binIn) const {
      return (std::equal(BinIDs_.begin(), BinIDs_.end(), binIn.begin()));
    }
    long int Count()  const { return count_; }
    typedef std::vector<int>::const_iterator bin_it;
    bin_it binbegin() const { return BinIDs_.begin(); }
    bin_it binend()   const { return BinIDs_.end();   }
    typedef std::vector<int>::const_iterator frame_it;
    frame_it framebegin() const { return frames_.begin(); }
    frame_it frameend()   const { return frames_.end();   }
    int NumFrames()       const { return (int)frames_.size(); }
  private:
    std::vector<int> BinIDs_;
    std::vector<int> frames_;
    long int count_;
};
// -----------------------------------------------------------------------------
class Action_ClusterDihedral::DCmask {
  public: 
    DCmask() : a1_(0), a2_(0), a3_(0), a4_(0), bins_(0) {}
    DCmask(int a1, int a2, int a3, int a4, int bins, double min) :
           a1_(a1), a2_(a2), a3_(a3), a4_(a4), 
           bins_(bins), step_(360/(double)bins), min_(min) {}
    int A1()   const { return a1_; }
    int A2()   const { return a2_; }
    int A3()   const { return a3_; }
    int A4()   const { return a4_; }
    int Bins() const { return bins_; }
    double Step() const { return step_; }
    double Min()  const { return min_; }
  private:
    int a1_;
    int a2_;
    int a3_;
    int a4_;
    int bins_;
    double step_; 
    double min_;
};
#endif
