import cv2

# Configuration and tunables (extracted from original script)
# =========================

# ВАЖНО: укажите путь к новой YOLO11s-P2-CBAM модели (best.pt, imgsz=1280)
MODEL_PATH = r"D:\PycharmProjects\YOLOTrain\BYTETRACK\TEST\fpv_tracker_optimized\best.pt"
DEVICE = 0
USE_HALF = True

# Универсальный источник:
# Видео:
# SOURCE = r"C:\Users\Legion\Desktop\dataset01.26\24-01_2.mp4"
# Камера / capture card:
#SOURCE = 0
# SOURCE = 1
# RTSP:
# SOURCE = "rtsp://192.168.1.10:8554/live"
# UDP:
# SOURCE = "udp://@0.0.0.0:5600"
SOURCE = r"C:\Users\Legion\Desktop\Видео для МАИ ФУЛЛ\Группа 1\23-01_10один.mp4"

VIDEO_REALTIME = True

# Windows:
CAP_BACKEND = cv2.CAP_DSHOW  # или cv2.CAP_MSMF
# Linux обычно:
# CAP_BACKEND = cv2.CAP_V4L2

SHOW_OUTPUT = True
WINDOW_NAME = "FPV LOCK PAL + ByteTrack"
TARGET_OUT_FPS = 0   # <=0: file -> source fps, camera/stream -> no forced pacing

# Frame timebase / dt stabilization
TIMESTAMP_USE_SOURCE_CLOCK = True
INPUT_FPS_FALLBACK = 25.0
DT_MIN_SEC = 1.0 / 120.0
DT_MAX_SEC = 0.20
DT_RESET_GAP_SEC = 0.40

# Effective resolution policy (for analog PAL)
EFFECTIVE_W = 720
EFFECTIVE_H = 576
FORCE_EFFECTIVE_PAL = True

# YOLO inference settings
CONF = 0.25
IMG_SIZE_ROI = 640
IMG_SIZE_FULL = 1280
MAX_DET = 60
TARGET_CLASS_ID = 0

# Filters (small drones far away)
MIN_BOX_AREA = 8
MAX_BOX_AREA = 0
ASPECT_RANGE = (0.15, 6.0)

# FSM
CONFIRM_HITS = 2
MAX_MISSES = 20
RECOVER_FULLSCAN_EVERY = 30
RECOVER_FORCED_DET_EVERY = 2

# ROI sizing (in effective pixels)
BASE_RADIUS = 150
MAX_RADIUS = 520
SPEED_RADIUS_FACTOR = 18.0
FORWARD_BIAS = 1.8
SIDE_BIAS = 1.2

# KLT optical flow tracker (effective)
KLT_MAX_CORNERS = 140
KLT_QUALITY = 0.01
KLT_MIN_DIST = 6
KLT_WIN = (21, 21)
KLT_MAX_LEVEL = 3
KLT_OUTLIER_THRESH = 20.0

# KLT validity gates (anti-drift)
KLT_OK_Q = 0.22
KLT_OK_PTS = 14
KLT_REINIT_MIN_POINTS = 18
KLT_REFRESH_WITH_YOLO = True
KLT_REFRESH_EVERY = 6
KLT_REFRESH_MIN_SCORE = 0.18
KLT_REFRESH_MIN_IOU = 0.25
KLT_REFRESH_MIN_QUALITY = 0.35

# YOLO safety
YOLO_NO_DET_LIMIT = 20

# Robust single-target logic
GAP_MAX_SECONDS = 1.2
ACQUIRE_CONFIRM_SCORE = 6
ACQUIRE_HIT_BONUS = 2
ACQUIRE_MISS_PENALTY = 1
ACQUIRE_MAX_MISS = 90
LOCK_MAX_MISS_FRAMES = 120
FULLSCAN_WHEN_MISS_GE = 16

# Template matching fallback
TM_ENABLE = True
TM_TEMPLATE_SIZE = 40
TM_SEARCH_SCALE_BASE = 2.5
TM_SEARCH_SCALE_PER_MISS = 0.20
TM_MIN_SCORE = 0.55

# Kalman safety limits
KALMAN_CLAMP_V_PX_S = 180.0
KALMAN_RESET_IF_OUT_OF_FRAME = True
YOLO_FRESH_SEC = 0.50
YOLO_FORCE_DET_WHEN_WEAK = True
KALMAN_Q_DT_BOOST_ENABLE = True
KALMAN_Q_DT_BOOST_START_SEC = 0.08
KALMAN_Q_DT_BOOST_SLOPE = 16.0
KALMAN_Q_DT_BOOST_MAX = 4.0

# Appearance gate
USE_HSV_GATE = False
HSV_HIST_BINS = (16, 16)
HSV_GATE_MIN_SIM = 0.06
HSV_UPDATE_EVERY = 8

# Async YOLO worker
YOLO_QUEUE_MAX = 1

# Debug / draw
DEBUG = True
DRAW_ALL_BOXES = True
DRAW_LOCK_BOX = True
DRAW_KALMAN = True
DRAW_KLT_POINTS = False
DRAW_BT_TRACKS = True
BT_DRAW_ONLY_CONFIRMED = False

# Output recording
SAVE_INFER_VIDEO = True
OUT_VIDEO_PATH = "out_infer.mp4"
TRACK_LOG_ENABLE = True
TRACK_LOG_PATH = "track_log.csv"
TRACK_SUMMARY_PATH = "track_summary.json"
TRACK_LOG_FLUSH_EVERY = 30

