command -nargs=1 -complete=dir DlcPro call s:ProjectSet('dlcpro', '<args>')
command -nargs=1 -complete=dir DlcProShg call s:ProjectSet('shg', '<args>')
command -nargs=1 -complete=dir DlcProGui call s:ProjectSet('dlcpro-gui', '<args>')
command -nargs=1 -complete=dir DlcProTui call s:ProjectSet('dlcpro-tui', '<args>')
command -nargs=1 -complete=dir DlcProCan call s:ProjectSet('dlcpro-can', '<args>')
command -nargs=1 -complete=dir DlcProSpecalyser call s:ProjectSet('dlcpro-specalyser', '<args>')
command -nargs=1 -complete=dir Topmode call s:ProjectSet('topmode', '<args>')
command -nargs=1 -complete=dir TopmodeGui call s:ProjectSet('topmode-gui', '<args>')
command -nargs=1 -complete=dir DigiFalc call s:ProjectSet('digifalc', '<args>')
command -nargs=1 -complete=dir ServoBoard call s:ProjectSet('servoboard', '<args>')
command -nargs=1 -complete=dir DlMotor call s:ProjectSet('dl-motor', '<args>')
command -nargs=1 -complete=dir OperationPanelF1 call s:ProjectSet('operation-panel-f1', '<args>')
command -nargs=1 -complete=dir OperationPanelF4 call s:ProjectSet('operation-panel-f4', '<args>')
command -nargs=1 -complete=dir DeCoF call s:ProjectSet('decof', '<args>')
command DeviceFirmwareUpdate call s:DeviceFirmwareUpdate()
command -nargs=? -complete=file JenkinsLinter call s:Jenkins_linter('<args>')

