// Safety-relevant CAN messages for Ford vehicles.
#define FORD_EngBrakeData          0x165   // RX from PCM, for driver brake pedal and cruise state
#define FORD_EngVehicleSpThrottle  0x204   // RX from PCM, for driver throttle input
#define FORD_DesiredTorqBrk        0x213   // RX from ABS, for standstill state
#define FORD_BrakeSysFeatures      0x415   // RX from ABS, for vehicle speed
#define FORD_EngVehicleSpThrottle2 0x202   // RX from PCM, for second vehicle speed
#define FORD_Yaw_Data_FD1          0x91    // RX from RCM, for yaw rate
#define FORD_Steering_Data_FD1     0x083   // TX by OP, various driver switches and LKAS/CC buttons
#define FORD_ACCDATA               0x186   // TX by OP, ACC controls
#define FORD_ACCDATA_3             0x18A   // TX by OP, ACC/TJA user interface
#define FORD_Lane_Assist_Data1     0x3CA   // TX by OP, Lane Keep Assist
#define FORD_LateralMotionControl  0x3D3   // TX by OP, Lateral Control message
#define FORD_LateralMotionControl2 0x3D6   // TX by OP, alternate Lateral Control message
#define FORD_IPMA_Data             0x3D8   // TX by OP, IPMA and LKAS user interface

// CAN bus numbers.
#define FORD_MAIN_BUS 0U
#define FORD_CAM_BUS  2U

const CanMsg FORD_STOCK_TX_MSGS[] = {
  {FORD_Steering_Data_FD1, 0, 8},
  {FORD_Steering_Data_FD1, 2, 8},
  {FORD_ACCDATA_3, 0, 8},
  {FORD_Lane_Assist_Data1, 0, 8},
  {FORD_LateralMotionControl, 0, 8},
  {FORD_IPMA_Data, 0, 8},
};

const CanMsg FORD_LONG_TX_MSGS[] = {
  {FORD_Steering_Data_FD1, 0, 8},
  {FORD_Steering_Data_FD1, 2, 8},
  {FORD_ACCDATA, 0, 8},
  {FORD_ACCDATA_3, 0, 8},
  {FORD_Lane_Assist_Data1, 0, 8},
  {FORD_LateralMotionControl, 0, 8},
  {FORD_IPMA_Data, 0, 8},
};

const CanMsg FORD_CANFD_STOCK_TX_MSGS[] = {
  {FORD_Steering_Data_FD1, 0, 8},
  {FORD_Steering_Data_FD1, 2, 8},
  {FORD_ACCDATA_3, 0, 8},
  {FORD_Lane_Assist_Data1, 0, 8},
  {FORD_LateralMotionControl2, 0, 8},
  {FORD_IPMA_Data, 0, 8},
};

const CanMsg FORD_CANFD_LONG_TX_MSGS[] = {
  {FORD_Steering_Data_FD1, 0, 8},
  {FORD_Steering_Data_FD1, 2, 8},
  {FORD_ACCDATA, 0, 8},
  {FORD_ACCDATA_3, 0, 8},
  {FORD_Lane_Assist_Data1, 0, 8},
  {FORD_LateralMotionControl2, 0, 8},
  {FORD_IPMA_Data, 0, 8},
};

// warning: quality flags are not yet checked in openpilot's CAN parser,
// this may be the cause of blocked messages
RxCheck ford_rx_checks[] = {
  {.msg = {{FORD_BrakeSysFeatures, 0, 8, .check_checksum = true, .max_counter = 15U, .quality_flag=true, .frequency = 50U}, { 0 }, { 0 }}},
  // TODO: FORD_EngVehicleSpThrottle2 has a counter that skips by 2, understand and enable counter check
  {.msg = {{FORD_EngVehicleSpThrottle2, 0, 8, .check_checksum = true, .quality_flag=true, .frequency = 50U}, { 0 }, { 0 }}},
  {.msg = {{FORD_Yaw_Data_FD1, 0, 8, .check_checksum = true, .max_counter = 255U, .quality_flag=true, .frequency = 100U}, { 0 }, { 0 }}},
  // These messages have no counter or checksum
  {.msg = {{FORD_EngBrakeData, 0, 8, .frequency = 10U}, { 0 }, { 0 }}},
  {.msg = {{FORD_EngVehicleSpThrottle, 0, 8, .frequency = 100U}, { 0 }, { 0 }}},
  {.msg = {{FORD_DesiredTorqBrk, 0, 8, .frequency = 50U}, { 0 }, { 0 }}},
};