# Screen-space lock/guidance layer for simulator integration
GUIDANCE_ENABLE = True
DRAW_GUIDANCE = True
GUIDANCE_EXPORT_ENABLE = True
GUIDANCE_EXPORT_PATH = "guidance_state.json"
GUIDANCE_EXPORT_EVERY = 1
GUIDANCE_LEAD_SEC = 0.12
GUIDANCE_MAX_LEAD_PX = 90.0
GUIDANCE_BOX_BIAS_Y = 0.0
GUIDANCE_DEADZONE_NORM = 0.04
GUIDANCE_EXPO = 1.35
GUIDANCE_GAIN_X = 1.0
GUIDANCE_GAIN_Y = 1.0
GUIDANCE_CMD_SMOOTH = 0.70
GUIDANCE_POINT_SMOOTH = 0.55
GUIDANCE_CONF_MIN = 0.35
GUIDANCE_ON_TARGET_NORM = 0.12
GUIDANCE_LOOK_MAX_STEP_PX = 28.0
GUIDANCE_OVERRIDE_ENABLE = True
GUIDANCE_OVERRIDE_MISS_GE = 2
GUIDANCE_OVERRIDE_TTL = 2

# Trajectory drawing
DRAW_TRAJ = False
TRAIL_SECONDS = 1.2
TRAIL_ALPHA = 0.25
TRAIL_DRAW_EVERY_N = 2
TRAIL_MIN_STEP_PX = 3
CLEAR_TRAJ_ON_RECOVER = True
DRAW_TRAJ_ONLY_WHEN_LOCKED = True

DRAW_KALMAN_WHEN_MISS_LE = 8

# Camera motion compensation
USE_CAM_MOTION_COMP = True
CAM_MOTION_MAX_CORNERS = 500
CAM_MOTION_QL = 0.01
CAM_MOTION_MIN_DIST = 12
CAM_MOTION_RANSAC_THR = 3.0
CAM_MOTION_MAX_SHIFT_PX = 55.0
CAM_MOTION_SCALE_MIN = 0.90
CAM_MOTION_SCALE_MAX = 1.10
CAM_MOTION_MAX_ROT_DEG = 12.0
CAM_MOTION_EXCLUDE_NORM = [
    (0.00, 0.00, 0.25, 0.85),
    (0.75, 0.00, 1.00, 0.85),
    (0.00, 0.00, 1.00, 0.12),
    (0.30, 0.86, 0.70, 1.00),
]

# ByteTrack settings
BT_HIGH = 0.25
BT_LOW = 0.03
BT_NEW = 0.12
BT_MATCH_IOU = 0.15
BT_BUFFER = 140
BT_MIN_HITS = 1
BT_MAX_PREDICT_AGE = 6

YOLO_CONF_EFFECTIVE = min(CONF, BT_LOW)
KALMAN_HOLD_MAX = 10

# Single-target lock policy
TARGET_SWITCH_MISS_FRAMES = 1
TARGET_PICK_MIN_SCORE = 0.00
TARGET_REACQ_DIST_FACTOR = 7.5
TURN_MAX_ASPECT_RATIO = 6.0
TURN_MAX_AREA_RATIO = 9.0
TARGET_SWITCH_CONFIRM_HITS = 1
TARGET_STICKY_SCORE_BONUS = 0.45
TARGET_SWITCH_IOU_FLOOR = 0.0
STALE_LOCK_BREAK_ENABLE = False
STALE_LOCK_BREAK_DIST_DIAG = 3.5
STALE_LOCK_BREAK_MIN_MISS = 2
STALE_LOCK_BREAK_KLT_QUALITY = 0.35
STALE_LOCK_BREAK_KLT_IOU = 0.25
FAST_HANDOFF_ENABLE = False
FAST_HANDOFF_CONFIRM_HITS = 2
FAST_HANDOFF_GUIDANCE_CMD_DAMP = 0.0
CONFIRMED_NO_ID_NEAR_FACTOR = 2.6
CONFIRMED_NO_ID_NEAR_MIN = 26.0
APPROACH_MODE_ENABLE = True
ADAPTIVE_CHASE_ENABLE = True
ADAPTIVE_STAGE_MID_AREA = 260.0
APPROACH_CLOSE_AREA = 850.0
ADAPTIVE_STAGE_AREA_HYST = 120.0
APPROACH_MID_FORCE_DET_EVERY = 2
APPROACH_FORCE_DET_AREA = 850.0
APPROACH_FORCE_DET_EVERY = 1
APPROACH_MID_KF_ROI_SCALE = 1.15
APPROACH_KF_ROI_SCALE = 1.35
APPROACH_MID_MAX_AREA_RATIO = 14.0
APPROACH_MAX_AREA_RATIO = 24.0
APPROACH_MID_NEAR_DIST_DIAG = 1.35
APPROACH_NEAR_DIST_DIAG = 1.6
APPROACH_HSV_RELAX_GROW_RATIO = 2.5
APPROACH_MID_HSV_MIN_SCALE = 0.65
APPROACH_HSV_MIN_SCALE = 0.35
APPROACH_MID_SCORE_WEIGHT = 0.08
APPROACH_MID_SCORE_CLIP = 0.16
APPROACH_SCORE_WEIGHT = 0.18
APPROACH_SCORE_CLIP = 0.35
APPROACH_MID_SWITCH_EXTRA_MISS = 1
APPROACH_MID_SWITCH_EXTRA_HITS = 1
APPROACH_CLOSE_SWITCH_EXTRA_MISS = 2
APPROACH_CLOSE_SWITCH_EXTRA_HITS = 2
CLOSE_FORCE_FULLSCAN = True
PART_MERGE_ENABLE = True
PART_MERGE_MIN_PARTS = 2
PART_MERGE_MIN_REF_DIAG = 28.0
PART_MERGE_REF_DIST_DIAG = 2.2
PART_MERGE_CLUSTER_DIST_DIAG = 1.4
PART_MERGE_IOU_FLOOR = 0.02
PART_MERGE_MIN_UNION_GAIN = 1.35
PART_MERGE_MAX_UNION_RATIO = 40.0
PART_MERGE_MAX_ASPECT = 8.0
PART_MERGE_SCORE_BONUS = 0.03
SWITCH_FAST_MANEUVER_ENABLE = True
SWITCH_FAST_MANEUVER_SPEED = 60.0
SWITCH_FAST_MANEUVER_EXTRA_MISS = 2
SWITCH_FAST_MANEUVER_EXTRA_HITS = 2

