package main

import (
	"fmt"
	"io"
	"io/ioutil"
	"log"
	"os"
	"os/exec"
	"path/filepath"
)

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

	defer os.RemoveAll(tempDir)

	cp := func(src, dst string) error {
		log.Printf("iso: adding %s as %s", src, dst)
		in, err := os.Open(src)
		if err != nil {
			return err
		}

		defer in.Close()

		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 = io.Copy(out, in)
		return err
	}

	err = func() error {
		// grub

		if err := os.MkdirAll(filepath.Join(tempDir, "grub"), 0755); err != nil {
			return err
		}
		err = ioutil.WriteFile(filepath.Join(tempDir, "grub", "grub.cfg"), []byte(`
search --set=root --file /config.yaml

insmod all_video
set timeout=3

menuentry "Direktil" {
    linux  /vmlinuz direktil.boot=DEVNAME=sr0 direktil.boot.fs=iso9660
    initrd /initrd
}
`), 0644)
		if err != nil {
			return err
		}

		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 := ioutil.ReadFile("/usr/lib/grub/i386-pc/cdboot.img")
		if err != nil {
			return err
		}

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

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

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

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

	// config
	cfgBytes, cfg, err := ctx.Config()
	if err != nil {
		return err
	}

	ioutil.WriteFile(filepath.Join(tempDir, "config.yaml"), cfgBytes, 0600)

	// kernel and initrd
	type distCopy struct {
		Src []string
		Dst string
	}

	copies := []distCopy{
		{Src: []string{"kernels", ctx.Host.Kernel}, Dst: "vmlinuz"},
		{Src: []string{"initrd", ctx.Host.Initrd}, Dst: "initrd"},
	}

	// layers
	for _, layer := range cfg.Layers {
		layerVersion := ctx.Host.Versions[layer]
		if layerVersion == "" {
			return fmt.Errorf("layer %q not mapped to a version", layer)
		}

		copies = append(copies,
			distCopy{
				Src: []string{"layers", layer, layerVersion},
				Dst: filepath.Join("current", "layers", layer+".fs"),
			})
	}

	for _, copy := range copies {
		outPath, err := ctx.distFetch(copy.Src...)
		if err != nil {
			return err
		}

		err = cp(outPath, copy.Dst)
		if err != nil {
			return err
		}
	}

	// 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()
}
