import Lake
open System Lake DSL

package LeanDoomed

def sdlGitRepo : String := "https://github.com/libsdl-org/SDL.git"
def sdlRepoDir : String := "vendor/SDL"

def sdlImageGitRepo : String := "https://github.com/libsdl-org/SDL_image.git"
def sdlImageRepoDir : String := "vendor/SDL_image"

-- clone from a stable branch to avoid breakages
def sdlBranch : String := "release-3.2.x"

input_file sdl.c where
  path := "c" / "sdl.c"
  text := true

target sdl.o pkg : FilePath := do
  let srcJob ← sdl.c.fetch
  let oFile := pkg.buildDir / "c" / "sdl.o"
  let leanInclude := (<- getLeanIncludeDir).toString
  let sdlInclude := "vendor/SDL/include/"
  let sdlImageInclude := "vendor/SDL_image/include/"
  buildO oFile srcJob #[] #["-fPIC", s!"-I{sdlInclude}", s!"-I{sdlImageInclude}", "-D_REENTRANT", s!"-I{leanInclude}"] "cc"

target libleansdl pkg : FilePath := do
-- Helper function to run command and handle errors
-- Clone the repos if they don't exist
  let sdlExists ← System.FilePath.pathExists sdlRepoDir
  if !sdlExists then
    IO.println "Cloning SDL"
    let sdlClone ← IO.Process.output { cmd := "git", args := #["clone", "-b", sdlBranch, "--single-branch", "--depth", "1", "--recursive", sdlGitRepo, sdlRepoDir] }
    if sdlClone.exitCode != 0 then
      IO.println s!"Error cloning SDL: {sdlClone.stderr}"
    else
      IO.println "SDL cloned successfully"
      IO.println sdlClone.stdout

  let sdlImageExists ← System.FilePath.pathExists sdlImageRepoDir
  if !sdlImageExists then
    IO.println "Cloning SDL_image"
    let sdlImageClone ← IO.Process.output { cmd := "git", args := #["clone", "-b", sdlBranch, "--single-branch", "--depth", "1", "--recursive", sdlImageGitRepo, sdlImageRepoDir] }
    if sdlImageClone.exitCode != 0 then
      IO.println s!"Error cloning SDL_image: {sdlImageClone.stderr}"
    else
      IO.println "SDL_image cloned successfully"
      IO.println sdlImageClone.stdout

-- Build the repos with cmake
-- SDL itself needs to be built before SDL_image, as the latter depends on the former
-- We also need to make sure we are using a system provided C compiler, as the one that comes with Lean is missing important headers
  IO.println "Building SDL"
-- Create build directory if it doesn't exist
  let sdlBuildDirExists ← System.FilePath.pathExists (sdlRepoDir ++ "/build")
  if !sdlBuildDirExists then
    let configureSdlBuild ← IO.Process.output { cmd := "cmake", args := #["-S", sdlRepoDir, "-B", sdlRepoDir ++ "/build", "-DBUILD_SHARED_LIBS=ON", "-DCMAKE_BUILD_TYPE=Release", "-DCMAKE_C_COMPILER=cc"] }
    if configureSdlBuild.exitCode != 0 then
      IO.println s!"Error configuring SDL: {configureSdlBuild.stderr}"
    else
      IO.println "SDL configured successfully"
      IO.println configureSdlBuild.stdout
  else
    IO.println "SDL build directory already exists, skipping configuration step"
-- now actually build SDL once we've configured it
  let buildSdl ← IO.Process.output { cmd := "cmake", args :=  #["--build", sdlRepoDir ++ "/build", "--config", "Release",] }
  if buildSdl.exitCode != 0 then
    IO.println s!"Error building SDL: {buildSdl.exitCode}"
    IO.println buildSdl.stderr
  else
    IO.println "SDL built successfully"
    IO.println buildSdl.stdout
-- Build SDL_Image
  IO.println "Building SDL_image"
-- Create SDL_Image build directory if it doesn't exist
  let sdlImageBuildDirExists ← System.FilePath.pathExists (sdlImageRepoDir ++ "/build")
  if !sdlImageBuildDirExists then
    let currentDir ← IO.currentDir
    let sdlConfigPath := currentDir / sdlRepoDir / "build"
    let configureSdlImageBuild ← IO.Process.output { cmd := "cmake", args :=  #["-S", sdlImageRepoDir, "-B", sdlImageRepoDir ++ "/build", s!"-DSDL3_DIR={sdlConfigPath}", "-DBUILD_SHARED_LIBS=ON", "-DCMAKE_BUILD_TYPE=Release", "-DCMAKE_C_COMPILER=cc"] }
    if configureSdlImageBuild.exitCode != 0 then
      IO.println s!"Error configuring SDL_image: {configureSdlImageBuild.stderr}"
    else
      IO.println "SDL_image configured successfully"
      IO.println configureSdlImageBuild.stdout
  else
    IO.println "SDL_image build directory already exists, skipping configuration step"
-- now actually build SDL_image once we've configured it
  let buildSdlImage ← IO.Process.output { cmd := "cmake", args := #["--build", sdlImageRepoDir ++ "/build", "--config", "Release"] }
  if buildSdlImage.exitCode != 0 then
    IO.println s!"Error building SDL_image: {buildSdlImage.stderr}"
  else
    IO.println "SDL_image built successfully"
    IO.println buildSdlImage.stdout

  let sdlO ← sdl.o.fetch
  let name := nameToStaticLib "leansdl"
  -- manually copy the DLLs we need to .lake/build/bin/ for the game to work
  IO.FS.createDirAll ".lake/build/bin/"
  let dstDir := ".lake/build/bin/"
  let sdlBinariesDir : FilePath := "vendor/SDL/build/"
  for entry in (← sdlBinariesDir.readDir) do
    if entry.path.extension != none then
      copyFile entry.path (dstDir / entry.path.fileName.get!)
  let sdlImageBinariesDir : FilePath := "vendor/SDL_image/build/"
  for entry in (← sdlImageBinariesDir.readDir) do
    if entry.path.extension != none then
      copyFile entry.path (dstDir / entry.path.fileName.get!)
  if Platform.isWindows then
    -- binaries for Lean/Lake itself for the executable to run standalone
    let lakeBinariesDir := (← IO.appPath).parent.get!
    println! "Copying Lake DLLs from {lakeBinariesDir}"

    for entry in (← lakeBinariesDir.readDir) do
      if entry.path.extension == some "dll" then
       copyFile entry.path (".lake/build/bin/" / entry.path.fileName.get!)
  else
  -- binaries for Lean/Lake itself - only copy Lean-specific libraries, not all Nix libs
    let leanLibDir := (← IO.appPath).parent.get!.parent.get! / "lib" / "lean"
    let leanLibDirExists ← System.FilePath.pathExists leanLibDir
    if leanLibDirExists then
      println! "Copying Lean libraries from {leanLibDir}"
      for entry in (← leanLibDir.readDir) do
        if entry.path.extension == some "so" then
          copyFile entry.path (".lake/build/bin/" / entry.path.fileName.get!)

  buildStaticLib (pkg.staticLibDir / name) #[sdlO]

lean_lib SDL where
  moreLinkObjs := #[libleansdl]
  moreLinkArgs := #["-lSDL3", "-lSDL3_image"]

lean_lib Engine

@[default_target]
lean_exe LeanDoomed where
  root := `Main
  -- we have to add the rpath to tell the compiler where all of the libraries are
  moreLinkArgs := if Platform.isWindows then
    #["vendor/SDL/build/SDL3.dll", "vendor/SDL_image/build/SDL3_image.dll"]
  else
    #["vendor/SDL/build/libSDL3.so", "vendor/SDL_image/build/libSDL3_image.so", "-Wl,--allow-shlib-undefined", "-Wl,-rpath=$ORIGIN"]