# Switch-only trajectory gate (candidate must be kinematically plausible
# относительно направления цели)
SWITCH_TRAJ_GATE_ENABLE = True
SWITCH_TRAJ_MISS_GE = 1
SWITCH_TRAJ_MIN_SPEED = 1.2
SWITCH_TRAJ_COS_MIN = 0.10
SWITCH_TRAJ_PERP_DIAG = 0.35
SWITCH_TRAJ_PERP_MIN = 10.0
SWITCH_TRAJ_REQUIRE_RESIDUAL_BYPASS = True

# Switch-only anti-static gate (reject candidates that do not move over time)
SWITCH_STATIC_GUARD_ENABLE = True
SWITCH_TRACK_HIST_WINDOW = 8
SWITCH_STATIC_MIN_DISP = 4.0
SWITCH_STATIC_MIN_DIAG = 0.10
SWITCH_STATIC_REQUIRE_RESIDUAL_BYPASS = True

# Weak-reacquire guard (suppress one-frame turn false positives)
WEAK_REACQ_GUARD_ENABLE = True
WEAK_REACQ_MISS_GE = 6
WEAK_REACQ_MIN_HITS = 2
WEAK_REACQ_MIN_SCORE = 0.05
WEAK_REACQ_MAX_DIST_FACTOR = 4.0
WEAK_REACQ_MAX_DIST_MIN = 24.0
WEAK_REACQ_ADOPT_HITS = 3

# Separate score floors for actual target adoption.
# ByteTrack still receives low-score candidates, but lock/switch decisions
# require stronger track evidence.
TRACK_SCORE_MIN_ACQUIRE = 0.08
TRACK_SCORE_MIN_REACQUIRE = 0.07
TRACK_SCORE_MIN_SWITCH = 0.10

# Provisional reacquire memory (keep priority near last true micro-detection)
PROVISIONAL_HOLD_ENABLE = True
PROVISIONAL_HOLD_MAX = 8
PROVISIONAL_HIT_DECAY = 1
RECOVER_NEAR_DIST_FACTOR = 3.2
RECOVER_NEAR_DIST_MIN = 26.0

# Analog FPV hardening
ANALOG_FPV_MODE = True
APPLY_YOLO_PREPROC = True
PRE_CLAHE_CLIP = 2.2
PRE_CLAHE_TILE = (8, 8)
PRE_BLUR_K = 3
PRE_UNSHARP = 0.12

# OSD false positives rejection
REJECT_OSD_ZONES = True
REJECT_OSD_SMALL_AREA_MAX = 2600.0
REJECT_OSD_ZONES_NORM = [
    (0.00, 0.00, 0.20, 0.80),
    (0.80, 0.00, 1.00, 0.80),
    (0.00, 0.00, 1.00, 0.08),
    (0.35, 0.90, 0.65, 1.00),
]
# Additional lock-switch guard: do not switch to candidates inside OSD zones
# when they are far from predicted target position.
OSD_SWITCH_BLOCK_ENABLE = True
OSD_SWITCH_BLOCK_DIST_DIAG = 1.25

# Turn-safe recovery
TURN_SAFE_ENABLE = True
TURN_SAFE_UNC_LIMIT = 2800.0
TURN_SAFE_MISS_BEFORE_RESET = 12
TURN_SAFE_FORCE_FULLSCAN = True
TURN_SAFE_MIN_SCORE = 0.08
TURN_SAFE_REQUIRE_TRACK_ID = True

# Safety against drifting "CONFIRMED" state without a valid track anchor
CONFIRMED_NO_ID_MAX_MISS = 3

