name: CI

on: [push, pull_request]

concurrency:
  group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.run_id }}
  cancel-in-progress: true

jobs:
  clang-format:
    name: 'clang-format'
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - name: Run clang-format
        run: |
          find CONFIG LEGO1 ISLE miniwin -iname '*.h' -o -iname '*.cpp' | xargs \
          pipx run "clang-format>=17,<18" \
            --style=file \
            -i
      - name: Check diff
        run: |
          git diff --exit-code

  build:
    name: ${{ matrix.name }}
    runs-on: ${{ matrix.os }}
    container: ${{ matrix.container || '' }}
    defaults:
      run:
        shell: ${{ matrix.shell || 'sh' }}

    strategy:
      fail-fast: false
      matrix:
        include:
          - { name: 'Linux',          os: 'ubuntu-latest',  dx5: false, config: true,  linux: true, werror: true,   clang-tidy: true }
          - { name: 'MSVC (x86)',     os: 'windows-latest', dx5: true,  config: false, msvc: true,  werror: false,  clang-tidy: false,  vc-arch: 'amd64_x86' }
          - { name: 'MSVC (x64)',     os: 'windows-latest', dx5: false, config: true, msvc: true,  werror: false,  clang-tidy: false,  vc-arch: 'amd64' }
          - { name: 'MSVC (arm64)',   os: 'windows-latest', dx5: false, config: false, msvc: true,  werror: false,  clang-tidy: false,  vc-arch: 'amd64_arm64' }
          - { name: 'msys2 mingw32',  os: 'windows-latest', dx5: false, config: false, mingw: true, werror: true,   clang-tidy: true,   msystem: 'mingw32', msys-env: 'mingw-w64-i686',   shell: 'msys2 {0}' }
          - { name: 'msys2 mingw64',  os: 'windows-latest', dx5: false, config: true,  mingw: true, werror: true,   clang-tidy: true,   msystem: 'mingw64', msys-env: 'mingw-w64-x86_64', shell: 'msys2 {0}' }
          - { name: 'macOS',          os: 'macos-latest',   dx5: false, config: true,  brew: true,  werror: true,   clang-tidy: false }
          - { name: 'Emscripten',     os: 'ubuntu-latest',  dx5: false, config: false, emsdk: true, werror: true,   clang-tidy: false,  cmake-wrapper: 'emcmake' }
          - { name: 'Nintendo 3DS',   os: 'ubuntu-latest',  dx5: false, config: false, n3ds: true,  werror: true,   clang-tidy: false,  container: 'devkitpro/devkitarm:latest', cmake-args: '-DCMAKE_TOOLCHAIN_FILE=/opt/devkitpro/cmake/3DS.cmake' }
    steps:
      - name: Setup vcvars
        if: ${{ !!matrix.msvc }}
        uses: ilammy/msvc-dev-cmd@v1
        with:
          arch: ${{ matrix.vc-arch }}

      - name: Set up MSYS2
        if: ${{ !!matrix.msystem }}
        uses: msys2/setup-msys2@v2
        with:
          msystem: ${{ matrix.msystem }}
          install: >-
            ${{ matrix.msys-env }}-cc
            ${{ matrix.msys-env }}-cmake
            ${{ matrix.msys-env }}-ninja
            ${{ matrix.msys-env }}-clang-tools-extra
            ${{ (matrix.config && format('{0}-qt6-base', matrix.msys-env)) || '' }}
      - name: Install Qt
        if: ${{ !!matrix.msvc && matrix.config }}
        uses: jurplel/install-qt-action@v4
        with:
          cache: 'true'          

      - name: Install Linux dependencies (apt-get)
        if: ${{ matrix.linux }}
        run: |
          sudo apt-get update
          sudo apt-get install -y \
            libx11-dev libxext-dev libxrandr-dev libxrender-dev libxfixes-dev libxi-dev libxinerama-dev \
            libxcursor-dev libwayland-dev libxkbcommon-dev wayland-protocols libgl1-mesa-dev qt6-base-dev \
            libasound2-dev qt6-xdgdesktopportal-platformtheme

      - name: Install macOS dependencies (brew)
        if: ${{ matrix.brew }}
        run: |
          brew update
          brew install cmake ninja llvm qt6
          echo "LLVM_ROOT=$(brew --prefix llvm)/bin" >> $GITHUB_ENV

      - name: Setup Emscripten
        uses: mymindstorm/setup-emsdk@master
        if: ${{ matrix.emsdk }}

      - name: Setup ninja
        if: ${{ matrix.msvc }}
        uses: ashutoshvarma/setup-ninja@master

      - uses: actions/checkout@v4

      - name: Configure (CMake)
        run: |
          ${{ matrix.cmake-wrapper || '' }} cmake -S . -B build -GNinja \
            ${{ matrix.cmake-args || '' }} \
            -DCMAKE_BUILD_TYPE=Release \
            -DISLE_USE_DX5=${{ !!matrix.dx5 }} \
            -DISLE_BUILD_CONFIG=${{ !!matrix.config }} \
            -DENABLE_CLANG_TIDY=${{ !!matrix.clang-tidy  }} \
            -DISLE_WERROR=${{ !!matrix.werror }} \
            -DISLE_DEBUG=OFF \
            -Werror=dev

      - name: Build (CMake)
        run: cmake --build build --verbose

      - name: Package (CPack)
        if: ${{ !matrix.n3ds }}
        run: |
          cd build
          cpack .

      - name: Install linuxdeploy
        if: ${{ matrix.linux }}
        id: install-linuxdeploy
        uses: miurahr/install-linuxdeploy-action@v1.8.0
        with:
          plugins: qt appimage

      - name: Package (AppImage)
        if: ${{ matrix.linux }}
        run: |
          cd build && \
          export LD_LIBRARY_PATH=".:$LD_LIBRARY_PATH" && \
          NO_STRIP=1 ${{ steps.install-linuxdeploy.outputs.linuxdeploy }} \
            -p qt \
            -e isle \
            -e isle-config \
            -d packaging/linux/org.legoisland.Isle.desktop \
            -i icons/org.legoisland.Isle.svg \
            --custom-apprun=../packaging/linux/appimage/AppRun \
            --appdir packaging/linux/appimage/AppDir \
            --output appimage && \
          mv *.AppImage dist/

      - name: Package (3DS)
        if: ${{ matrix.n3ds }}
        run: |
          cd build
          mkdir dist
          mv *.3dsx dist/

      - name: Upload Build Artifacts
        uses: actions/upload-artifact@v4
        with:
          name: '${{ matrix.name }}'
          path: |
            build/dist/isle-*
            build/dist/*.AppImage
            build/dist/*.3dsx

  flatpak:
    name: "Flatpak (${{ matrix.arch }})"
    runs-on: ${{ matrix.os }}

    strategy:
      fail-fast: false
      matrix:
        include:
          - arch: x86_64
            os: ubuntu-latest

          - arch: aarch64
            os: ubuntu-22.04-arm

    container:
      image: ghcr.io/flathub-infra/flatpak-github-actions:kde-6.8
      options: --privileged

    steps:
      - uses: actions/checkout@v4

      - name: Build Flatpak
        uses: flatpak/flatpak-github-actions/flatpak-builder@v6
        with:
          bundle: org.legoisland.Isle.${{ matrix.arch }}.flatpak
          manifest-path: packaging/linux/flatpak/org.legoisland.Isle.json
          arch: ${{ matrix.arch }}

  ncc:
    name: 'C++'
    runs-on: ubuntu-latest

    steps:
      - uses: actions/checkout@v4

      - name: Install LLVM and Clang
        uses: KyleMayes/install-llvm-action@v1
        with:
          version: "16"

      - name: Install python libraries
        run: |
          pip install -r tools/requirements.txt

      - name: Run ncc
        run: |
          action_headers=$(find LEGO1/lego/legoomni/include/actions \
            -name '*.h' -print0 | xargs -0 echo)

          python3 tools/ncc/ncc.py \
            --clang-lib ${{ env.LLVM_PATH }}/lib/libclang.so \
            --recurse \
            --style tools/ncc/ncc.style \
            --skip tools/ncc/skip.yml \
            --definition WINAPI FAR BOOL CALLBACK HWND__=HWND SDLCALL \
            --include \
              util \
              LEGO1 \
              LEGO1/omni/include \
              LEGO1/lego/legoomni/include \
              LEGO1/lego/sources \
            --exclude \
              LEGO1/omni/include/flic.h \
              LEGO1/omni/src/video/flic.cpp \
              $action_headers \
            --path LEGO1/omni LEGO1/lego/legoomni

  release:
    name: 'Release'
    if: ${{ github.event_name == 'push' && github.ref_name == 'master' }}
    runs-on: ubuntu-latest
    needs:
      - build
      - flatpak
    steps:
      - name: Download All Artifacts
        uses: actions/download-artifact@main
        with:
          pattern: "*"
          path: Release
          merge-multiple: true

      - name: Checkout uploadtool
        uses: actions/checkout@v4
        with:
          repository: 'probonopd/uploadtool'
          path: 'uploadtool'

      - name: Upload Continuous Release
        env:
          GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
        run: |
          ./uploadtool/upload.sh Release/*