static uint8_t ford_get_counter(CANPacket_t *to_push) {
  int addr = GET_ADDR(to_push);

  uint8_t cnt = 0;
  if (addr == FORD_BrakeSysFeatures) {
    // Signal: VehVActlBrk_No_Cnt
    cnt = (GET_BYTE(to_push, 2) >> 2) & 0xFU;
  } else if (addr == FORD_Yaw_Data_FD1) {
    // Signal: VehRollYaw_No_Cnt
    cnt = GET_BYTE(to_push, 5);
  } else {
  }
  return cnt;
}

static uint32_t ford_get_checksum(CANPacket_t *to_push) {
  int addr = GET_ADDR(to_push);

  uint8_t chksum = 0;
  if (addr == FORD_BrakeSysFeatures) {
    // Signal: VehVActlBrk_No_Cs
    chksum = GET_BYTE(to_push, 3);
  } else if (addr == FORD_EngVehicleSpThrottle2) {
    // Signal: VehVActlEng_No_Cs
    chksum = GET_BYTE(to_push, 1);
  } else if (addr == FORD_Yaw_Data_FD1) {
    // Signal: VehRollYawW_No_Cs
    chksum = GET_BYTE(to_push, 4);
  } else {
  }
  return chksum;
}

static uint32_t ford_compute_checksum(CANPacket_t *to_push) {
  int addr = GET_ADDR(to_push);

  uint8_t chksum = 0;
  if (addr == FORD_BrakeSysFeatures) {
    chksum += GET_BYTE(to_push, 0) + GET_BYTE(to_push, 1);  // Veh_V_ActlBrk
    chksum += GET_BYTE(to_push, 2) >> 6;                    // VehVActlBrk_D_Qf
    chksum += (GET_BYTE(to_push, 2) >> 2) & 0xFU;           // VehVActlBrk_No_Cnt
    chksum = 0xFFU - chksum;
  } else if (addr == FORD_EngVehicleSpThrottle2) {
    chksum += (GET_BYTE(to_push, 2) >> 3) & 0xFU;           // VehVActlEng_No_Cnt
    chksum += (GET_BYTE(to_push, 4) >> 5) & 0x3U;           // VehVActlEng_D_Qf
    chksum += GET_BYTE(to_push, 6) + GET_BYTE(to_push, 7);  // Veh_V_ActlEng
    chksum = 0xFFU - chksum;
  } else if (addr == FORD_Yaw_Data_FD1) {
    chksum += GET_BYTE(to_push, 0) + GET_BYTE(to_push, 1);  // VehRol_W_Actl
    chksum += GET_BYTE(to_push, 2) + GET_BYTE(to_push, 3);  // VehYaw_W_Actl
    chksum += GET_BYTE(to_push, 5);                         // VehRollYaw_No_Cnt
    chksum += GET_BYTE(to_push, 6) >> 6;                    // VehRolWActl_D_Qf
    chksum += (GET_BYTE(to_push, 6) >> 4) & 0x3U;           // VehYawWActl_D_Qf
    chksum = 0xFFU - chksum;
  } else {
  }

  return chksum;
}

static bool ford_get_quality_flag_valid(CANPacket_t *to_push) {
  int addr = GET_ADDR(to_push);

  bool valid = false;
  if (addr == FORD_BrakeSysFeatures) {
    valid = (GET_BYTE(to_push, 2) >> 6) == 0x3U;           // VehVActlBrk_D_Qf
  } else if (addr == FORD_EngVehicleSpThrottle2) {
    valid = ((GET_BYTE(to_push, 4) >> 5) & 0x3U) == 0x3U;  // VehVActlEng_D_Qf
  } else if (addr == FORD_Yaw_Data_FD1) {
    valid = ((GET_BYTE(to_push, 6) >> 4) & 0x3U) == 0x3U;  // VehYawWActl_D_Qf
  } else {
  }
  return valid;
}

const uint16_t FORD_PARAM_LONGITUDINAL = 1;
const uint16_t FORD_PARAM_CANFD = 2;

bool ford_longitudinal = false;
bool ford_canfd = false;