# Motion-zone recover assist (for tiny distant targets)
MOTION_ZONE_ENABLE = True
MOTION_ZONE_GRID = (8, 6)
MOTION_DIFF_THR = 16
MOTION_BLUR_K = 3
MOTION_MORPH_K = 3
MOTION_ZONE_MIN_PIXELS = 10
MOTION_ZONE_MIN_RATIO = 0.0012
MOTION_MAX_ACTIVE_ZONE_RATIO = 0.55
MOTION_ROI_MARGIN = 28
MOTION_ROI_MIN_SIZE = 80
DRAW_MOTION_ROI = False
MOTION_TURN_THR_GAIN = 2.0
MOTION_TURN_THR_MAX_BONUS = 10
MOTION_ZONE_HEAT_DECAY = 0.88
MOTION_ZONE_HEAT_BOOST = 1.0
MOTION_ZONE_HEAT_CLIP = 4.0
MOTION_ZONE_HEAT_MIN = 0.65
MOTION_ZONE_HOT_FAVOR_BONUS = 0.35
MOTION_ZONE_ENFORCE_HOT = True
MOTION_ZONE_ENFORCE_MISS_GE = 2
# Use motion ROI also in CONFIRMED state when target starts slipping.
MOTION_CONF_ENABLE = True
MOTION_CONF_MISS_GE = 2
MOTION_CONF_KLT_INVALID_ONLY = True
# Extra anti-jump guard for switching while using motion/wave ROI in CONFIRMED state.
MOTION_CONF_SWITCH_MISS_FRAMES = 3
MOTION_CONF_SWITCH_MIN_HITS = 2
MOTION_CONF_SWITCH_MIN_SCORE = 0.08
MOTION_CONF_SWITCH_IOU_FLOOR = 0.05
MOTION_CONF_SWITCH_DIST_DIAG = 0.90
MOTION_CONF_SWITCH_CONFIRM_HITS = 4
MOTION_CONF_SWITCH_REQUIRE_RESIDUAL = True
# While confirmed+miss, force periodic FULL scans so detector can reacquire globally.
CONF_MISS_FORCE_FULLSCAN_EVERY = 8
EGO_RESIDUAL_GATE_ENABLE = True
EGO_RESIDUAL_MISS_GE = 2
EGO_RESIDUAL_MIN_PIXELS = 2
EGO_RESIDUAL_MIN_RATIO = 0.006

# Wavelet assist (tiny distant target recover)
WAVELET_ASSIST_ENABLE = True
WAVELET_ACTIVE_MISS_GE = 2
WAVELET_ONLY_UNCONFIRMED = True
WAVELET_ONLY_RECOVER_PHASE = True
WAVELET_BOX_MARGIN = 8
WAVELET_MIN_SIDE = 20
WAVELET_GATE_TINY_AREA_MAX = 260.0
WAVELET_GATE_MISS_GE = 2
WAVELET_GATE_MIN_ENERGY = 0.14
WAVELET_ENERGY_BASE = 0.22
WAVELET_SCORE_WEIGHT = 0.28
WAVELET_SCORE_CLIP = 0.35
# Temporal M/N stabilization for wavelet (per track id)
WAVELET_MN_ENABLE = True
WAVELET_MN_WINDOW = 4
WAVELET_MN_MIN_HITS = 1
WAVELET_MN_MISS_GE = 2
# Wavelet-driven micro ROI for recover
WAVELET_ROI_ENABLE = True
WAVELET_ROI_MISS_GE = 2
WAVELET_ROI_CONF_MISS_GE = 6
WAVELET_ROI_TTL = 4
WAVELET_ROI_EVERY_N = 3
WAVELET_ROI_MARGIN = 20
WAVELET_ROI_MIN_SIDE = 84
WAVELET_ROI_PRED_SCALE = 3.0
WAVELET_ROI_MIN_PEAK_RATIO = 1.5

# AutoGaze assist (optional ROI prior for recover)
AUTOGAZE_ENABLE = False
AUTOGAZE_MODEL_ID = "nvidia/AutoGaze"  # can be HF id or local folder
AUTOGAZE_USE_FLASH_ATTN = False        # keep False unless flash-attn is installed and stable
AUTOGAZE_LOCAL_FILES_ONLY = True       # avoids HF network retries; set False to download/update model
AUTOGAZE_CLIP_LEN = 16
AUTOGAZE_UPDATE_EVERY = 3
AUTOGAZE_GAZING_RATIO = 0.75
AUTOGAZE_TASK_LOSS_REQUIREMENT = 0.70
AUTOGAZE_RESULT_TTL = 12
AUTOGAZE_MISS_GE = 2
AUTOGAZE_CONF_MISS_GE = 2
AUTOGAZE_TOPK = 2
AUTOGAZE_MIN_ACTIVE_CELLS = 1
AUTOGAZE_ROI_MARGIN_CELLS = 1
AUTOGAZE_ROI_MIN_SIDE = 90
DRAW_AUTOGAZE_ROI = False
MODULE_TIME_BUDGET_MS = 30.0
AUTOGAZE_COOLDOWN_FRAMES = 12
WAVELET_COOLDOWN_FRAMES = 8

# Recover anti-noise gates for tiny distant target
RECOVER_FILTER_ENABLE = True
RECOVER_MIN_SCORE = 0.03
RECOVER_TINY_AREA_MAX = 180.0
RECOVER_TINY_MIN_SCORE = 0.045
RECOVER_TINY_MIN_MOTION_PIXELS = 1
RECOVER_TINY_MIN_MOTION_RATIO = 0.003
RECOVER_TARGET_MIN_HITS = 1

# Keep scanning around last brief detection to stabilize reacquire
FLASH_ROI_ENABLE = True
FLASH_ROI_TTL = 7
FLASH_ROI_MARGIN = 72
FLASH_ROI_MIN_SIDE = 90
FLASH_ROI_MIN_SCORE = 0.04

# Anti-stall recover (avoid getting stuck in wrong ROI-KF when detections disappear)
RECOVER_ROI_KF_DISABLE_MISS_GE = 6
RECOVER_FORCE_MOTION_FIRST_MISS_GE = 8
RECOVER_FORCE_FULLSCAN_MISS_GE = 18

