"""
HX711 Load cell amplifier Python Library
Original source: https://gist.github.com/underdoeg/98a38b54f889fce2b237
Documentation source: https://github.com/aguegu/ardulibs/tree/master/hx711
Adapted by 2017 Jiri Dohnalek

This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.

This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
GNU General Public License for more details.

You should have received a copy of the GNU General Public License along
with this program; if not, write to the Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
"""


#import RPi.GPIO as GPIO
import gpiozero
import time
import sys


class HX711:

    def __init__(self, dout, pd_sck, gain=128):
        """
        Set GPIO Mode, and pin for communication with HX711
        :param dout: Serial Data Output pin
        :param pd_sck: Power Down and Serial Clock Input pin
        :param gain: set gain 128, 64, 32
        """
        self.GAIN = 0
        self.OFFSET = 0
        self.SCALE = 1

        # Setup the gpio pin numbering system
        # GPIO.setmode(GPIO.BCM)

        # Set the pin numbers
        self.PD_SCK = gpiozero.OutputDevice(pd_sck)

        self.DOUT = gpiozero.DigitalInputDevice(dout, pull_up=False)

        # Setup the GPIO Pin as output
        #GPIO.setup(self.PD_SCK, GPIO.OUT)
        

        # Setup the GPIO Pin as input
        #GPIO.setup(self.DOUT, GPIO.IN)

        # Power up the chip
        self.power_up()
        self.set_gain(gain)

    def set_gain(self, gain=128):

        try:
            if gain == 128:
                self.GAIN = 3
            elif gain == 64:
                self.GAIN = 2
            elif gain == 32:
                self.GAIN = 1
        except:
            self.GAIN = 3  # Sets default GAIN at 128

        # GPIO.output(self.PD_SCK, False)
        self.PD_SCK.off()
        self.read()

    def set_scale(self, scale):
        """
        Set scale
        :param scale, scale
        """
        self.SCALE = scale

    def set_offset(self, offset):
        """
        Set the offset
        :param offset: offset
        """
        self.OFFSET = offset

    def get_scale(self):
        """
        Returns value of scale
        """
        return self.SCALE

    def get_offset(self):
        """
        Returns value of offset
        """
        return self.OFFSET

    def read(self):
        """
        Read data from the HX711 chip
        :param void
        :return reading from the HX711
        """

        # Control if the chip is ready
        # while not (GPIO.input(self.DOUT) == 0):
        while not (self.DOUT.is_active == 0):
            # Uncommenting the print below results in noisy output
            # print("No input from HX711.")
            pass

        # Original C source code ported to Python as described in datasheet
        # https://cdn.sparkfun.com/datasheets/Sensors/ForceFlex/hx711_english.pdf
        # Output from python matched the output of
        # different HX711 Arduino library example
        # Lastly, behaviour matches while applying pressure
        # Please see page 8 of the PDF document

        count = 0

        for i in range(24):
            # GPIO.output(self.PD_SCK, True)
            self.PD_SCK.on()
            count = count << 1
            # GPIO.output(self.PD_SCK, False)
            self.PD_SCK.off()
            if(self.DOUT.is_active == 1):
                count += 1

        # GPIO.output(self.PD_SCK, True)
        self.PD_SCK.on()
        count = count ^ 0x800000
        # GPIO.output(self.PD_SCK, False)
        self.PD_SCK.off()

        # set channel and gain factor for next reading
        for i in range(self.GAIN):
            #GPIO.output(self.PD_SCK, True)
            #GPIO.output(self.PD_SCK, False)
            self.PD_SCK.on()
            self.PD_SCK.off()

        return count

    def read_average(self, times=16):
        """
        Calculate average value from
        :param times: measure x amount of time to get average
        """
        sum = 0
        for i in range(times):
            sum += self.read()
        return sum / times

    def get_grams(self, times=16):
        """
        :param times: Set value to calculate average, 
        be aware that high number of times will have a 
        slower runtime speed.        
        :return float weight in grams
        """
        value = (self.read_average(times) - self.OFFSET)
        grams = (value / self.SCALE)
        return grams

    def tare(self, times=16):
        """
        Tare functionality fpr calibration
        :param times: set value to calculate average
        """
        sum = self.read_average(times)
        self.set_offset(sum)

    def power_down(self):
        """
        Power the chip down
        """
        # GPIO.output(self.PD_SCK, False)
        # GPIO.output(self.PD_SCK, True)
        self.PD_SCK.off()
        self.PD_SCK.on()

    def power_up(self):
        """
        Power the chip up
        """
        # GPIO.output(self.PD_SCK, False)
        self.PD_SCK.off()