const LongitudinalLimits FORD_LONG_LIMITS = {
  // acceleration cmd limits (used for brakes)
  // Signal: AccBrkTot_A_Rq
  .max_accel = 5641,       //  1.9999 m/s^s
  .min_accel = 4231,       // -3.4991 m/s^2
  .inactive_accel = 5128,  // -0.0008 m/s^2

  // gas cmd limits
  // Signal: AccPrpl_A_Rq & AccPrpl_A_Pred
  .max_gas = 700,          //  2.0 m/s^2
  .min_gas = 450,          // -0.5 m/s^2
  .inactive_gas = 0,       // -5.0 m/s^2
};

#define FORD_INACTIVE_CURVATURE 1000U
#define FORD_INACTIVE_CURVATURE_RATE 4096U
#define FORD_INACTIVE_PATH_OFFSET 512U
#define FORD_INACTIVE_PATH_ANGLE 1000U

#define FORD_CANFD_INACTIVE_CURVATURE_RATE 1024U

#define FORD_MAX_SPEED_DELTA 2.0  // m/s

static bool ford_lkas_msg_check(int addr) {
  return (addr == FORD_ACCDATA_3)
      || (addr == FORD_Lane_Assist_Data1)
      || (addr == FORD_LateralMotionControl)
      || (addr == FORD_LateralMotionControl2)
      || (addr == FORD_IPMA_Data);
}

// Curvature rate limits
const SteeringLimits FORD_STEERING_LIMITS = {
  .max_steer = 1000,
  .angle_deg_to_can = 50000,        // 1 / (2e-5) rad to can
  .max_angle_error = 100,           // 0.002 * FORD_STEERING_LIMITS.angle_deg_to_can
  .angle_rate_up_lookup = {
    {5., 25., 25.},
    {0.0002, 0.0001, 0.0001}
  },
  .angle_rate_down_lookup = {
    {5., 25., 25.},
    {0.000225, 0.00015, 0.00015}
  },

  // no blending at low speed due to lack of torque wind-up and inaccurate current curvature
  .angle_error_min_speed = 10.0,    // m/s

  .enforce_angle_error = true,
  .inactive_angle_is_zero = true,
};

static void ford_rx_hook(CANPacket_t *to_push) {
  if (GET_BUS(to_push) == FORD_MAIN_BUS) {
    int addr = GET_ADDR(to_push);

    // Update in motion state from standstill signal
    if (addr == FORD_DesiredTorqBrk) {
      // Signal: VehStop_D_Stat
      vehicle_moving = ((GET_BYTE(to_push, 3) >> 3) & 0x3U) != 1U;
    }

    // Update vehicle speed
    if (addr == FORD_BrakeSysFeatures) {
      // Signal: Veh_V_ActlBrk
      UPDATE_VEHICLE_SPEED(((GET_BYTE(to_push, 0) << 8) | GET_BYTE(to_push, 1)) * 0.01 / 3.6);
    }

    // Check vehicle speed against a second source
    if (addr == FORD_EngVehicleSpThrottle2) {
      // Disable controls if speeds from ABS and PCM ECUs are too far apart.
      // Signal: Veh_V_ActlEng
      float filtered_pcm_speed = ((GET_BYTE(to_push, 6) << 8) | GET_BYTE(to_push, 7)) * 0.01 / 3.6;
      if (ABS(filtered_pcm_speed - ((float)vehicle_speed.values[0] / VEHICLE_SPEED_FACTOR)) > FORD_MAX_SPEED_DELTA) {
        controls_allowed = false;
      }
    }

    // Update vehicle yaw rate
    if (addr == FORD_Yaw_Data_FD1) {
      // Signal: VehYaw_W_Actl
      float ford_yaw_rate = (((GET_BYTE(to_push, 2) << 8U) | GET_BYTE(to_push, 3)) * 0.0002) - 6.5;
      float current_curvature = ford_yaw_rate / MAX(vehicle_speed.values[0] / VEHICLE_SPEED_FACTOR, 0.1);
      // convert current curvature into units on CAN for comparison with desired curvature
      update_sample(&angle_meas, ROUND(current_curvature * FORD_STEERING_LIMITS.angle_deg_to_can));
    }

    // Update gas pedal
    if (addr == FORD_EngVehicleSpThrottle) {
      // Pedal position: (0.1 * val) in percent
      // Signal: ApedPos_Pc_ActlArb
      gas_pressed = (((GET_BYTE(to_push, 0) & 0x03U) << 8) | GET_BYTE(to_push, 1)) > 0U;
    }

    // Update brake pedal and cruise state
    if (addr == FORD_EngBrakeData) {
      // Signal: BpedDrvAppl_D_Actl
      brake_pressed = ((GET_BYTE(to_push, 0) >> 4) & 0x3U) == 2U;

      // Signal: CcStat_D_Actl
      unsigned int cruise_state = GET_BYTE(to_push, 1) & 0x07U;
      acc_main_on = (cruise_state == 3U) ||(cruise_state == 4U) || (cruise_state == 5U);
      bool cruise_engaged = (cruise_state == 4U) || (cruise_state == 5U);
      pcm_cruise_check(cruise_engaged);
    }

    // If steering controls messages are received on the destination bus, it's an indication
    // that the relay might be malfunctioning.
    bool stock_ecu_detected = ford_lkas_msg_check(addr);
    if (ford_longitudinal) {
      stock_ecu_detected = stock_ecu_detected || (addr == FORD_ACCDATA);
    }
    generic_rx_checks(stock_ecu_detected);
  }

}