# Pre-acquire for tiny distant target (M/N logic before stable track-id appears)
PREACQ_ENABLE = True
PREACQ_WINDOW = 7
PREACQ_MIN_HITS = 3
PREACQ_MIN_SCORE = 0.035
PREACQ_NEAR_FACTOR = 3.4
PREACQ_NEAR_MIN = 20.0
PREACQ_ACQ_BONUS = 2
PREACQ_INIT_KF_ON_MN = True

# Anti-false profile: "soft" (max recall), "medium", "hard", "hard_far"
ANTI_FP_PROFILE = "hard_far"
ACTIVE_ANTI_FP_PROFILE = "custom"


def apply_anti_fp_profile():
    global CONFIRM_HITS, TARGET_SWITCH_CONFIRM_HITS
    global BT_NEW, BT_MATCH_IOU, BT_LOW, YOLO_CONF_EFFECTIVE
    global TRACK_SCORE_MIN_ACQUIRE, TRACK_SCORE_MIN_REACQUIRE, TRACK_SCORE_MIN_SWITCH
    global RECOVER_MIN_SCORE, RECOVER_TINY_MIN_SCORE, RECOVER_TINY_MIN_MOTION_PIXELS
    global RECOVER_TINY_MIN_MOTION_RATIO, RECOVER_TARGET_MIN_HITS
    global WEAK_REACQ_MISS_GE, WEAK_REACQ_MIN_HITS, WEAK_REACQ_MIN_SCORE, WEAK_REACQ_ADOPT_HITS
    global RECOVER_NEAR_DIST_FACTOR, PROVISIONAL_HOLD_MAX, PROVISIONAL_HIT_DECAY
    global MOTION_DIFF_THR, MOTION_ZONE_MIN_RATIO, MOTION_MAX_ACTIVE_ZONE_RATIO
    global MOTION_ZONE_HEAT_MIN, MOTION_ZONE_HOT_FAVOR_BONUS, MOTION_ZONE_ENFORCE_MISS_GE
    global EGO_RESIDUAL_MISS_GE, EGO_RESIDUAL_MIN_PIXELS, EGO_RESIDUAL_MIN_RATIO
    global ACTIVE_ANTI_FP_PROFILE

    p = str(ANTI_FP_PROFILE).strip().lower()
    if p not in ("soft", "medium", "hard", "hard_far"):
        p = "medium"
    ACTIVE_ANTI_FP_PROFILE = p

    if p == "soft":
        CONFIRM_HITS = 2
        TARGET_SWITCH_CONFIRM_HITS = 1
        BT_LOW = 0.03
        BT_NEW = 0.12
        BT_MATCH_IOU = 0.15
        RECOVER_MIN_SCORE = 0.03
        RECOVER_TINY_MIN_SCORE = 0.045
        RECOVER_TINY_MIN_MOTION_PIXELS = 1
        RECOVER_TINY_MIN_MOTION_RATIO = 0.003
        RECOVER_TARGET_MIN_HITS = 1
        TRACK_SCORE_MIN_ACQUIRE = 0.06
        TRACK_SCORE_MIN_REACQUIRE = 0.05
        TRACK_SCORE_MIN_SWITCH = 0.08
        WEAK_REACQ_MISS_GE = 8
        WEAK_REACQ_MIN_HITS = 1
        WEAK_REACQ_MIN_SCORE = 0.045
        WEAK_REACQ_ADOPT_HITS = 2
        RECOVER_NEAR_DIST_FACTOR = 3.8
        PROVISIONAL_HOLD_MAX = 10
        PROVISIONAL_HIT_DECAY = 1
        MOTION_DIFF_THR = 16
        MOTION_ZONE_MIN_RATIO = 0.0012
        MOTION_MAX_ACTIVE_ZONE_RATIO = 0.60
        MOTION_ZONE_HEAT_MIN = 0.55
        MOTION_ZONE_HOT_FAVOR_BONUS = 0.25
        MOTION_ZONE_ENFORCE_MISS_GE = 3
        EGO_RESIDUAL_MISS_GE = 4
        EGO_RESIDUAL_MIN_PIXELS = 1
        EGO_RESIDUAL_MIN_RATIO = 0.002

    elif p == "medium":
        CONFIRM_HITS = 3
        TARGET_SWITCH_CONFIRM_HITS = 2
        BT_LOW = 0.03
        BT_NEW = 0.14
        BT_MATCH_IOU = 0.17
        RECOVER_MIN_SCORE = 0.04
        RECOVER_TINY_MIN_SCORE = 0.060
        RECOVER_TINY_MIN_MOTION_PIXELS = 2
        RECOVER_TINY_MIN_MOTION_RATIO = 0.008
        RECOVER_TARGET_MIN_HITS = 2
        TRACK_SCORE_MIN_ACQUIRE = 0.08
        TRACK_SCORE_MIN_REACQUIRE = 0.07
        TRACK_SCORE_MIN_SWITCH = 0.10
        WEAK_REACQ_MISS_GE = 5
        WEAK_REACQ_MIN_HITS = 2
        WEAK_REACQ_MIN_SCORE = 0.060
        WEAK_REACQ_ADOPT_HITS = 3
        RECOVER_NEAR_DIST_FACTOR = 2.9
        PROVISIONAL_HOLD_MAX = 7
        PROVISIONAL_HIT_DECAY = 1
        MOTION_DIFF_THR = 17
        MOTION_ZONE_MIN_RATIO = 0.0016
        MOTION_MAX_ACTIVE_ZONE_RATIO = 0.50
        MOTION_ZONE_HEAT_MIN = 0.70
        MOTION_ZONE_HOT_FAVOR_BONUS = 0.35
        MOTION_ZONE_ENFORCE_MISS_GE = 2
        EGO_RESIDUAL_MISS_GE = 2
        EGO_RESIDUAL_MIN_PIXELS = 2
        EGO_RESIDUAL_MIN_RATIO = 0.006

    else:  # hard / hard_far
        CONFIRM_HITS = 3
        TARGET_SWITCH_CONFIRM_HITS = 3
        BT_LOW = 0.035
        BT_NEW = 0.11
        BT_MATCH_IOU = 0.17
        RECOVER_MIN_SCORE = 0.05
        RECOVER_TINY_MIN_SCORE = 0.075
        RECOVER_TINY_MIN_MOTION_PIXELS = 3
        RECOVER_TINY_MIN_MOTION_RATIO = 0.015
        RECOVER_TARGET_MIN_HITS = 3
        TRACK_SCORE_MIN_ACQUIRE = 0.10
        TRACK_SCORE_MIN_REACQUIRE = 0.09
        TRACK_SCORE_MIN_SWITCH = 0.12
        WEAK_REACQ_MISS_GE = 3
        WEAK_REACQ_MIN_HITS = 3
        WEAK_REACQ_MIN_SCORE = 0.080
        WEAK_REACQ_ADOPT_HITS = 3
        RECOVER_NEAR_DIST_FACTOR = 2.4
        PROVISIONAL_HOLD_MAX = 5
        PROVISIONAL_HIT_DECAY = 2
        MOTION_DIFF_THR = 19
        MOTION_ZONE_MIN_RATIO = 0.0022
        MOTION_MAX_ACTIVE_ZONE_RATIO = 0.42
        MOTION_ZONE_HEAT_MIN = 0.85
        MOTION_ZONE_HOT_FAVOR_BONUS = 0.45
        MOTION_ZONE_ENFORCE_MISS_GE = 1
        EGO_RESIDUAL_MISS_GE = 1
        EGO_RESIDUAL_MIN_PIXELS = 3
        EGO_RESIDUAL_MIN_RATIO = 0.010

    YOLO_CONF_EFFECTIVE = min(CONF, BT_LOW)


