class RCCarController():
    def __init__(self,
                 joystick_port=0,
                 serial_port='/dev/ttyACM0',
                 publish_rate=10.0):
        self.joystick = Joystick(joystick_port)
        self.serial_port = serial_port
        self.publish_rate = publish_rate
        self.xbee = Packet()
        self.arm_car = False
        self.last_data = None
        self.arm_display_count = 0
        self.safety_check_done = False

    def setup(self):
        self.xbee.open(self.serial_port)

    def trigger_check(self):
        safe = False
        while not safe:
            data = self.joystick.parse_logitech()
            if data['left_trigger'] == -1.0 and data['right_trigger'] == -1.0:
                safe = True
            else:
                print(
                    '\nPlease press the left and right trigger (LT/RT) and release.'
                )
                print('LT: %.1f, RT: %.1f (Both must be -1.0)' %
                      (data['left_trigger'], data['right_trigger']))
                input('>>> Press [ENTER] to continue.\n')
        self.safety_check_done = True

    def set_throttle_trigger(self, data):
        forward = mapfloat(data['right_trigger'], -1.0, 1.0, 0, 255)
        backward = mapfloat(data['left_trigger'], -1.0, 1.0, 0, 255)
        magnitude = constrain(int(max(forward, backward)), 0, 255)
        direction = 0 if forward >= backward else 1

        self.xbee.tx_throttle = magnitude
        self.xbee.tx_direction = direction

    def set_throttle_axis(self, data):
        magnitude = mapfloat(data['right_stick'][1], -1.0, 1.0, -255, 255)
        direction = 0 if magnitude > 0 else 1
        magnitude = constrain(abs(int(magnitude)), 0, 255)

        self.xbee.tx_throttle = magnitude
        self.xbee.tx_direction = direction

    def set_steering(self, data):
        steering = mapfloat(data['left_stick'][0], -1.0, 1.0, 0, 255)
        steering = constrain(int(steering), 0, 255)
        self.xbee.tx_steering = steering

    def set_arm(self, data):
        if data['face_buttons'][
                'X'] and not self.last_data['face_buttons']['X']:
            if not self.arm_car: self.arm_car = True
            else: self.arm_car = False
        self.xbee.reset_packet()
        self.xbee.tx_arm = self.arm_car

    def run(self):
        # Parse joystick data.
        data = self.joystick.parse_logitech()

        # Safety checks.
        if not self.safety_check_done:
            self.trigger_check()
        if self.last_data is None:
            self.last_data = data
            return

        # Set data and send serial packet.
        self.set_arm(data)
        if self.arm_car:
            self.set_steering(data)
            # self.set_throttle_axis(data)
            self.set_throttle_trigger(data)
        self.xbee.send()

        self.display()
        self.last_data = data

    def loop(self):
        while True:
            try:
                time.sleep(1 / self.publish_rate)
                self.run()
            except KeyboardInterrupt:
                print('\n\n>>> USER STOPPED')
                break
            except Exception as error:
                print('\n\n>>> ERROR OCCURRED:', error)
                traceback.print_tb(error.__traceback__)
                break

        self.xbee.close()

    def display(self):
        # Arm text.
        if self.xbee.tx_arm:
            arm_text = '   ' if self.arm_display_count > 0.6 * self.publish_rate \
                             else '\033[1m\033[91mARM\033[0m'
        else:
            arm_text = 'OFF'
        self.arm_display_count = (self.arm_display_count +
                                  1) % self.publish_rate

        # Steering text.
        if self.xbee.tx_steering == 127: str_text = '127'
        else: str_text = '\033[1m\033[93m%03d\033[0m' % self.xbee.tx_steering

        # Throttle text.
        if self.xbee.tx_throttle == 0: thr_text = 'NET 000'
        elif not self.xbee.tx_direction:
            thr_text = '\033[1m\033[92mFWD %03d\033[0m' % self.xbee.tx_throttle
        elif self.xbee.tx_direction:
            thr_text = '\033[1m\033[94mREV %03d\033[0m' % self.xbee.tx_throttle

        # Display all text.
        text = 'ARM: %s | THR: %s | STR: %s'
        text = text % (arm_text, thr_text, str_text)
        sys.stdout.write('\r' + text + ' ' * 5)
        sys.stdout.flush()