// Copyright (C) 2010, Guy Barrand. All rights reserved.
// See the file tools.license for terms.

#ifndef tools_histo_b1
#define tools_histo_b1

#include "base_histo"

#include <ostream>

namespace tools {
namespace histo {

template <class TC,class TN,class TW,class TH>
class b1 : public base_histo<TC,TN,TW,TH> {
  typedef base_histo<TC,TN,TW,TH> parent;
protected:
  enum {AxisX=0};
public:
  typedef typename base_histo<TC,TN,TW,TH>::bn_t bn_t;
public:
  virtual TH bin_error(int) const = 0; //for print
public:
  void update_fast_getters(){}

  // Partition :
  int coord_to_index(TC aCoord) const {
    return axis().coord_to_index(aCoord);
  }
  TC mean() const {
    TC value;
    this->get_ith_axis_mean(AxisX,value); //can return false.
    return value;
  }
  TC rms() const {
    TC value;
    this->get_ith_axis_rms(AxisX,value); //can return false.
    return value;
  }

  // bins :
  TN bin_entries(int aI) const {
    if(parent::m_bin_number==0) return 0;
    bn_t offset;
    if(!parent::m_axes[0].in_range_to_absolute_index(aI,offset)) return 0;
    return parent::m_bin_entries[offset];
  }

  TW bin_Sw(int aI) const {
    if(parent::m_bin_number==0) return 0;
    bn_t offset;
    if(!parent::m_axes[0].in_range_to_absolute_index(aI,offset)) return 0;
    return parent::m_bin_Sw(offset);
  }

  TW bin_Sw2(int aI) const {
    if(parent::m_bin_number==0) return 0;
    bn_t offset;
    if(!parent::m_axes[0].in_range_to_absolute_index(aI,offset)) return 0;
    return parent::m_bin_Sw2(offset);
  }
  TC bin_Sxw(int aI) const {
    if(parent::m_bin_number==0) return 0;
    bn_t offset;
    if(!parent::m_axes[0].in_range_to_absolute_index(aI,offset)) return 0;
    return parent::m_bin_Sxw[offset][AxisX];
  }
  TC bin_Sx2w(int aI) const {
    if(parent::m_bin_number==0) return 0;
    bn_t offset;
    if(!parent::m_axes[0].in_range_to_absolute_index(aI,offset)) return 0;
    return parent::m_bin_Sx2w[offset][AxisX];
  }

  TH bin_height(int aI) const {
    if(parent::m_bin_number==0) return 0;
    bn_t offset;
    if(!parent::m_axes[0].in_range_to_absolute_index(aI,offset)) return 0;
    return this->get_bin_height(offset);
  }

  TC bin_center(int aI) const {return parent::m_axes[0].bin_center(aI);}

  TC bin_mean(int aI) const {
    if(parent::m_bin_number==0) return 0;
    bn_t offset;
    if(!parent::m_axes[0].in_range_to_absolute_index(aI,offset)) return 0;
    TW sw = parent::m_bin_Sw[offset];
    if(sw==0) return 0;
    return parent::m_bin_Sxw[offset][AxisX]/sw;
  }

  TC bin_rms(int aI) const {
    if(parent::m_bin_number==0) return 0;
    bn_t offset;
    if(!parent::m_axes[0].in_range_to_absolute_index(aI,offset)) return 0;
    TW sw = parent::m_bin_Sw[offset];
    if(sw==0) return 0;
    TC sxw = parent::m_bin_Sxw[offset][AxisX];
    TC sx2w = parent::m_bin_Sx2w[offset][AxisX];
    TC mean = sxw/sw;
    return ::sqrt(::fabs((sx2w / sw) - mean * mean));
  }

  // Axis :
  const histo::axis<TC>& axis() const {return parent::m_axes[0];}
  histo::axis<TC>& axis() {return parent::m_axes[0];} //touchy
public:
  //NOTE : print is a Python keyword.
  void hprint(std::ostream& a_out) {
    // A la HPRINT.
    a_out << parent::dimension() << parent::title() << std::endl;
    a_out 
      << " * ENTRIES = " << parent::all_entries()
      << " * ALL CHANNELS = " << parent::sum_bin_heights()
      << " * UNDERFLOW = " << bin_height(histo::axis<TC>::UNDERFLOW_BIN)
      << " * OVERFLOW = " << bin_height(histo::axis<TC>::OVERFLOW_BIN)
      << std::endl;
    a_out 
      << " * BIN WID = " << axis().bin_width(0)
      << " * MEAN VALUE = " << mean()
      << " * R . M . S = " << rms()
      << std::endl;
  
    // Some bins :
    bn_t bins = axis().bins();
    a_out 
      << " * ENTRIES[0]   = " 
      << bin_entries(0)    
      << " * HEIGHT[0] = " 
      << bin_height(0)    
      << " * ERROR[0] = "     
      << bin_error(0)    
      << std::endl;
    a_out 
      << " * ENTRIES[N/2] = " 
      << bin_entries(bins/2)    
      << " * HEIGHT[N/2] = " 
      << bin_height(bins/2)
      << " * ERROR[N/2] = " 
      << bin_error(bins/2)
      << std::endl;
    a_out 
      << " * ENTRIES[N-1] = " 
      << bin_entries(bins-1)    
      << " * HEIGHT[N-1] = " 
      << bin_height(bins-1)
      << " * ERROR[N-1] = " 
      << bin_error(bins-1)
      << std::endl;
  }
protected:
  b1(const std::string& a_title,
             bn_t aXnumber,TC aXmin,TC aXmax){
    parent::m_title = a_title;
    std::vector<bn_t> ns;
    ns.push_back(aXnumber);
    std::vector<TC> mins;
    mins.push_back(aXmin);
    std::vector<TC> maxs;
    maxs.push_back(aXmax);
    parent::configure(1,ns,mins,maxs);
  }
  b1(const std::string& a_title,const std::vector<TC>& aEdges) {
    parent::m_title = a_title;
    std::vector< std::vector<TC> > edges(1);
    edges[0] = aEdges;
    parent::configure(1,edges);
  }

  virtual ~b1(){}
protected:
  b1(const b1& a_from): parent(a_from) {
    update_fast_getters();
  }
  b1& operator=(const b1& a_from) {
    parent::operator=(a_from);
    update_fast_getters();
    return *this;
  }
};

}}

#endif