apply_anti_fp_profile()

# ===== Chase-drone preset overrides (applied after anti-fp profile) =====
# Ужесточаем переключение на альтернативный track, чтобы меньше липнуть на землю.
CONFIRM_HITS = 4
ACQUIRE_CONFIRM_SCORE = 8
PREACQ_MIN_HITS = 4
PREACQ_MIN_SCORE = 0.05
TARGET_SWITCH_MISS_FRAMES = 3
TARGET_SWITCH_CONFIRM_HITS = 4
TARGET_SWITCH_IOU_FLOOR = 0.10
TARGET_REACQ_DIST_FACTOR = 5.5
TRACK_SCORE_MIN_ACQUIRE = 0.10
TRACK_SCORE_MIN_REACQUIRE = 0.09
TRACK_SCORE_MIN_SWITCH = 0.14

# Switch gates preset for chase-scene
SWITCH_TRAJ_GATE_ENABLE = True
SWITCH_TRAJ_MISS_GE = 1
SWITCH_TRAJ_MIN_SPEED = 8.0
SWITCH_TRAJ_COS_MIN = 0.18
SWITCH_TRAJ_PERP_DIAG = 0.28
SWITCH_TRAJ_PERP_MIN = 9.0
SWITCH_TRAJ_REQUIRE_RESIDUAL_BYPASS = True

SWITCH_STATIC_GUARD_ENABLE = True
SWITCH_TRACK_HIST_WINDOW = 8
SWITCH_STATIC_MIN_DISP = 5.0
SWITCH_STATIC_MIN_DIAG = 0.12
SWITCH_STATIC_REQUIRE_RESIDUAL_BYPASS = True

WEAK_REACQ_MISS_GE = 4
WEAK_REACQ_MIN_HITS = 3
WEAK_REACQ_MIN_SCORE = 0.09
WEAK_REACQ_ADOPT_HITS = 5

MOTION_CONF_SWITCH_MIN_SCORE = 0.12
MOTION_CONF_SWITCH_CONFIRM_HITS = 6
MOTION_CONF_SWITCH_DIST_DIAG = 0.70
MOTION_CONF_SWITCH_IOU_FLOOR = 0.10

# Режем псевдодвижение земли.
EGO_RESIDUAL_MIN_PIXELS = 3
EGO_RESIDUAL_MIN_RATIO = 0.010

RECOVER_MIN_SCORE = 0.05
RECOVER_TINY_MIN_SCORE = 0.08
RECOVER_TARGET_MIN_HITS = 2
TURN_SAFE_MIN_SCORE = 0.12

# Appearance gate для подавления статичных ложняков.
USE_HSV_GATE = True
HSV_GATE_MIN_SIM = 0.10
HSV_UPDATE_EVERY = 4

# Wavelet чаще цепляет текстуру земли на chase-сценах.
WAVELET_ROI_ENABLE = False

# AutoGaze подключаем только на более явном срыве.
AUTOGAZE_MISS_GE = 4
AUTOGAZE_CONF_MISS_GE = 3
AUTOGAZE_TOPK = 1
AUTOGAZE_UPDATE_EVERY = 4
AUTOGAZE_RESULT_TTL = 8

