import numpy as np

class histogram:

    def __init__(self, nbins, xlow, xhigh):

        self.nbins = nbins
        self.xlow = xlow
        self.xhigh = xhigh

        self.__bin_edges_and_centres()

        self.raw_histogram = []
        self.wgt_histogram = []
        self.bin_raw_contents = np.zeros(nbins)
        self.bin_wgt_contents = np.zeros(nbins)
        self.bin_errors = np.zeros(nbins)

    def __bin_edges_and_centres(self):

        self.bin_edges = np.linspace(self.xlow, self.xhigh, self.nbins+1)
        self.bin_centres = np.zeros(self.nbins)
        for bin in range(self.bin_edges.size - 1):
            self.bin_centres[bin] = self.bin_edges[bin] + (self.bin_edges[bin+1] - self.bin_edges[bin])/2.0  

    def getbincontent(self, _bin):

        return self.bin_wgt_contents[_bin]

    def getbinerror(self, bin):

        return np.sqrt(self.bin_errors[bin])    

    def integral(self, xlow=0, xhigh=0):

        if xlow != xhigh:
            integral = 0
            for bin in range(xlow, xhigh):
                integral += self.bin_wgt_contents[bin]
            return integral    
        else:
            return np.sum(self.bin_wgt_contents)

    def fill(self, value, weight):

        self.raw_histogram.append(value)
        self.wgt_histogram.append(value)

        bin_value = self.__find_bin(value)

        self.bin_raw_contents[bin_value] += 1
        self.bin_wgt_contents[bin_value] += weight
        self.bin_errors[bin_value] += weight**2

    def normalise(self):

        initial_integral = self.integral()
        final_integral = 0 
        for bin in range(self.nbins):

            self.bin_wgt_contents[bin] = self.bin_wgt_contents[bin]/initial_integral
            self.bin_errors[bin] = self.bin_errors[bin]/initial_integral 

    def __find_bin(self, value):

        for bin in range(self.nbins):

            if value >= self.bin_edges[bin] and value < self.bin_edges[bin+1]:

                return int(bin)

    def fill_gaus(self):

        for i in np.random.normal((self.xhigh-self.xlow)/2., 10, 1000):

            self.fill(i, 1)