function s:ProjectSet(project_type, project_base_dir)
    let g:project_type = a:project_type
    let g:jenkins_url = 'http://jenkins.toptica.com'

    " directories
    if a:project_base_dir != ''
        if (isdirectory(fnamemodify(a:project_base_dir, ':p:h:h').'/src'))
            let s:ProjectBaseDir = fnamemodify(a:project_base_dir, ':p:h:h')
        elseif (isdirectory(fnamemodify(a:project_base_dir, ':p:h').'/src'))
            let s:ProjectBaseDir = fnamemodify(a:project_base_dir, ':p:h')
        else
            let s:ProjectBaseDir = fnamemodify(a:project_base_dir, ':p')
        endif
    else
        " defaults
        if (g:project_type == 'dlcpro')
            let s:ProjectBaseDir = '/home/stefan/dlcpro/firmware'
        elseif (g:project_type == 'dlcpro-tui')
            let s:ProjectBaseDir = '/home/stefan/dlcpro/firmware'
        elseif (g:project_type == 'dlcpro-can')
            let s:ProjectBaseDir = '/home/stefan/dlcpro/firmware'
        elseif (g:project_type == 'dlcpro-specalyser')
            let s:ProjectBaseDir = '/home/stefan/dlcpro/firmware'
        elseif (g:project_type == 'shg')
            let s:ProjectBaseDir = '/home/stefan/dlcpro/shg-firmware'
        elseif (g:project_type == 'dlcpro-gui')
            let s:ProjectBaseDir = '/home/stefan/dlcpro/pc-gui'
        elseif (g:project_type == 'topmode')
            let s:ProjectBaseDir = '/home/stefan/topmode/firmware'
        elseif (g:project_type == 'topmode-gui')
            let s:ProjectBaseDir = '/home/stefan/topmode/pc-gui'
        elseif (g:project_type == 'digifalc')
            let s:ProjectBaseDir = '/home/stefan/dlcpro/falc/firmware'
        elseif (g:project_type == 'servoboard')
            let s:ProjectBaseDir = '/home/stefan/dlcpro/mta/firmware'
        elseif (g:project_type == 'dl-motor')
            let s:ProjectBaseDir = '/home/stefan/dlcpro/dl-motor'
        else
            echo "no project"
        endif
    endif

    let g:ProjectSrcDirRel = 'src'
    let s:ProjectSrcDir = s:ProjectBaseDir.'/'.g:ProjectSrcDirRel
    let g:ProjectBuildDir = s:ProjectBaseDir.'/build'

    " vim path
    execute 'cd '.s:ProjectSrcDir
    execute 'set path-=./**'
    execute 'set path+=' .  s:ProjectSrcDir.'/**'
    execute 'set path+=' .  g:ProjectBuildDir.'/**'
    let s:oselas_include = '/sysroot-arm-cortexa8-linux-gnueabi/usr/include'
    let s:oselas_gdb_4 = '/opt/OSELAS.Toolchain-2012.12.1/arm-cortexa8-linux-gnueabi/gcc-4.7.3-glibc-2.16.0-binutils-2.22-kernel-3.6-sanitized/bin/arm-cortexa8-linux-gnueabi-gdb'
    let s:oselas_gdb = '/opt/OSELAS.Toolchain-2021.07.0/arm-v7a-linux-gnueabihf/gcc-11.1.1-clang-12.0.0-glibc-2.33-binutils-2.36.1-kernel-5.12.4-sanitized/bin/arm-v7a-linux-gnueabihf-gdb'
    let s:jlink_path = '/home/stefan/opt/SEGGER/JLink_Linux_V654a_x86_64'

    if (g:project_type == 'dlcpro')
        let s:Program = '/device-control/device-control'
        let s:Elffile = s:Program
        let g:ProgramRemote = '/opt/app/bin/device-control'
        set wildignore-=**/firmware/src/device-control/**
        set wildignore+=**/shg-firmware/**
        let s:makegoals = ['artifacts-firmware', 'artifacts-docu', 'artifacts', 'device-control', 'user-interface', 'doxygen', 'fw-updates', 'shg-firmware', 'can-updater', 'specalyser', 'docu-ul0', 'code-generation', 'dependency-graphs', 'decof', 'decof-sdk', 'clean', 'distclean', 'help', 'jamplayer', 'dlcpro-slot']
        let s:makeprg = 'make'
        let g:DeviceIP = 'DLC_PRO__040083'
        let g:DebugRemote = v:true
        let g:GdbPort = '2345'
        let g:GccRoot = "/opt/OSELAS.Toolchain-2012.12.1/arm-cortexa8-linux-gnueabi/gcc-4.7.3-glibc-2.16.0-binutils-2.22-kernel-3.6-sanitized"
        let g:termdebugger = s:oselas_gdb
        let g:SshOpts = ""
        let g:SshOpts2 = ""
        execute 'set path+=' .  g:GccRoot . s:oselas_include
        command DlcproEmissionOn call s:DlcproEmission('1')
        command DlcproEmissionOff call s:DlcproEmission('0')
        command DlcproShutup call s:DlcproShutup()
"        let g:ycm_clangd_args = ['--all-scopes-completion', '--header-insertion=iwyu', '--suggest-missing-includes', '--enable-config', '-isystem '.g:GccRoot . s:oselas_include]
    elseif (g:project_type == 'dlcpro-tui')
        let s:Program = '/user-interface/src/user-interface'
        let s:Elffile = s:Program
        let g:ProgramRemote = '/opt/app/bin/user-interface'
        set wildignore-=**/firmware/src/device-control/**
        set wildignore+=**/shg-firmware/**
        let s:makegoals = ['artifacts-firmware', 'artifacts', 'device-control', 'user-interface', 'doxygen', 'fw-updates', 'shg-firmware', 'can-updater', 'specalyser', 'docu-ul0', 'code-generation', 'dependency-graphs', 'clean', 'distclean', 'help', 'jamplayer', 'dlcpro-slot']
        let s:makeprg = 'make'
        let g:DeviceIP = 'DLC_PRO__040083'
        let g:DebugRemote = v:true
        let g:GdbPort = '2345'
        let g:GccRoot = "/opt/OSELAS.Toolchain-2012.12.1/arm-cortexa8-linux-gnueabi/gcc-4.7.3-glibc-2.16.0-binutils-2.22-kernel-3.6-sanitized"
        let g:termdebugger = s:oselas_gdb
        let g:SshOpts = ""
        let g:SshOpts2 = ""
        execute 'set path+=' .  g:GccRoot . s:oselas_include
        command DlcproTuiStart execute('!cd user-interface/src && ../../../build/user-interface/src/user-interface&')
    elseif (g:project_type == 'dlcpro-can')
        let s:Program = '/canopen/can-updater'
        let s:Elffile = s:Program
        let g:ProgramRemote = '/opt/app/bin/can-updater'
        set wildignore-=**/firmware/src/device-control/**
        set wildignore+=**/shg-firmware/**
        let s:makegoals = []
        let s:makeprg = 'make'
        let g:DeviceIP = 'DLC_PRO__040083'
        let g:DebugRemote = v:true
        let g:GdbPort = '2345'
        let g:GccRoot = "/opt/OSELAS.Toolchain-2012.12.1/arm-cortexa8-linux-gnueabi/gcc-4.7.3-glibc-2.16.0-binutils-2.22-kernel-3.6-sanitized"
        let g:termdebugger = s:oselas_gdb
        let g:SshOpts = ""
        let g:SshOpts2 = ""
        execute 'set path+=' .  g:GccRoot . s:oselas_include
"        let g:ycm_clangd_args = ['-I'.g:GccRoot . s:oselas_include]
    elseif (g:project_type == 'dlcpro-specalyser')
        let s:Program = '/specalyser/specalyser'
        let s:Elffile = s:Program
        let g:ProgramRemote = '/opt/app/bin/specalyser'
        set wildignore-=**/firmware/src/device-control/**
        set wildignore+=**/shg-firmware/**
        let s:makegoals = []
        let s:makeprg = 'make'
        let g:DeviceIP = 'DLC_PRO__040083'
        let g:DebugRemote = v:true
        let g:GdbPort = '2345'
        let g:GccRoot = "/opt/OSELAS.Toolchain-2012.12.1/arm-cortexa8-linux-gnueabi/gcc-4.7.3-glibc-2.16.0-binutils-2.22-kernel-3.6-sanitized"
        let g:termdebugger = s:oselas_gdb
        let g:SshOpts = ""
        let g:SshOpts2 = ""
        execute 'set path+=' .  g:GccRoot . s:oselas_include
"        let g:ycm_clangd_args = ['-I'.g:GccRoot . s:oselas_include]
    elseif (g:project_type == 'shg')
        let s:Program = '/shg-firmware/device-control/device-control-shg'
        let s:Elffile = s:Program
        let g:ProgramRemote = '/opt/app/bin/device-control-shg'
        set wildignore-=**/shg-firmware/**
        set wildignore+=**/firmware/src/device-control/**
        let s:makegoals = ['artifacts-firmware', 'artifacts', 'device-control', 'user-interface', 'doxygen', 'fw-updates', 'shg-firmware', 'can-updater', 'specalyser', 'docu-ul0', 'code-generation', 'dependency-graphs', 'clean', 'distclean', 'help', 'jamplayer', 'dlcpro-slot']
        let s:makeprg = 'make'
        let g:DeviceIP = 'DLC_PRO__040083'
        let g:DebugRemote = v:true
        let g:GdbPort = '6666'
        let g:GccRoot = "/opt/OSELAS.Toolchain-2012.12.1/arm-cortexa8-linux-gnueabi/gcc-4.7.3-glibc-2.16.0-binutils-2.22-kernel-3.6-sanitized"
        let g:termdebugger = s:oselas_gdb
        let g:SshOpts = '-o ForwardAgent=yes -o ProxyCommand="ssh -o RemoteCommand=none -q -W shg:22 root@%h" -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null -o LogLevel=ERROR'
        let g:SshOpts2 = "-L localhost:1998:localhost:1998 -L localhost:1999:localhost:1999"
        execute 'set path+=' .  g:GccRoot . s:oselas_include
"        let g:ycm_clangd_args = ['-I'.g:GccRoot . s:oselas_include]
    elseif (g:project_type == 'dlcpro-gui')
        let s:Program = '/TOPAS_DLC_pro'
        let s:Elffile = s:Program
        let s:makegoals = []
        let s:makeprg = 'make'
        let g:termdebugger = 'gdb'
        let g:DebugRemote = v:false
    elseif (g:project_type == 'topmode')
        let s:Program = '/topmode'
        let s:Elffile = s:Program
        let g:ProgramRemote = '/usr/toptica/topmode'
        let s:makegoals = []
        let s:makeprg = 'make'
        let g:DeviceIP = 'topmode_stefan'
        let g:DebugRemote = v:true
        let g:GdbPort = '2345'
        let g:GccRoot = "/opt/OSELAS.Toolchain-2011.11.3/arm-cortexa8-linux-gnueabi/gcc-4.6.2-glibc-2.14.1-binutils-2.21.1a-kernel-2.6.39-sanitized"
        let g:termdebugger = s:oselas_gdb
        let g:SshOpts = ""
        let g:SshOpts2 = ""
        execute 'set path+=' .  g:GccRoot . s:oselas_include
        let $PATH = $PATH.':'.g:GccRoot.'/bin'
"        let g:ycm_clangd_args = ['-I'.g:GccRoot . s:oselas_include]
    elseif (g:project_type == 'topmode-gui')
        let s:Program = '/TOPAS_Topmode'
        let s:Elffile = s:Program
        let s:makegoals = []
        let s:makeprg = 'make'
        let g:termdebugger = 'gdb'
        let g:DebugRemote = v:false
    elseif (g:project_type == 'digifalc')
        let s:Program = '/digifalc-image.bin'
        let s:Elffile = '/application/digifalc.elf'
        let s:makegoals = ['firmware-update', 'html-docs', 'doxygen', 'digifalc.elf', 'bootloader.elf']
        let s:makeprg = 'cmake --build . --target'
        let g:DebugRemote = v:true
        let g:GdbPort = '2331'
        let g:GccRoot = '/opt/gcc-arm-none-eabi-8-2019-q3-update'
        let g:termdebugger = g:GccRoot.'/bin/arm-none-eabi-gdb'
        let $PATH = $PATH.':'.g:GccRoot.'/bin'
    elseif (g:project_type == 'servoboard')
        let s:Program = '/servo-board-image.bin'
        let s:Elffile = '/application/servo-board.elf'
        let s:makegoals = ['firmware-update', 'html-docs', 'doxygen', 'servo-board.elf', 'bootloader.elf']
        let s:makeprg = 'cmake --build . --target'
        let g:DebugRemote = v:true
        let g:GdbPort = '2331'
        let g:GccRoot = '/home/stefan/opt/gcc/gcc-arm-none-eabi-8-2019-q3-update'
        let g:termdebugger = g:GccRoot.'/bin/arm-none-eabi-gdb'
        let $PATH = g:GccRoot.'/bin'.':'.$PATH
    elseif (g:project_type == 'dl-motor')
        let s:Program = '/dl-motor-image.bin'
        let s:Elffile = '/application/dl-motor.elf'
        let s:makegoals = ['firmware-update', 'html-docs', 'doxygen', 'dl-motor.elf', 'bootloader.elf']
        let s:makeprg = 'cmake --build . --target'
        let g:DebugRemote = v:true
        let g:GdbPort = '2331'
        let g:GccRoot = '/home/stefan/opt/gcc/gcc-arm-none-eabi-9-2020-q2-update'
        let g:termdebugger = g:GccRoot.'/bin/arm-none-eabi-gdb'
        let $PATH = g:GccRoot.'/bin'.':'.$PATH
    elseif (g:project_type == 'operation-panel-f1')
        let g:stm32_target = 'STM32F10X'
        let s:Program = '/firmware/operation_panel_'.g:stm32_target.'.bin'
        let s:Elffile = '/firmware/operation_panel_'.g:stm32_target.'.elf'
        let s:makegoals = ['artifacts']
        let s:makeprg = 'cmake --build . --target'
        let g:DebugRemote = v:true
        let g:GdbPort = '3333'
        let g:GccRoot = '/home/stefan/opt/gcc/gcc-arm-none-eabi-9-2020-q2-update'
        let g:termdebugger = g:GccRoot.'/bin/arm-none-eabi-gdb'
        let s:stm_cfg = 'stm32f1x'
        let $PATH = g:GccRoot.'/bin'.':'.$PATH
    elseif (g:project_type == 'operation-panel-f4')
        let g:stm32_target = 'STM32F4XX'
        let s:Program = '/firmware/operation_panel_'.g:stm32_target.'.bin'
        let s:Elffile = '/firmware/operation_panel_'.g:stm32_target.'.elf'
        let s:makegoals = ['artifacts']
        let s:makeprg = 'cmake --build . --target'
        let g:DebugRemote = v:true
        let g:GdbPort = '3333'
        let g:GccRoot = '/home/stefan/opt/gcc/gcc-arm-none-eabi-9-2020-q2-update'
        let g:termdebugger = g:GccRoot.'/bin/arm-none-eabi-gdb'
        let s:stm_cfg = 'stm32f4x'
        let $PATH = g:GccRoot.'/bin'.':'.$PATH
    elseif (g:project_type == 'decof')
        let s:makegoals = ['all']
        let s:makeprg = 'cmake --build . --target'
        let g:DebugRemote = v:false
    else
        echo "no project"
    endif

    " editor settings
    set spell spelllang=en,de
    set expandtab
    set cinoptions=l1,g2,h2,N-2,t0,+0,(0,w1,Ws,m1,)100,*100
    set textwidth=120

    " python tags
    execute "set tags+=" . s:ProjectBaseDir . '/tags'

    " compiler
    compiler gcc
    let s:makeopts = ['-j3', 'VERBOSE=1']
    if exists("s:Program")
        let g:Program = g:ProjectBuildDir.s:Program
        let g:Elffile = g:ProjectBuildDir.s:Elffile
    endif
    command! -complete=custom,GetAllMakeCompletions -nargs=* MakeCmd call s:Make('<args>', 0)
    command! MakeTestBuild call s:MakeTestBuild()
    command! -nargs=* Ctest call s:Ctest('<args>', 0)
    command! -nargs=* Ctest1 call s:Ctest1('<args>', 0)
    command! -nargs=* Ctest2 call s:Ctest2('<args>', 0)
    command! -nargs=* Ctest3 call s:Ctest3('<args>', 0)
    command! -nargs=? MakeCheck call s:MakeCheck('<args>', 0)

    " cmake
    command! -nargs=* -complete=custom,CmakeBuildTypes Cmake call s:Cmake('<args>', 0)
    function! CmakeBuildTypes(ArgLead, CmdLine, CorsorPos)
        return join([
                    \'-DCMAKE_BUILD_TYPE=Debug',
                    \'-DCMAKE_BUILD_TYPE=RelWithDebInfo',
                    \'-DHARDWARE_DLCSMART=ON',
                    \'-DMOCK_ALL_CAN_DEVICES=ON',
                    \'-DNEW_COMPILER=ON',
                    \'-DUNIT_TESTING',
                    \], "\n")
    endfunction

    " configure quickfix window for asyncrun
    augroup QuickfixStatus
        autocmd BufWinEnter quickfix setlocal 
                    \ statusline=%t\ [%{g:asyncrun_status}]\ %{exists('w:quickfix_title')?\ '\ '.w:quickfix_title\ :\ ''}\ %=%-15(%l,%c%V%)\ %P
    augroup END

    function! s:DeviceFirmwareUpdate()
        if (g:project_type == 'dlcpro-can')
            call s:DeviceUpdateProgramLinux()
        elseif ((g:project_type == 'digifalc') || (g:project_type == 'servoboard') || (g:project_type == 'dl-motor'))
            call s:JLinkFlashProgram()
        else
            call s:DeviceFirmwareUpdateStartLinux()
        endif
    endfunction

    command! DeviceDebug call s:DeviceDebug(0)
    command! DeviceDebugAttach call s:DeviceDebug(1)
    command! DeviceStartGdbServer call s:DeviceStartGdbServer()
    command! DeviceStartGdbServerAttach call s:DeviceStartGdbServerAttach()

    " YouCompleteMe plugin
    "set completeopt-=preview
    "let g:ycm_add_preview_to_completeopt = 0
    "let g:ycm_autoclose_preview_window_after_completion = 0
    "let g:ycm_autoclose_preview_window_after_insertion = 0
    "let g:ycm_key_previous_completion = ['<TAB>', '<Down>', '<Enter>']
"    let g:ycm_extra_conf_globlist = [
"                \s:ProjectBaseDir.'/.ycm_extra_conf.py',
"                \'!~/tools/vimsuite/vimfiles.YouCompleteMe/*',
"                \]

    " little helpers
    command! -nargs=? BuildDirStash call s:BuildDirStash('<args>')
    command! -complete=custom,GetAllStashedBuildDirs -nargs=? BuildDirUnStash call s:BuildDirUnStash('<args>')
    let $QT_FONT_DPI=96 " Size for qt-application in debugger


endfunction

" ====
" Make
" ====
function GetAllMakeCompletions(ArgLead, CmdLine, CursorPos)
    return join(s:makegoals + s:makeopts + glob(a:ArgLead.'*', 1, 1), "\n")
endfunction

function s:Make(args, async_mode)
    let &makeprg = s:makeprg
    call asyncrun#quickfix_toggle(10, 1)
"    execute 'AsyncRun -mode='.a:async_mode.' -save=2 -program=make @ --directory='.g:ProjectBuildDir.' '.a:args
    execute 'AsyncRun -mode='.a:async_mode.' -save=2 -program=make -cwd='.g:ProjectBuildDir. ' @ '.a:args
endfunction

function s:MakeTestBuild()
    let &makeprg = s:makeprg
    call s:BuildDirStash('save')
    call s:Cmake('-DCMAKE_BUILD_TYPE=Release', 1)
    call s:Make('-j4 device-control artifacts doxygen user-interface', 1)
    call s:BuildDirStash('release-test')
    call s:BuildDirUnStash('save')
endfunction

function s:MakeCheck(filename, async_mode)
    let &makeprg = s:ProjectSrcDir.'/tools/static-check.py'
    call asyncrun#quickfix_toggle(10, 1)
    if a:filename != ''
        let args = '--infile='.a:filename
    else
        let args = '--infile=%'
    endif
    if exists("g:analyser")
        let args .= ' --analyser='.g:analyser
    endif
    execute 'AsyncRun -mode='.a:async_mode.' -save=2 -program=make -cwd='.g:ProjectBuildDir. ' @ '.args
endfunction

function s:Cmake(options, async_mode)
    if !isdirectory(g:ProjectBuildDir)
        call mkdir(g:ProjectBuildDir)
    endif
    execute "!rm -f ".g:ProjectBuildDir."/cmake-cmd*"
    call asyncrun#quickfix_toggle(10, 1)
    let args = ""
    let args .= " ../".g:ProjectSrcDirRel."/"
    if (g:project_type == 'decof')
        let args .= "/targets/simulator"
    endif
    let args .= " --graphviz=dependencies.dot"
    let args .= " ".a:options
    let args .= " -DCMAKE_EXPORT_COMPILE_COMMANDS=1"
    if (g:project_type == 'dlcpro')
        let args .= " -DCMAKE_TOOLCHAIN_FILE=../".g:ProjectSrcDirRel."/Toolchain-target.cmake"
        let args .= " -DBUILD_TARGET=target"
    elseif (g:project_type == 'dlcpro-tui')
        let args .= " -DBUILD_TARGET=simulation"
        let args .= " -DQT5_INSTALL_PATH=/usr/lib/x86_64-linux-gnu/qt5"
    elseif (g:project_type == 'topmode')
        let args .= " -DCMAKE_TOOLCHAIN_FILE=../".g:ProjectSrcDirRel."/Toolchain-target.cmake"
        let args .= " -DSYSROOT=~/topmode/topmode-sdk/sysroot-target"
    elseif (g:project_type == 'topmode-gui')
    elseif ((g:project_type == 'digifalc') || (g:project_type == 'servoboard') || (g:project_type == 'dl-motor'))
        let args .= " -G Ninja"
        let args .= " -DCMAKE_TOOLCHAIN_FILE=../".g:ProjectSrcDirRel."/GNU\\ Arm\\ Embedded.toolchain.cmake"
    elseif ((g:project_type == 'operation-panel-f1') || (g:project_type == 'operation-panel-f4'))
        let args .= " -DCMAKE_TOOLCHAIN_FILE=../".g:ProjectSrcDirRel."/gnu-arm-none-eabi.toolchain.cmake"
        let args .= " -DOPC_TARGET=".g:stm32_target
    elseif (g:project_type == 'decof')
        let args .= " -DCMAKE_TOOLCHAIN_FILE=../".g:ProjectSrcDirRel."/ci/Toolchain-dlcpro-target.cmake"
    endif
    execute '!echo "cmake '.args.'" > ' g:ProjectBuildDir."/cmake_cmd"
    execute 'AsyncRun -mode='.a:async_mode.' -save=2 -cwd='.g:ProjectBuildDir.' @ cmake '.args
endfunction

function s:Ctest(args, async_mode)
    let cmd = 'ctest --build-and-test ../src . --build-generator "Unix Makefiles" --build-target unit_tests --nocompress-output -T Test --build-options -DCMAKE_BUILD_TYPE=Debug --test-command ctest'
    call asyncrun#quickfix_toggle(10, 1)
    execute 'AsyncRun -mode='.a:async_mode.' -save=2 -cwd='.s:ProjectBaseDir.'/unit-tests @ ' . cmd .' '. a:args
endfunction

function s:Ctestx(args, async_mode)
    let unit_test_dirname = 'unit-tests'
    let unit_test_dir = s:ProjectBaseDir . '/' . unit_test_dirname
    if !isdirectory('./unit-tests')
        call mkdir('./unit-tests')
    endif
    let cmake_cmd = 'cmake --build-generator "Unix Makefiles" --build-target unit_tests -DCMAKE_BUILD_TYPE=Debug ../'.g:ProjectSrcDirRel
    let build_cmd = 'cmake --build . -j --target unit_tests'
    let ctest_cmd = 'ctest  --nocompress-output -T Test'
    call asyncrun#quickfix_toggle(10, 1)
    execute 'AsyncRun -mode=1 -save=2 -cwd='.unit_test_dir.' @ ' . cmake_cmd
    execute 'AsyncRun -mode=1 -save=2 -cwd='.unit_test_dir.' @ ' . build_cmd
    execute 'AsyncRun -mode='.a:async_mode.' -save=2 -cwd='.unit_test_dir.' @ ' . ctest_cmd .' '. a:args
endfunction

function s:Ctest1(args, async_mode)
    let unit_test_dirname = 'unit-tests'
    let unit_test_dir = s:ProjectBaseDir . '/' . unit_test_dirname
    if !isdirectory(unit_test_dir)
        call mkdir(unit_test_dir)
    endif
    call asyncrun#quickfix_toggle(10, 1)
    " let cmake_cmd = 'cmake --build-generator "Unix Makefiles" --build-target unit_tests -DCMAKE_BUILD_TYPE=Debug -DUNIT_TESTING=ON ../'.g:ProjectSrcDirRel . ' '. a:args
    let cmake_cmd = 'cmake -DCMAKE_BUILD_TYPE=Debug -DUNIT_TESTING=ON ../'.g:ProjectSrcDirRel . ' '. a:args
    execute 'AsyncRun -mode=1 -save=2 -cwd='.unit_test_dir.' @ ' . cmake_cmd
endfunction

function s:Ctest2(args, async_mode)
    let unit_test_dirname = 'unit-tests'
    let unit_test_dir = s:ProjectBaseDir . '/' . unit_test_dirname
    let build_cmd = 'cmake --build . -j --target unit_tests ' . a:args
    call asyncrun#quickfix_toggle(10, 1)
    execute 'AsyncRun -mode=1 -save=2 -cwd='.unit_test_dir.' @ ' . build_cmd
endfunction

function s:Ctest3(args, async_mode)
    let unit_test_dirname = 'unit-tests'
    let unit_test_dir = s:ProjectBaseDir . '/' . unit_test_dirname
    let ctest_cmd = 'ctest  --nocompress-output -T Test'
    call asyncrun#quickfix_toggle(10, 1)
    execute 'AsyncRun -mode='.a:async_mode.' -save=2 -cwd='.unit_test_dir.' @ ' . ctest_cmd .' '. a:args
endfunction

function s:Call_and_log(cmd)
    echom a:cmd
    let r = system(a:cmd)
    let e = v:shell_error
    if (e != 0)
        echom 'return value: '.e.', output: "'.r.'"'
    endif
    return v:shell_error
endfunction

"function s:CopyFirmware(command)
"    let command = 'bash '.s:GdbSlave.' -h '.g:DeviceIP.' '.a:command
""    if a:command == 'update' || a:command == 'start-debug'
"        let command .= ' '.g:Program
""    endif
"    echom command
"    call system(command)
"endfunction

function s:DlcproEmission(state)
    call s:Call_and_log('ssh -o RemoteCommand=none '.g:SshOpts.' root@'.g:DeviceIP.' "echo '.a:state.' > /sys/bus/i2c/devices/200-0028/emission_button_state"')
endfunction

function s:DlcproShutup()
    call s:Call_and_log('ssh -o RemoteCommand=none '.g:SshOpts.' root@'.g:DeviceIP.' "modprobe -r tam3517_buzzer"')
endfunction

function s:JLinkFlashProgram()
    call term_start(s:ProjectSrcDir.'/flash_firmware.py -j '.s:jlink_path.'/JLinkExe -a 0x8000000 '.g:Program)
endfunction

function s:DeviceUpdateProgramLinux()
    call s:Call_and_log('ssh -o RemoteCommand=none '.g:SshOpts.' root@'.g:DeviceIP.' "killall -q gdbserver start-dc.sh '.fnamemodify(g:ProgramRemote, ':t').'"')
    sleep 2
"    call s:Call_and_log('ssh -o RemoteCommand=none '.g:SshOpts.' root@'.g:DeviceIP.' "killall -q -9 gdbserver start-dc.sh '.g:ProgramRemote.'"')
    call s:Call_and_log('ssh -o RemoteCommand=none '.g:SshOpts.' root@'.g:DeviceIP.' "killall -q -9 gdbserver start-dc.sh '.fnamemodify(g:ProgramRemote, ':t').'"')
    let r = s:Call_and_log('ssh -o RemoteCommand=none '.g:SshOpts.' root@'.g:DeviceIP.' "mount -o rw,remount / && rm -f '.g:ProgramRemote.'" && scp '.g:SshOpts.' "'.g:Program.'" "root@'.g:DeviceIP.':'.g:ProgramRemote.'"')
    return r
endfunction

function s:DeviceFirmwareUpdateStartLinux()
    let r = s:DeviceUpdateProgramLinux()
    if (r == 0)
        let workdir = 'cd /opt/app &&'
        call s:Call_and_log('ssh -o RemoteCommand=none '.g:SshOpts.' -f root@'.g:DeviceIP.' "{ '.workdir.' '.g:ProgramRemote.' 2>&1 | logger -t "'.g:ProgramRemote.'" -p user.err; } &"')
    endif
endfunction

function s:DeviceStartGdbServer()
    if ((g:project_type == 'digifalc') || (g:project_type == 'servoboard') || (g:project_type == 'dl-motor'))
        call s:Call_and_log('pkill --full JLinkGDBServer')
        call s:Call_and_log(s:jlink_path.'/JLinkGDBServer -if SWD -device STM32H743ZI &')
    elseif ((g:project_type == 'operation-panel-f1') || (g:project_type == 'operation-panel-f4'))
        call s:Call_and_log('pkill --full openocd')
        call s:Call_and_log('openocd -f interface/stlink.cfg -f target/'.s:stm_cfg.'.cfg &')
    else
        call s:Call_and_log('pkill --full gdbserver')
        call s:Call_and_log('ssh -o RemoteCommand=none '.g:SshOpts.' root@'.g:DeviceIP.' "killall -q -9 gdbserver start-dc.sh '.fnamemodify(g:ProgramRemote, ':t').'"')
        call s:Call_and_log('ssh -o RemoteCommand=none '.g:SshOpts.' -L localhost:'.g:GdbPort.':localhost:'.g:GdbPort.' "root@'.g:DeviceIP.'" '.g:SshOpts2.' gdbserver --multi localhost:'.g:GdbPort.' &')
    endif
endfunction

function s:DeviceStartGdbServerAttach()
    if ((g:project_type == 'digifalc') || (g:project_type == 'servoboard') || (g:project_type == 'dl-motor'))
    else
        call s:Call_and_log('pkill --full gdbserver')
        call s:Call_and_log('ssh -o RemoteCommand=none '.g:SshOpts.' -L localhost:'.g:GdbPort.':localhost:'.g:GdbPort.' "root@'.g:DeviceIP.'" '.g:SshOpts2.' "gdbserver localhost:'.g:GdbPort.' --attach \`pidof '.fnamemodify(g:ProgramRemote, ':t').'\` &"')
    endif
endfunction

function s:SendToTerm(command)
    call term_sendkeys('', a:command . "\n")
endfunction

function s:SendToConque(command)
    execute 'ConqueGdbCommand ' . a:command
endfunction

function s:SendToDebugger(command)
    if !exists("s:UseConqueGdb")
        call s:SendToTerm(a:command)
    else
        call s:SendToConque(a:command)
    endif
endfunction

function s:StartDebugger(elffile, attach)
    if !exists("s:UseConqueGdb")
        if (a:attach == 0)
            Termdebug
        else
            execute 'Termdebug ' . a:elffile
        endif
    else
        echom 'Using ConqueGdb'
        if (a:attach == 0)
            ConqueGdbTab
        else
            execute 'ConqueGdbTab '.a:elffile
        endif
    endif
endfunction

let g:DlcproBasePath = "/jenkins/workspace/pro--firmware_release_1.9.0-DCESJ5C5R577IG5QFEWTML22UFDDZCJDGFLMDA4DCD3V2ZAGVEJA/source/"
function s:DeviceDebug(attach)
    if (a:attach == 0)
"        let r = s:DeviceFirmwareUpdate()
        let r = 0
        if (r != 0)
            echoerr "DeviceFirmwareUpdate() failed!"
        else
            if (g:DebugRemote == v:false)
                call s:StartDebugger(g:Elffile, a:attach)
                call s:SendToDebugger('file '.g:Elffile)
                call s:SendToDebugger('break main')
                call s:SendToDebugger('run')
            else
                call s:DeviceStartGdbServer()
                sleep 1
                call s:StartDebugger(g:Elffile, a:attach)
                call s:SendToDebugger('target extended-remote localhost:'.g:GdbPort)
                if exists("g:ProgramRemote")
                    call s:SendToDebugger('set remote exec-file '.g:ProgramRemote)
                endif
                call s:SendToDebugger('file '.g:Elffile)
                if ((g:project_type == 'operation-panel-f1') || (g:project_type == 'operation-panel-f4'))
                    call s:SendToDebugger('y')
                    call s:SendToDebugger('monitor reset')
                    call s:SendToDebugger('load')
                    call s:SendToDebugger('break main')
                else
                    call s:SendToDebugger('break main')
                    call s:SendToDebugger('run')
                endif
                execute '2hide'
            endif
        endif
    else
        call s:DeviceStartGdbServerAttach()
        call s:StartDebugger(a:attach)
        call s:SendToDebugger('target remote localhost:'.g:GdbPort)
        " get remote src path with gdb: info sources or gdb: break main
        call s:SendToDebugger('set substitute-path '.g:DlcproBasePath.' '.s:ProjectSrcDir)
    endif

"    call s:SendToDebugger('set solib-search-path '.g:GccRoot.'/arm-cortexa8-linux-gnueabi/lib/'.':'.g:GccRoot.'/sysroot-arm-cortexa8-linux-gnueabi/lib/'.':'.g:GccRoot.'/sysroot-arm-cortexa8-linux-gnueabi/usr/lib/')
"    call s:SendToDebugger('set can-use-hw-watchpoints 0')
endfunction

" ================
" Regression Tests
" ================
function g:DlcproRegtestCmd(arguments)
    return s:DlcproRegtestCmd('',              '',            '0', '1', '',          '--capture=no',                  a:arguments)
endfunction
function g:DlcproRegtestDlProCmd(arguments)
    return s:DlcproRegtestCmd('dlc_pro__040011', 'elab-dlcpro2', '2', '1', 'DLpro',     '',                              a:arguments)
endfunction
function g:DlcproRegtestTaProCmd(arguments)
    return s:DlcproRegtestCmd('dlc_pro_020372',  'elab-dlcpro2', '3', '1', 'TApro',     '-m "not usb and not usbstick"', a:arguments)
endfunction
function g:DlcproRegtestCtlCmd(arguments)
    return s:DlcproRegtestCmd('dlc_pro__040120', 'elab-dlcpro2', '1', '1', 'CTL',       '-m "not usb and not usbstick"', a:arguments)
endfunction
function g:DlcproRegtestDualDlCmd(arguments)
    return s:DlcproRegtestCmd('dlc_pro_041299', 'elab-dlcpro2', '4', '2', 'DLpro',     '-m "not usb and not usbstick"', a:arguments)
endfunction
function g:DlcproRegtestDualDl1Cmd(arguments)
    return s:DlcproRegtestCmd('dlc_pro_041299', 'elab-dlcpro2', '4', '1', 'DLpro',     '-m "not usb and not usbstick"', a:arguments)
endfunction
function g:DlcproRegtestShgProCmd(arguments)
    return s:DlcproRegtestCmd('dlc_pro_041355', 'elab-dlcpro2', '7', '1', 'TA-SHGpro', '-m "not usb and not usbstick"', a:arguments)
endfunction

command -nargs=1 -complete=file DlcproRegtest        call s:DlcproRegtest(g:DlcproRegtestCmd('<args>'))
command -nargs=1 -complete=file DlcproRegtestDlPro   call s:DlcproRegtest(g:DlcproRegtestDlProCmd('<args>'))
command -nargs=1 -complete=file DlcproRegtestTaPro   call s:DlcproRegtest(g:DlcproRegtestTaProCmd('<args>'))
command -nargs=1 -complete=file DlcproRegtestCtl     call s:DlcproRegtest(g:DlcproRegtestCtlCmd('<args>'))
command -nargs=1 -complete=file DlcproRegtestDualDl  call s:DlcproRegtest(g:DlcproRegtestDualDl1Cmd('<args>'))
command -nargs=1 -complete=file DlcproRegtestDualDl1 call s:DlcproRegtest(g:DlcproRegtestDualDl1Cmd('<args>'))
command -nargs=1 -complete=file DlcproRegtestShgPro  call s:DlcproRegtest(g:DlcproRegtestShgProCmd('<args>'))

let g:DlcproRegtest_fast_restart = 1
"let g:DlcproRegtest_marks = '"-m (no_crash_without_mc_fpga and not usb and not usbstick and not si1 and not servo_control and not eom and not cavity and not cell_spectroscopy)"'
let g:DlcproRegtest_marks = ""
let g:DlcproRegtest_lasertype = "DLpro"
let g:DlcproRegtest_powerswitch_ip = ""

function s:DlcproRegtestCmd(ip, powerswitch_ip, powerplug, laser_count, laser_type, opts, arguments)
    if (a:ip == '')
        let ip = g:DeviceIP
    else
        let ip = a:ip
    endif
    if (a:laser_type == '')
        let laser_type = g:DlcproRegtest_lasertype
    else
        let laser_type = a:laser_type
    endif
    if (a:powerswitch_ip == '')
        let powerswitch_ip = g:DlcproRegtest_powerswitch_ip
    else
        let powerswitch_ip = a:powerswitch_ip
    endif

    let archive_dir = g:ProjectBuildDir."/artifacts"
    let license_builddir = s:ProjectBaseDir.'/build.license'
    let licensetool = license_builddir."/dlcprolicense-tool/dlcprolicense-tool"
    let $JENKINS_URL = g:jenkins_url

    let test_cmd =
                \s:ProjectSrcDir."/test/python-env/bin/python -u -m pytest ".
                \"--showlocals --tb=long --verbose --cache-clear ".
                \"-o junit_family=xunit1 ".
                \"--junit-xml=regtest.".laser_type.".xml ".
                \"--debug_build ".
                \"--laser_count=".a:laser_count." ".
                \"--laser1_type=".laser_type." ".
                \"--log-file=regtest.".laser_type.".log ".
                \"--target_ip=".ip." ".
                \"--powerswitch_ip=".powerswitch_ip." ".
                \"--powerswitch_passwd=nimda ".
                \"--power_plug=".a:powerplug." ".
                \"--power_plug_fan=8"." ".
                \"--version_file=".archive_dir."/VERSION ".
                \"--firmware_file=".archive_dir."/DLCpro-archive.fw ".
                \"--license_tool=".licensetool." ".
                \"--license_keyfile=".s:ProjectSrcDir."/license/libdlcprolicense/rsa-private.key ".
                \"--skip_shutdown_after_test ".
                \"--skip_fw_update ".
                \"--log-file-level=DEBUG "
                \""

    if (g:DlcproRegtest_fast_restart == 1)
        let test_cmd .= "--fast_restart "
    endif

    " hint: --collect-only
    "--log-cli-level=DEBUG --log-file-level=DEBUG

    let test_cmd .= a:opts." ". g:DlcproRegtest_marks." ".a:arguments
    let @+ = test_cmd
    return test_cmd
endfunction

function s:DlcproRegtest(test_cmd)
    execute "wa"

    " Build license tool
    let license_builddir = s:ProjectBaseDir.'/build.license'
    let licensetool = license_builddir."/libdlcprolicense/dlcprolicense-tool"
    let license_cmake = "cmake -DLICENSE_TOOL=1 -DCMAKE_BUILD_TYPE=Release ".s:ProjectSrcDir."/license"
    let license_make = "make dlcprolicense-tool"
    if !executable(licensetool)
        call mkdir(license_builddir, "p")
        call term_start(license_cmake, {'cwd' : license_builddir})
        sleep 2
        call term_start(license_make, {'cwd' : license_builddir})
        sleep 5
    endif

    " Build command
    echom a:test_cmd

    " Execute pytest
    execute "terminal ++shell cd " s:ProjectSrcDir. "/test && " . a:test_cmd
"    call term_start(a:test_cmd, {'cwd' : s:ProjectSrcDir."/test"})
endfunction

" -------------
" YouCompleteMe
" -------------
"let g:ycm_max_diagnostics_to_display = 1000
"let g:ycm_clangd_uses_ycmd_caching = 0
"let g:ycm_cache_omnifunc = 0
"let g:ycm_filter_diagnostics = {
"            \ "cpp": {
"            \   "regex": [
"            \       "'auto_ptr<boost::signals2::detail::foreign_weak_ptr_impl_base>' is deprecated",
"            \       "'boost/tuple.hpp' file not found",
"            \       "no template named 'tuple' in namespace 'boost'",
"            \       "no matching function for call to 'throw_exception'",
"            \       "variable templates are a C\\+\\+14 extension",
"            \       "inline variables are a C\\+\\+17 extension",
"            \       "expected ',' or '>' in template-parameter-list",
"            \       "expected a qualified name after 'typename'",
"            \       "expected ';' at end of declaration list",
"            \       "'std::unordered_set::_Hashtable' \\(aka 'int'\\) is not a class, namespace, or enumeration",
"            \       "no template named '__uset_hashtable'",
"            \   ],
"            \ }
"            \}

" ===============
" Stash / Unstash
" ===============
function s:BuildDirStash(suffix)
    if a:suffix != ''
        let suffix = a:suffix
    else
        let suffix = FugitiveHead()
    endif
    let target_dir = g:ProjectBuildDir.'.'.suffix
    let subsuffix = 1
    while isdirectory(target_dir)
        let target_dir = g:ProjectBuildDir.'.'.suffix.'.'.subsuffix
        let subsuffix += 1
    endwhile
    call rename(g:ProjectBuildDir, target_dir)

    " create new build dir and copy eclipse files
    call mkdir(g:ProjectBuildDir)
    call execute('!cp '.target_dir.'/.cproject '.g:ProjectBuildDir, 'silent!')
    call execute('!cp '.target_dir.'/.project '.g:ProjectBuildDir, 'silent!')
endfunction

function s:BuildDirUnStash(suffix)
    if a:suffix != ''
        let suffix = a:suffix
    else
        let suffix = fugitive#head()
    endif
    let source_dir = g:ProjectBuildDir.'.'.suffix
    if !isdirectory(source_dir)
        echoerr 'source directory '.source_dir.' not found'
    elseif isdirectory(g:ProjectBuildDir) && !empty(globpath(g:ProjectBuildDir, '*', 0, 1))
        echoerr 'target directory '.g:ProjectBuildDir.' exists and is not empty'
    else
        echom 'restore '.source_dir.' to '.g:ProjectBuildDir
        call delete(expand(g:ProjectBuildDir), 'rf')
        call rename(expand(source_dir), expand(g:ProjectBuildDir))
    endif
endfunction

function GetAllStashedBuildDirs(ArgLead, CmdLine, CursorPos)
    return system('ls '.s:ProjectBaseDir.' | grep build.')
endfunction

" update PDH-firmware
"/opt/app/bin/jamplayer -sm3 -aconfigure PDD.jam
"/opt/app/bin/jamplayer -sm3 -aprogram PDD.jam
"/opt/app/bin/jamplayer -sm3 -areconfigure /opt/app/fpga-configurations/reconfigure.jam

" read/write eeprom
"/opt/app/bin/eepromio

" update python tags
" cd ~/dclpro/firmware
" ctags --recurse --languages=python src

" ------
" Pandoc
" ------
command TopticaBeamer execute('!pandoc -s -t beamer -H toptica-style.tex -o %:r.pdf %')

" -----------------
" Jenkins validator
" -----------------
function s:Jenkins_linter(jenkinsfile)
    if a:jenkinsfile == ''
        let jenkinsfile = expand("%")
    else
        let jenkinsfile = a:jenkinsfile
    endif

    if !filereadable(jenkinsfile)
        echoerr("File not found: ".jenkinsfile)
        return
    endif

    if !exists("s:user")
        let s:user = input("jenkins-user:")
    endif
    if !exists("s:passwd")
        let s:passwd = inputsecret("password:")
        echo "\n"
    endif
    let user_pass = s:user.":".s:passwd

    let l:out = system("curl --no-progress-meter --user '".user_pass."' -X POST -F 'jenkinsfile=<".jenkinsfile."' ".g:jenkins_url."/pipeline-model-converter/validate")
    if match(l:out, "HTTP ERROR 401") >= 0
        " Forget credentials on authorization error
        unlet s:user
        unlet s:passwd
    endif
    echo l:out
endfunction
