package main

import (
	"bytes"
	"encoding/json"
	"flag"
	"fmt"
	"log"
	"log/slog"
	"os"
	"path/filepath"
	"runtime/pprof"
	"strings"
	"time"

	"anime.bike/slogutil/slogenv"
	"anime.bike/stint"
	"git.tuxpa.in/a/nori/renderer"
	"gitlab.com/gfxlabs/gfximg/apng"
	"golang.org/x/sync/errgroup"

	"git.tuxpa.in/a/nori"
	"git.tuxpa.in/a/nori/utils"
)

var (
	dumpPtr        = flag.Bool("d", false, "dump nri file to zip")
	silentPtr      = flag.Bool("s", false, "silent")
	bulkDumpPtr    = flag.Bool("b", false, "bulk dump nris from a folder")
	jsonDumpPtr    = flag.Bool("j", false, "bulk dump nri animation names from a folder")
	directoryPtr   = flag.String("dir", "nri", "directory to bulk dump from")
	destinationPtr = flag.String("dest", "", "directory to bulk dump to")
	filePtr        = flag.String("f", "example.nri", "file to load")
	outputPtr      = flag.String("o", "output.zip", "output zip file")

	cpuprofile = flag.String("cpuprofile", "", "write cpu profile to file")
)

func init() {
	slog.SetDefault(slog.New(stint.NewHandler(os.Stderr, &stint.Options{
		AddSource: true,
		Level:     slogenv.EnvLevel(),
	})))
}

func main() {
	flag.Parse()
	if len(os.Args) < 2 {
		flag.PrintDefaults()
	}
	if *cpuprofile != "" {
		f, err := os.Create(*cpuprofile)
		if err != nil {
			panic(err)
		}
		pprof.StartCPUProfile(f)
		defer pprof.StopCPUProfile()
	}
	if *dumpPtr {
		filename := *filePtr
		output := *outputPtr
		if _, err := os.Stat(filename); err != nil {
			printIf("could not find file %s\n", filename)
			return
		}
		dump(filename, output)
		return
	}

	dir := *directoryPtr
	dest := *destinationPtr
	if dest == "" {
		dest = dir
	}
	printIf("output: %s\n", dest)
	if *bulkDumpPtr || *jsonDumpPtr {
		files, err := os.ReadDir(dir)
		if err != nil {
			printIf("%s: %s\n", dir, err)
			return
		}

		egg := errgroup.Group{}
		egg.SetLimit(12)
		for _, ff := range files {
			f := ff
			if strings.HasSuffix(f.Name(), ".nri") {
				egg.Go(func() error {
					if *bulkDumpPtr {
						if err := dump_nozip(dir+"/"+f.Name(), dest); err != nil {
							return err
						}
					}
					if *jsonDumpPtr {
						if err := dump_nozip(dir+"/"+f.Name(), dest); err != nil {
							return dump_anim_json(dir+"/"+f.Name(), dest)
						}
					}
					return nil
				})
			}
		}
		if err := egg.Wait(); err != nil {
			log.Panicf("oh fuck: %s\n", err)
			return
		}
	}
}

func printIf(s string, args ...interface{}) {
	if !*silentPtr {
		fmt.Printf(s, args...)
	}
}

func dump_anim_json(filename string, destination string) error {
	n, err := nori.FromFile(filename)
	if err != nil {
		return fmt.Errorf("decode: %s", err)
	}
	names, err := nori.GetAnimationNames(n)
	if err != nil {
		return fmt.Errorf("animation: %s", err)
	}

	json, err := json.Marshal(names)
	if err != nil {
		return fmt.Errorf("could not marshal json: %s\n", err)
	}
	baseName := filepath.Base(strings.TrimSuffix(filename, filepath.Ext(filename)))
	output := fmt.Sprintf("%s/%s.json", destination, baseName)
	return os.WriteFile(output, json, 0o740)
}

func dump_nozip(filename string, destination string) error {
	start := time.Now()
	n, err := nori.FromFile(filename)
	if err != nil {
		return fmt.Errorf("decode: %s", err)
	}
	// printIf("rendering %d animation(s)\n", n.AnimationCount)
	animations, err := renderer.RenderAnimationsApng(n)
	if err != nil {
		return fmt.Errorf("animation: %s", err)
	}

	baseName := filepath.Base(strings.TrimSuffix(filename, filepath.Ext(filename)))
	for i, v := range animations {
		output := fmt.Sprintf(destination+"/%s_%d.png", baseName, i)
		// printIf("saving to %s \n", output)
		f, fErr := os.OpenFile(output, os.O_CREATE, 0740)
		if fErr != nil {
			return fErr
		}
		defer f.Close()
		if err := apng.Encode(f, *v); err != nil {
			return err
		}
	}

	//
	//if err := utils.ZipApngs(out, animations); err != nil {
	//	return fmt.Errorf("zipping: %s", err)
	//}
	printIf("done in %v \n", time.Now().Sub(start))
	//os.WriteFile(output, out.Bytes(), 0740)
	return nil
}

func dump(filename string, output string) error {
	start := time.Now()
	n, err := nori.FromFile(filename)
	if err != nil {
		return fmt.Errorf("decode: %s", err)
	}
	out := new(bytes.Buffer)
	printIf("rendering %d animation(s)\n", n.AnimationCount)
	animations, err := renderer.RenderAnimationsApng(n)
	if err != nil {
		return fmt.Errorf("animation: %s", err)
	}
	printIf("saving to %s \n", output)
	if err := utils.ZipApngs(out, animations); err != nil {
		return fmt.Errorf("zipping: %s", err)
	}
	printIf("done in %v \n", time.Now().Sub(start))
	os.WriteFile(output, out.Bytes(), 0740)
	return nil
}
