package main

import (
	"fmt"
	"io"
	"log"
	"os"
	"os/exec"
	"path/filepath"
	"strconv"

	"github.com/cespare/xxhash"
)

func buildBootISO(out io.Writer, ctx *renderContext) (err error) {
	tempDir, err := os.MkdirTemp("/tmp", "iso-v2-")
	if err != nil {
		return
	}

	defer os.RemoveAll(tempDir)

	buildRes := func(build func(out io.Writer, ctx *renderContext) error, dst string) (err error) {
		log.Printf("iso-v2: building %s", dst)

		outPath := filepath.Join(tempDir, dst)

		if err = os.MkdirAll(filepath.Dir(outPath), 0755); err != nil {
			return err
		}

		out, err := os.Create(outPath)
		if err != nil {
			return err
		}
		defer out.Close()

		err = build(out, ctx)
		if err != nil {
			return
		}

		return err
	}

	err = func() (err error) {
		// grub

		if err = os.MkdirAll(filepath.Join(tempDir, "grub"), 0755); err != nil {
			return
		}

		// create a tag file
		bootstrapBytes, err := ctx.BootstrapConfig()
		if err != nil {
			return
		}

		h := xxhash.New()
		fmt.Fprintln(h, ctx.Host.Kernel)
		h.Write(bootstrapBytes)

		tag := "dkl-" + strconv.FormatUint(h.Sum64(), 32) + ".tag"

		f, err := os.Create(filepath.Join(tempDir, tag))
		if err != nil {
			return
		}
		f.Write([]byte("direktil marker file\n"))
		f.Close()

		err = os.WriteFile(filepath.Join(tempDir, "grub", "grub.cfg"), []byte(`
search --set=root --file /`+tag+`

insmod all_video
set timeout=3

menuentry "Direktil" {
    linux  /vmlinuz `+ctx.CmdLine+`
    initrd /initrd
}
`), 0644)
		if err != nil {
			return
		}

		coreImgPath := filepath.Join(tempDir, "grub", "core.img")
		grubCfgPath := filepath.Join(tempDir, "grub", "grub.cfg")

		cmd := exec.Command("grub-mkstandalone",
			"--format=i386-pc",
			"--output="+coreImgPath,
			"--install-modules=linux normal iso9660 biosdisk memdisk search tar ls",
			"--modules=linux normal iso9660 biosdisk search",
			"--locales=",
			"--fonts=",
			"boot/grub/grub.cfg="+grubCfgPath,
		)
		cmd.Stdout = os.Stdout
		cmd.Stderr = os.Stderr
		if err := cmd.Run(); err != nil {
			return err
		}

		defer os.Remove(coreImgPath)
		defer os.Remove(grubCfgPath)

		out, err := os.Create(filepath.Join(tempDir, "grub", "bios.img"))
		if err != nil {
			return err
		}

		defer out.Close()

		b, err := os.ReadFile("/usr/lib/grub/i386-pc/cdboot.img")
		if err != nil {
			return err
		}

		if _, err := out.Write(b); err != nil {
			return err
		}

		b, err = os.ReadFile(coreImgPath)
		if err != nil {
			return err
		}

		if _, err := out.Write(b); err != nil {
			return err
		}

		return nil
	}()
	if err != nil {
		return err
	}

	// kernel and initrd
	buildRes(fetchKernel, "vmlinuz")
	buildRes(buildInitrd, "initrd")

	// build the ISO
	mkisofs, err := exec.LookPath("genisoimage")
	if err != nil {
		mkisofs, err = exec.LookPath("mkisofs")
	}
	if err != nil {
		return err
	}

	cmd := exec.Command(mkisofs,
		"-quiet",
		"-joliet",
		"-joliet-long",
		"-rock",
		"-translation-table",
		"-no-emul-boot",
		"-boot-load-size", "4",
		"-boot-info-table",
		"-eltorito-boot", "grub/bios.img",
		"-eltorito-catalog", "grub/boot.cat",
		tempDir,
	)
	cmd.Stdout = out
	cmd.Stderr = os.Stderr

	return cmd.Run()
}
