import subprocess
import os
import shutil

import bootstrap_icons
import libusb

Import('env', 'arch', 'common', 'messaging', 'visionipc', 'cereal', 'replay_lib', 'ffmpeg_libs')

# Detect Qt - skip build if not available
if arch == "Darwin":
  try:
    brew_prefix = subprocess.check_output(['brew', '--prefix'], encoding='utf8').strip()
    has_qt = os.path.isdir(os.path.join(brew_prefix, "opt/qt@5"))
  except (FileNotFoundError, subprocess.CalledProcessError):
    has_qt = False
else:
  has_qt = shutil.which('qmake') is not None
if not has_qt:
  Return()

qt_env = env.Clone()
qt_modules = ["Widgets", "Gui", "Core"]

qt_libs = []
if arch == "Darwin":
  qt_env['QTDIR'] = f"{brew_prefix}/opt/qt@5"
  qt_dirs = [
    os.path.join(qt_env['QTDIR'], "include"),
  ]
  qt_dirs += [f"{qt_env['QTDIR']}/include/Qt{m}" for m in qt_modules]
  qt_env["LINKFLAGS"] += ["-F" + os.path.join(qt_env['QTDIR'], "lib")]
  qt_env["FRAMEWORKS"] += [f"Qt{m}" for m in qt_modules]
  qt_env.AppendENVPath('PATH', os.path.join(qt_env['QTDIR'], "bin"))
else:
  qt_install_prefix = subprocess.check_output(['qmake', '-query', 'QT_INSTALL_PREFIX'], encoding='utf8').strip()
  qt_install_headers = subprocess.check_output(['qmake', '-query', 'QT_INSTALL_HEADERS'], encoding='utf8').strip()

  qt_env['QTDIR'] = qt_install_prefix
  qt_dirs = [
    f"{qt_install_headers}",
  ]

  qt_gui_path = os.path.join(qt_install_headers, "QtGui")
  qt_gui_dirs = [d for d in os.listdir(qt_gui_path) if os.path.isdir(os.path.join(qt_gui_path, d))]
  qt_dirs += [f"{qt_install_headers}/QtGui/{qt_gui_dirs[0]}/QtGui", ] if qt_gui_dirs else []
  qt_dirs += [f"{qt_install_headers}/Qt{m}" for m in qt_modules]

  qt_libs = [f"Qt5{m}" for m in qt_modules]
qt_env['QT3DIR'] = qt_env['QTDIR']
qt_env.Tool('qt3')

qt_env['CPPPATH'] += qt_dirs
qt_flags = [
  "-D_REENTRANT",
  "-DQT_NO_DEBUG",
  "-DQT_WIDGETS_LIB",
  "-DQT_GUI_LIB",
  "-DQT_CORE_LIB",
  "-DQT_MESSAGELOGCONTEXT",
]
qt_env['CXXFLAGS'] += qt_flags
qt_env['LIBPATH'] += ['#openpilot/selfdrive/ui', ]
qt_env['LIBS'] = qt_libs

base_frameworks = qt_env['FRAMEWORKS']
base_libs = [common, messaging, cereal, visionipc, 'm', 'pthread'] + qt_env["LIBS"]

if arch == "Darwin":
  base_frameworks += ['CoreFoundation', 'CoreVideo', 'CoreMedia', 'IOKit', 'Security', 'VideoToolbox']

cabana_env = qt_env.Clone()
cabana_env['CPPPATH'] += [libusb.INCLUDE_DIR]
cabana_env['LIBPATH'] += [libusb.LIB_DIR]

cabana_libs = [cereal, messaging, visionipc, replay_lib] + ffmpeg_libs + ['bz2', 'zstd', 'usb-1.0'] + base_libs
opendbc_path = '-DOPENDBC_FILE_PATH=\'"%s"\'' % (cabana_env.Dir("../../../opendbc_repo/opendbc/dbc").abspath)
cabana_env['CXXFLAGS'] += [opendbc_path]