# ===== Soft handoff patch for tiny FPV chase targets =====
# Lets YOLO refresh Kalman/KLT even when ByteTrack changes track_id.
SOFT_YOLO_HANDOFF_ENABLE = True
SOFT_YOLO_MIN_MISS = 1
SOFT_YOLO_MIN_ABSENT = 1
SOFT_YOLO_MIN_SCORE = 0.055
SOFT_YOLO_DIST_DIAG = 8.0
SOFT_YOLO_DIST_MIN = 48.0
SOFT_YOLO_IOU_FLOOR = 0.0
SOFT_YOLO_MAX_AREA_RATIO = 18.0
SOFT_YOLO_MAX_ASPECT_RATIO = 8.0
SOFT_YOLO_TRACK_IOU = 0.02
SOFT_YOLO_TRACK_DIST_DIAG = 2.2
SOFT_YOLO_TRACK_DIST_MIN = 32.0
SOFT_YOLO_REFRESH_KLT = True

# Keep a valid KLT anchor alive instead of letting prediction hold run to miss=10.
KLT_VALID_HOLD_ENABLE = True
KLT_VALID_HOLD_MAX_MISS = 3

# Make handoff less rigid. The old chase preset required several frames plus
# approach/fast extras, so swHit often stayed at 1 and the lock was lost.
TARGET_SWITCH_MISS_FRAMES = 1
TARGET_SWITCH_CONFIRM_HITS = 1
TARGET_SWITCH_IOU_FLOOR = 0.0
TRACK_SCORE_MIN_SWITCH = 0.07
WEAK_REACQ_MISS_GE = 2
WEAK_REACQ_MIN_HITS = 1
WEAK_REACQ_MIN_SCORE = 0.05
WEAK_REACQ_ADOPT_HITS = 1
MOTION_CONF_SWITCH_MIN_HITS = 1
MOTION_CONF_SWITCH_MIN_SCORE = 0.06
MOTION_CONF_SWITCH_CONFIRM_HITS = 1
MOTION_CONF_SWITCH_DIST_DIAG = 1.25
MOTION_CONF_SWITCH_IOU_FLOOR = 0.0
MOTION_CONF_SWITCH_REQUIRE_RESIDUAL = False
APPROACH_MID_SWITCH_EXTRA_MISS = 0
APPROACH_MID_SWITCH_EXTRA_HITS = 0
APPROACH_CLOSE_SWITCH_EXTRA_MISS = 0
APPROACH_CLOSE_SWITCH_EXTRA_HITS = 0
SWITCH_FAST_MANEUVER_EXTRA_MISS = 0
SWITCH_FAST_MANEUVER_EXTRA_HITS = 0
SWITCH_TRAJ_GATE_ENABLE = False
STALE_LOCK_BREAK_ENABLE = True
STALE_LOCK_BREAK_DIST_DIAG = 0.8
STALE_LOCK_BREAK_MIN_MISS = 1
FAST_HANDOFF_ENABLE = True
FAST_HANDOFF_CONFIRM_HITS = 1
FAST_HANDOFF_GUIDANCE_CMD_DAMP = 0.35
USE_HSV_GATE = False
CONFIRMED_NO_ID_MAX_MISS = 8
KALMAN_HOLD_MAX = 6



# ===== Anti-flicker handoff patch v2 =====
# Сохраняем мягкий handoff, но не даем ByteTrack/YOLO утащить Kalman
# на одиночный ложняк, пока KLT уверенно держит физическую цель.
BT_KLT_ANCHOR_GUARD_ENABLE = True
BT_KLT_ANCHOR_MIN_Q = 0.55
BT_KLT_ANCHOR_DIST_DIAG = 1.65
BT_KLT_ANCHOR_DIST_MIN = 34.0
BT_KLT_ANCHOR_IOU_FLOOR = 0.015
BT_KLT_ANCHOR_MAX_AREA_RATIO = 9.0
BT_KLT_ANCHOR_MAX_ASPECT_RATIO = 7.0
BT_KLT_ANCHOR_HOLD_ON_REJECT = True
BT_KLT_ANCHOR_DEBUG_EVERY = 30

# Не считаем постоянную смену ByteTrack id физическим переключением цели,
# если KLT якорь сильный и bbox принят рядом с ним.
BT_ID_STABILIZE_WHEN_KLT_VALID = True

# Soft YOLO handoff: широкий радиус оставляем только для реального восстановления.
# Когда KLT валидный, handoff обязан быть рядом с KLT-якорем.
SOFT_YOLO_KLT_MIN_Q = 0.55
SOFT_YOLO_KLT_DIST_DIAG = 1.85
SOFT_YOLO_KLT_DIST_MIN = 34.0
SOFT_YOLO_KLT_IOU_FLOOR = 0.015
SOFT_YOLO_KLT_MAX_AREA_RATIO = 9.0
SOFT_YOLO_LOST_DIST_DIAG = 7.0
SOFT_YOLO_LOST_DIST_MIN = 48.0

# Предыдущий hotfix был слишком быстрый: иногда принимал ложняк на 1 кадр.
TARGET_SWITCH_CONFIRM_HITS = 2
WEAK_REACQ_ADOPT_HITS = 2
FAST_HANDOFF_CONFIRM_HITS = 2
STALE_LOCK_BREAK_DIST_DIAG = 1.6
STALE_LOCK_BREAK_MIN_MISS = 2
GUIDANCE_OVERRIDE_MISS_GE = 3