static bool ford_tx_hook(CANPacket_t *to_send) {
  bool tx = true;

  int addr = GET_ADDR(to_send);

  // Safety check for ACCDATA accel and brake requests
  if (addr == FORD_ACCDATA) {
    // Signal: AccPrpl_A_Rq
    int gas = ((GET_BYTE(to_send, 6) & 0x3U) << 8) | GET_BYTE(to_send, 7);
    // Signal: AccPrpl_A_Pred
    int gas_pred = ((GET_BYTE(to_send, 2) & 0x3U) << 8) | GET_BYTE(to_send, 3);
    // Signal: AccBrkTot_A_Rq
    int accel = ((GET_BYTE(to_send, 0) & 0x1FU) << 8) | GET_BYTE(to_send, 1);
    // Signal: CmbbDeny_B_Actl
    int cmbb_deny = GET_BIT(to_send, 37U);

    bool violation = false;
    violation |= longitudinal_accel_checks(accel, FORD_LONG_LIMITS);
    violation |= longitudinal_gas_checks(gas, FORD_LONG_LIMITS);
    violation |= longitudinal_gas_checks(gas_pred, FORD_LONG_LIMITS);

    // Safety check for stock AEB
    violation |= cmbb_deny != 0; // do not prevent stock AEB actuation

    if (violation) {
      tx = false;
    }
  }

  // Safety check for Steering_Data_FD1 button signals
  // Note: Many other signals in this message are not relevant to safety (e.g. blinkers, wiper switches, high beam)
  // which we passthru in OP.
  if (addr == FORD_Steering_Data_FD1) {
    // Violation if resume button is pressed while controls not allowed, or
    // if cancel button is pressed when cruise isn't engaged.
    bool violation = false;
    violation |= (GET_BIT(to_send, 8U) == 1U) && !cruise_engaged_prev;   // Signal: CcAslButtnCnclPress (cancel)
    violation |= (GET_BIT(to_send, 25U) == 1U) && !controls_allowed;     // Signal: CcAsllButtnResPress (resume)

    if (violation) {
      tx = false;
    }
  }

  // Safety check for Lane_Assist_Data1 action
  if (addr == FORD_Lane_Assist_Data1) {
    // Do not allow steering using Lane_Assist_Data1 (Lane-Departure Aid).
    // This message must be sent for Lane Centering to work, and can include
    // values such as the steering angle or lane curvature for debugging,
    // but the action (LkaActvStats_D2_Req) must be set to zero.
    unsigned int action = GET_BYTE(to_send, 0) >> 5;
    if (action != 0U) {
      tx = false;
    }
  }

  // Safety check for LateralMotionControl action
  if (addr == FORD_LateralMotionControl) {
    // Signal: LatCtl_D_Rq
    bool steer_control_enabled = ((GET_BYTE(to_send, 4) >> 2) & 0x7U) != 0U;
    unsigned int raw_curvature = (GET_BYTE(to_send, 0) << 3) | (GET_BYTE(to_send, 1) >> 5);
    unsigned int raw_curvature_rate = ((GET_BYTE(to_send, 1) & 0x1FU) << 8) | GET_BYTE(to_send, 2);
    unsigned int raw_path_angle = (GET_BYTE(to_send, 3) << 3) | (GET_BYTE(to_send, 4) >> 5);
    unsigned int raw_path_offset = (GET_BYTE(to_send, 5) << 2) | (GET_BYTE(to_send, 6) >> 6);

    // These signals are not yet tested with the current safety limits
    bool violation = (raw_curvature_rate != FORD_INACTIVE_CURVATURE_RATE) || (raw_path_angle != FORD_INACTIVE_PATH_ANGLE) || (raw_path_offset != FORD_INACTIVE_PATH_OFFSET);

    // Check angle error and steer_control_enabled
    int desired_curvature = raw_curvature - FORD_INACTIVE_CURVATURE;  // /FORD_STEERING_LIMITS.angle_deg_to_can to get real curvature
    violation |= steer_angle_cmd_checks(desired_curvature, steer_control_enabled, FORD_STEERING_LIMITS);

    if (violation) {
      tx = false;
    }
  }

  // Safety check for LateralMotionControl2 action
  if (addr == FORD_LateralMotionControl2) {
    // Signal: LatCtl_D2_Rq
    bool steer_control_enabled = ((GET_BYTE(to_send, 0) >> 4) & 0x7U) != 0U;
    unsigned int raw_curvature = (GET_BYTE(to_send, 2) << 3) | (GET_BYTE(to_send, 3) >> 5);
    unsigned int raw_curvature_rate = (GET_BYTE(to_send, 6) << 3) | (GET_BYTE(to_send, 7) >> 5);
    unsigned int raw_path_angle = ((GET_BYTE(to_send, 3) & 0x1FU) << 6) | (GET_BYTE(to_send, 4) >> 2);
    unsigned int raw_path_offset = ((GET_BYTE(to_send, 4) & 0x3U) << 8) | GET_BYTE(to_send, 5);

    // These signals are not yet tested with the current safety limits
    bool violation = (raw_curvature_rate != FORD_CANFD_INACTIVE_CURVATURE_RATE) || (raw_path_angle != FORD_INACTIVE_PATH_ANGLE) || (raw_path_offset != FORD_INACTIVE_PATH_OFFSET);

    // Check angle error and steer_control_enabled
    int desired_curvature = raw_curvature - FORD_INACTIVE_CURVATURE;  // /FORD_STEERING_LIMITS.angle_deg_to_can to get real curvature
    violation |= steer_angle_cmd_checks(desired_curvature, steer_control_enabled, FORD_STEERING_LIMITS);

    if (violation) {
      tx = false;
    }
  }

  return tx;
}