# embed the bootstrap icons SVG into the binary
def build_bootstrap_icons_src(target, source, env):
  data = open(str(source[0]), 'rb').read()
  with open(str(target[0]), 'w') as f:
    f.write('#include <cstddef>\n')
    f.write('extern const unsigned char bootstrap_icons_svg[];\n')
    f.write('extern const size_t bootstrap_icons_svg_len;\n')
    f.write('const unsigned char bootstrap_icons_svg[] = {\n')
    for i in range(0, len(data), 32):
      f.write(','.join(str(b) for b in data[i:i+32]) + ',\n')
    f.write('};\n')
    f.write('const size_t bootstrap_icons_svg_len = sizeof(bootstrap_icons_svg);\n')
  return None

bootstrap_icons_src = cabana_env.Command('assets/bootstrap_icons.cc', str(bootstrap_icons.SVG_PATH), build_bootstrap_icons_src)

# build assets
assets = "assets/assets.cc"
cabana_env.Command(assets, "assets/assets.qrc", f"rcc $SOURCES -o $TARGET")
cabana_env.Depends(assets, Glob('/assets/*', exclude=[assets, "assets/assets.o"]))

cabana_srcs = ['mainwin.cc', 'streams/pandastream.cc', 'streams/devicestream.cc', 'streams/livestream.cc', 'streams/abstractstream.cc', 'streams/replaystream.cc', 'binaryview.cc', 'historylog.cc', 'videowidget.cc', 'signalview.cc',
               'streams/routes.cc', 'dbc/dbc.cc', 'dbc/dbcfile.cc', 'dbc/dbcmanager.cc', 'dbc/dbcqt.cc',
               'utils/export.cc', 'utils/util.cc', 'utils/elidedlabel.cc',
               'chart/chartswidget.cc', 'chart/chart.cc', 'chart/signalselector.cc', 'chart/tiplabel.cc', 'chart/sparkline.cc',
               'commands.cc', 'messageswidget.cc', 'streamselector.cc', 'settings.cc', 'panda.cc',
               'cameraview.cc', 'detailwidget.cc', 'tools/findsimilarbits.cc', 'tools/findsignal.cc', 'tools/routeinfo.cc']
if arch != "Darwin":
  cabana_srcs += ['streams/socketcanstream.cc']
cabana_lib = cabana_env.Library("cabana_lib", cabana_srcs + [bootstrap_icons_src], LIBS=cabana_libs, FRAMEWORKS=base_frameworks)
cabana_env.Program('_cabana', ['cabana.cc', cabana_lib, assets], LIBS=cabana_libs, FRAMEWORKS=base_frameworks)

if GetOption('extras'):
  cabana_env.Program('tests/test_cabana', ['tests/test_runner.cc', 'tests/test_cabana.cc', cabana_lib], LIBS=[cabana_libs])

  # This target deliberately uses the base environment and links no Qt libraries.
  # It prevents Qt dependencies from creeping back into the DBC core.
  dbc_core_test_env = env.Clone()
  dbc_core_test_env['CXXFLAGS'] += [opendbc_path]
  dbc_core_test_objects = [
    dbc_core_test_env.Object('tests/dbc_core_test_runner', 'tests/test_runner.cc'),
    dbc_core_test_env.Object('tests/dbc_core_tests', 'tests/test_cabana.cc'),
    dbc_core_test_env.Object('tests/dbc_core_model', 'dbc/dbc.cc'),
    dbc_core_test_env.Object('tests/dbc_core_file', 'dbc/dbcfile.cc'),
    dbc_core_test_env.Object('tests/dbc_core_manager', 'dbc/dbcmanager.cc'),
  ]
  dbc_core_test_env.Program('tests/test_dbc_core', dbc_core_test_objects)

output_json_file = 'openpilot/tools/cabana/dbc/car_fingerprint_to_dbc.json'
generate_dbc = cabana_env.Command('#' + output_json_file,
                                  ['dbc/generate_dbc_json.py'],
                                  "python3 openpilot/tools/cabana/dbc/generate_dbc_json.py --out " + output_json_file)
cabana_env.Depends(generate_dbc, ["#openpilot/common", '#opendbc_repo', "#openpilot/cereal", "#msgq_repo"])
