from cereal import car
from selfdrive.config import Conversions as CV
from opendbc.can.can_define import CANDefine
from opendbc.can.parser import CANParser
from selfdrive.car.interfaces import CarStateBase
from selfdrive.car.mazda.values import DBC, LKAS_LIMITS, GEN1

class CarState(CarStateBase):
  def __init__(self, CP):
    super().__init__(CP)

    can_define = CANDefine(DBC[CP.carFingerprint]['pt'])
    self.shifter_values = can_define.dv["GEAR"]['GEAR']

    self.cruise_speed = 0
    self.acc_active_last = False
    self.low_speed_lockout = True
    self.low_speed_alert = False
    self.lkas_allowed = False

  def update(self, cp, cp_cam):

    ret = car.CarState.new_message()
    ret.wheelSpeeds.fl = cp.vl["WHEEL_SPEEDS"]['FL'] * CV.KPH_TO_MS
    ret.wheelSpeeds.fr = cp.vl["WHEEL_SPEEDS"]['FR'] * CV.KPH_TO_MS
    ret.wheelSpeeds.rl = cp.vl["WHEEL_SPEEDS"]['RL'] * CV.KPH_TO_MS
    ret.wheelSpeeds.rr = cp.vl["WHEEL_SPEEDS"]['RR'] * CV.KPH_TO_MS
    ret.vEgoRaw = (ret.wheelSpeeds.fl + ret.wheelSpeeds.fr + ret.wheelSpeeds.rl + ret.wheelSpeeds.rr) / 4.
    ret.vEgo, ret.aEgo = self.update_speed_kf(ret.vEgoRaw)

    # Match panda speed reading
    speed_kph = cp.vl["ENGINE_DATA"]['SPEED']
    ret.standstill = speed_kph < .1

    can_gear = int(cp.vl["GEAR"]['GEAR'])
    ret.gearShifter = self.parse_gear_shifter(self.shifter_values.get(can_gear, None))

    ret.leftBlinker = cp.vl["BLINK_INFO"]['LEFT_BLINK'] == 1
    ret.rightBlinker = cp.vl["BLINK_INFO"]['RIGHT_BLINK'] == 1

    ret.steeringAngleDeg = cp.vl["STEER"]['STEER_ANGLE']
    ret.steeringTorque = cp.vl["STEER_TORQUE"]['STEER_TORQUE_SENSOR']
    ret.steeringPressed = abs(ret.steeringTorque) > LKAS_LIMITS.STEER_THRESHOLD

    ret.steeringTorqueEps = cp.vl["STEER_TORQUE"]['STEER_TORQUE_MOTOR']
    ret.steeringRateDeg = cp.vl["STEER_RATE"]['STEER_ANGLE_RATE']

    ret.brakePressed = cp.vl["PEDALS"]['BRAKE_ON'] == 1
    ret.brake = cp.vl["BRAKE"]['BRAKE_PRESSURE']
    ret.brakeLights = ret.brakePressed

    ret.seatbeltUnlatched = cp.vl["SEATBELT"]['DRIVER_SEATBELT'] == 0
    ret.doorOpen = any([cp.vl["DOORS"]['FL'], cp.vl["DOORS"]['FR'],
                        cp.vl["DOORS"]['BL'], cp.vl["DOORS"]['BR']])

    ret.gas = cp.vl["ENGINE_DATA"]['PEDAL_GAS']
    ret.gasPressed = ret.gas > 0

    ret.leftBlindspot = cp.vl["BSM"]['LEFT_BS1'] == 1
    ret.rightBlindspot = cp.vl["BSM"]['RIGHT_BS1'] == 1

    # LKAS is enabled at 52kph going up and disabled at 45kph going down
    if speed_kph > LKAS_LIMITS.ENABLE_SPEED:
      self.lkas_allowed = True
    elif speed_kph < LKAS_LIMITS.DISABLE_SPEED:
      self.lkas_allowed = False

    # if any of the cruize buttons is pressed force state update
    if any([cp.vl["CRZ_BTNS"]['RES'],
                cp.vl["CRZ_BTNS"]['SET_P'],
                cp.vl["CRZ_BTNS"]['SET_M']]):
      self.cruise_speed = ret.vEgoRaw

    ret.cruiseState.available = True
    ret.cruiseState.enabled = cp.vl["CRZ_CTRL"]['CRZ_ACTIVE'] == 1
    ret.cruiseState.speed = self.cruise_speed

    if ret.cruiseState.enabled:
      if not self.lkas_allowed:
        if not self.acc_active_last:
          self.low_speed_lockout = True
        else:
          self.low_speed_alert = True
      else:
        self.low_speed_lockout = False
        self.low_speed_alert = False

    # On if no driver torque the last 5 seconds
    ret.steerWarning = cp.vl["STEER_RATE"]['HANDS_OFF_5_SECONDS'] == 1

    self.acc_active_last = ret.cruiseState.enabled

    self.cam_lkas = cp_cam.vl["CAM_LKAS"]
    ret.steerError = cp_cam.vl["CAM_LKAS"]['ERR_BIT_1'] == 1

    return ret

  @staticmethod
  def get_can_parser(CP):
    # this function generates lists for signal, messages and initial values
    signals = [
      # sig_name, sig_address, default
      ("LEFT_BLINK", "BLINK_INFO", 0),
      ("RIGHT_BLINK", "BLINK_INFO", 0),
      ("STEER_ANGLE", "STEER", 0),
      ("STEER_ANGLE_RATE", "STEER_RATE", 0),
      ("STEER_TORQUE_SENSOR", "STEER_TORQUE", 0),
      ("STEER_TORQUE_MOTOR", "STEER_TORQUE", 0),
      ("FL", "WHEEL_SPEEDS", 0),
      ("FR", "WHEEL_SPEEDS", 0),
      ("RL", "WHEEL_SPEEDS", 0),
      ("RR", "WHEEL_SPEEDS", 0),
    ]

    checks = [
      # sig_address, frequency
      ("BLINK_INFO", 10),
      ("STEER", 67),
      ("STEER_RATE", 83),
      ("STEER_TORQUE", 83),
      ("WHEEL_SPEEDS", 100),
    ]

    if CP.carFingerprint in GEN1:
      signals += [
        ("LKAS_BLOCK", "STEER_RATE", 0),
        ("LKAS_TRACK_STATE", "STEER_RATE", 0),
        ("HANDS_OFF_5_SECONDS", "STEER_RATE", 0),
        ("CRZ_ACTIVE", "CRZ_CTRL", 0),
        ("STANDSTILL", "PEDALS", 0),
        ("BRAKE_ON", "PEDALS", 0),
        ("BRAKE_PRESSURE", "BRAKE", 0),
        ("GEAR", "GEAR", 0),
        ("DRIVER_SEATBELT", "SEATBELT", 0),
        ("FL", "DOORS", 0),
        ("FR", "DOORS", 0),
        ("BL", "DOORS", 0),
        ("BR", "DOORS", 0),
        ("PEDAL_GAS", "ENGINE_DATA", 0),
        ("SPEED", "ENGINE_DATA", 0),
        ("RES", "CRZ_BTNS", 0),
        ("SET_P", "CRZ_BTNS", 0),
        ("SET_M", "CRZ_BTNS", 0),
        ("CTR", "CRZ_BTNS", 0),
        ("LEFT_BS1", "BSM", 0),
        ("RIGHT_BS1", "BSM", 0),
      ]

      checks += [
        ("ENGINE_DATA", 100),
        ("CRZ_CTRL", 50),
        ("CRZ_BTNS", 10),
        ("PEDALS", 50),
        ("BRAKE", 50),
        ("SEATBELT", 10),
        ("DOORS", 10),
        ("GEAR", 20),
        ("BSM", 10),
      ]

    return CANParser(DBC[CP.carFingerprint]['pt'], signals, checks, 0)

  @staticmethod
  def get_cam_can_parser(CP):
    signals = []
    checks = []

    if CP.carFingerprint in GEN1:
      signals += [
        # sig_name, sig_address, default
        ("LKAS_REQUEST",     "CAM_LKAS", 0),
        ("CTR",              "CAM_LKAS", 0),
        ("ERR_BIT_1",        "CAM_LKAS", 0),
        ("LINE_NOT_VISIBLE", "CAM_LKAS", 0),
        ("LDW",              "CAM_LKAS", 0),
        ("BIT_1",            "CAM_LKAS", 1),
        ("ERR_BIT_2",        "CAM_LKAS", 0),
        ("STEERING_ANGLE",   "CAM_LKAS", 0),
        ("ANGLE_ENABLED",    "CAM_LKAS", 0),
        ("CHKSUM",           "CAM_LKAS", 0),
      ]

      checks += [
        # sig_address, frequency
        ("CAM_LKAS",      16),
      ]

    return CANParser(DBC[CP.carFingerprint]['pt'], signals, checks, 2)
