package main

import (
	"errors"
	"fmt"
	"os"

	"github.com/urfave/cli/v2"
)

// Set by compiler, see Makefile
var (
	version = "v1.3.0"
	commit  = "unknown"
	builtBy = "unknown"
)

func main() {

	app := &cli.App{
		Name:                   "didder",
		Usage:                  "dither images with a variety of algorithms and processing options.",
		Description:            "didder dithers images.\n\nRun `man didder` for more information, or view the manual online:\nhttps://github.com/makeworld-the-better-one/didder/blob/main/MANPAGE.md",
		UseShortOptionHandling: true,
		Flags: []cli.Flag{
			&cli.StringFlag{
				Name:    "strength",
				Aliases: []string{"s"},
			},
			&cli.UintFlag{
				Name:    "threads",
				Aliases: []string{"j"},
			},
			&cli.StringFlag{
				Name:     "palette",
				Aliases:  []string{"p"},
				Required: true,
			},
			&cli.BoolFlag{
				Name:    "grayscale",
				Aliases: []string{"g"},
			},
			&cli.StringFlag{
				Name: "saturation",
			},
			&cli.StringFlag{
				Name: "brightness",
			},
			&cli.StringFlag{
				Name: "contrast",
			},
			&cli.StringFlag{
				Name:    "recolor",
				Aliases: []string{"r"},
			},
			&cli.BoolFlag{
				Name: "no-exif-rotation",
			},
			&cli.StringFlag{
				Name:    "format",
				Aliases: []string{"f"},
				Value:   "png",
			},
			&cli.StringFlag{
				Name:     "out",
				Aliases:  []string{"o"},
				Required: true,
			},
			&cli.StringSliceFlag{
				Name:     "in",
				Aliases:  []string{"i"},
				Required: true,
			},
			&cli.BoolFlag{
				Name: "no-overwrite",
			},
			&cli.StringFlag{
				Name:    "compression",
				Aliases: []string{"c"},
				Value:   "default",
			},
			&cli.Float64Flag{
				Name: "fps",
			},
			&cli.UintFlag{
				Name:    "loop",
				Aliases: []string{"l"},
			},
			&cli.UintFlag{
				Name:    "width",
				Aliases: []string{"x"},
			},
			&cli.UintFlag{
				Name:    "height",
				Aliases: []string{"y"},
			},
			&cli.UintFlag{
				Name:    "upscale",
				Aliases: []string{"u"},
				Value:   1,
			},
			&cli.BoolFlag{
				Name:    "version",
				Aliases: []string{"v"},
			},
		},
		Commands: []*cli.Command{
			{
				Name:  "random",
				Usage: "grayscale and RGB random dithering",
				Flags: []cli.Flag{
					&cli.Int64Flag{
						Name:    "seed",
						Aliases: []string{"s"},
					},
				},
				UseShortOptionHandling: true,
				Action:                 random,
				SkipFlagParsing:        true, // Allow for numbers that start with a negative
			},
			{
				Name:                   "bayer",
				Usage:                  "Bayer matrix ordered dithering",
				UseShortOptionHandling: true,
				Action:                 bayer,
			},
			{
				Name:                   "odm",
				Usage:                  "Ordered Dither Matrix",
				UseShortOptionHandling: true,
				Action:                 odm,
			},
			{
				Name:  "edm",
				Usage: "Error Diffusion Matrix",
				Flags: []cli.Flag{
					&cli.BoolFlag{
						Name:    "serpentine",
						Aliases: []string{"s"},
					},
				},
				UseShortOptionHandling: true,
				Action:                 edm,
			},
		},
		Before: preProcess,
		Action: func(c *cli.Context) error {
			return errors.New("no command specified")
		},
	}

	// Handle version flag
	if len(os.Args) == 2 && (os.Args[1] == "-v" || os.Args[1] == "--version") {
		fmt.Println("didder", version)
		fmt.Println("Commit:", commit)
		fmt.Println("Built by:", builtBy)
		return
	}

	// Hack around issue where required flags are still required even for help
	// https://github.com/urfave/cli/issues/1247
	if len(os.Args) == 3 {
		if os.Args[1] == "h" || os.Args[1] == "help" {
			// Like: didder help bayer
			for _, c := range app.Commands {
				if c.Name == os.Args[2] {
					cli.HelpPrinter(os.Stdout, cli.CommandHelpTemplate, c)
					return
				}
			}
			fmt.Println("no command with that name")
			os.Exit(1)
		} else if os.Args[len(os.Args)-1] == "-h" || os.Args[len(os.Args)-1] == "--help" {
			// Like: didder bayer --help
			for _, c := range app.Commands {
				if c.Name == os.Args[1] {
					cli.HelpPrinter(os.Stdout, cli.CommandHelpTemplate, c)
					return
				}
			}
			fmt.Println("no command with that name")
			os.Exit(1)
		}
	}

	err := app.Run(os.Args)
	if err != nil {
		if len(os.Args) == 1 {
			// Just ran the command with no flags
			return
		}
		fmt.Println(err)
		os.Exit(1)
	}
}