# ===== Safe YOLO re-anchor patch v3 =====
# Fixes stale KLT/ByteTrack hold: if KLT is confident but stuck on the old
# position, a repeated YOLO candidate can re-anchor Kalman/KLT after M/N hits.
YOLO_REANCHOR_ENABLE = True
YOLO_REANCHOR_MISS_GE = 2
YOLO_REANCHOR_ABSENT_GE = 1
YOLO_REANCHOR_MIN_SCORE = 0.065
YOLO_REANCHOR_HITS = 2
YOLO_REANCHOR_TRACK_HITS = 2
YOLO_REANCHOR_WINDOW = 3
YOLO_REANCHOR_REPEAT_DIST_MIN = 46.0
YOLO_REANCHOR_REPEAT_DIST_DIAG = 2.8
YOLO_REANCHOR_PRED_DIST_MIN = 72.0
YOLO_REANCHOR_PRED_DIST_DIAG = 7.0
YOLO_REANCHOR_NEAR_PRED_ONE_SHOT_SCORE = 0.18
YOLO_REANCHOR_MAX_AREA_RATIO = 20.0
YOLO_REANCHOR_MAX_ASPECT_RATIO = 8.0
YOLO_REANCHOR_OSD_REJECT = True
YOLO_REANCHOR_OSD_MIN_SCORE = 0.12
YOLO_REANCHOR_RESET_KLT = True

# Let the re-anchor path do the recovery work before guidance is reset.
STALE_LOCK_BREAK_MIN_MISS = 3
FAST_HANDOFF_CONFIRM_HITS = 2
# Debug: draw only fresh ByteTrack boxes; predicted old BT boxes caused visual "sticking" confusion.
DRAW_BT_ONLY_FRESH = True


# ===== Multi-hypothesis trajectory prediction patch v4 =====
# Builds several short-term hypotheses when YOLO/ByteTrack lose the target:
# constant velocity, acceleration, damped velocity, left/right turn, up/down.
# These hypotheses expand the ROI and help YOLO re-anchor without accepting one-frame false positives.
TRAJ_PREDICT_ENABLE = True
TRAJ_PREDICT_MISS_GE = 1
TRAJ_HISTORY_LEN = 18
TRAJ_HISTORY_LOOKBACK = 8
TRAJ_HISTORY_KEEP_MISS_LE = 2
TRAJ_VEL_HIST_WEIGHT = 0.55
TRAJ_MAX_ACCEL_PX_S2 = 1200.0
TRAJ_HORIZON_MISS_OFFSET = 1
TRAJ_HORIZON_MIN_SEC = 0.08
TRAJ_HORIZON_MAX_SEC = 0.55
TRAJ_BOX_GROW_PER_MISS = 0.08
TRAJ_BOX_GROW_MAX = 1.75
TRAJ_MAX_HYPOTHESES = 7
TRAJ_DUP_CENTER_DIST = 8.0
TRAJ_USE_ACCEL = True
TRAJ_MIN_SPEED_FOR_MANEUVER = 8.0
TRAJ_LATERAL_ACCEL_MIN = 90.0
TRAJ_LATERAL_ACCEL_MAX = 620.0
TRAJ_LATERAL_ACCEL_SPEED_GAIN = 5.5
TRAJ_VERTICAL_ACCEL_MIN = 60.0
TRAJ_VERTICAL_ACCEL_MAX = 420.0
TRAJ_VERTICAL_ACCEL_SPEED_GAIN = 3.5

# Use prediction hypotheses for detector search and re-anchor scoring.
TRAJ_ROI_ENABLE = True
TRAJ_ROI_MISS_GE = 1
TRAJ_ROI_MARGIN = 70
TRAJ_ROI_MIN_SIDE = 170
TRAJ_REANCHOR_ENABLE = True
TRAJ_REANCHOR_MIN_SCORE = 0.055
TRAJ_REANCHOR_DIST_MIN = 64.0
TRAJ_REANCHOR_DIST_DIAG = 4.2
TRAJ_USE_PRIMARY_FOR_HOLD = True
TRAJ_HOLD_MISS_GE = 1
DRAW_TRAJ_PREDICTIONS = True

# Trajectory evidence may adopt faster than a generic far re-anchor, but still requires
# repeat confirmation unless the score is strong and close to a predicted hypothesis.
YOLO_REANCHOR_NEAR_PRED_ONE_SHOT_SCORE = 0.16
YOLO_REANCHOR_HITS = 2
YOLO_REANCHOR_TRACK_HITS = 1

# ===== Close-stage ROI priority patch v5 =====
# In close stage do not run full-screen YOLO on every detector tick.
# Use ROI-KF/ROI-TRAJ while lock is healthy; run FULL-CLOSE only as a periodic
# health-check or when the anchor is weak/missing. This reduces false positives
# entering ByteTrack while the real target is already locked.
CLOSE_PERIODIC_FULLSCAN_ENABLE = True
CLOSE_PERIODIC_FULLSCAN_EVERY = 12
CLOSE_FULLSCAN_MISS_GE = 2
CLOSE_FULLSCAN_WHEN_KLT_INVALID = True
CLOSE_FULLSCAN_TARGET_ABSENT_GE = 2

from runtime_env import apply_config_env_overrides

apply_config_env_overrides(globals())