static int ford_fwd_hook(int bus_num, int addr) {
  int bus_fwd = -1;

  switch (bus_num) {
    case FORD_MAIN_BUS: {
      // Forward all traffic from bus 0 onward
      bus_fwd = FORD_CAM_BUS;
      break;
    }
    case FORD_CAM_BUS: {
      if (ford_lkas_msg_check(addr)) {
        // Block stock LKAS and UI messages
        bus_fwd = -1;
      } else if (ford_longitudinal && (addr == FORD_ACCDATA)) {
        // Block stock ACC message
        bus_fwd = -1;
      } else {
        // Forward remaining traffic
        bus_fwd = FORD_MAIN_BUS;
      }
      break;
    }
    default: {
      // No other buses should be in use; fallback to do-not-forward
      bus_fwd = -1;
      break;
    }
  }

  return bus_fwd;
}

static safety_config ford_init(uint16_t param) {
  UNUSED(param);
#ifdef ALLOW_DEBUG
  ford_longitudinal = GET_FLAG(param, FORD_PARAM_LONGITUDINAL);
  ford_canfd = GET_FLAG(param, FORD_PARAM_CANFD);
#endif

  safety_config ret;
  if (ford_canfd) {
    ret = ford_longitudinal ? BUILD_SAFETY_CFG(ford_rx_checks, FORD_CANFD_LONG_TX_MSGS) : \
                              BUILD_SAFETY_CFG(ford_rx_checks, FORD_CANFD_STOCK_TX_MSGS);
  } else {
    ret = ford_longitudinal ? BUILD_SAFETY_CFG(ford_rx_checks, FORD_LONG_TX_MSGS) : \
                              BUILD_SAFETY_CFG(ford_rx_checks, FORD_STOCK_TX_MSGS);
  }
  return ret;
}

const safety_hooks ford_hooks = {
  .init = ford_init,
  .rx = ford_rx_hook,
  .tx = ford_tx_hook,
  .fwd = ford_fwd_hook,
  .get_counter = ford_get_counter,
  .get_checksum = ford_get_checksum,
  .compute_checksum = ford_compute_checksum,
  .get_quality_flag_valid = ford_get_quality_flag_valid,
};
