// ppppcg is a PrePostPrint inspired political compass generator with SVG output.
package main

import (
	"flag"
	"fmt"
	"log"
	"os"
	"path/filepath"

	svg "github.com/ajstarks/svgo"
	"gopkg.in/yaml.v2"
)

// help is the CLI help text.
const help = `ppppcg [options]

A PrePostPrint inspired political compass generator with SVG output.

Options:
  -h    output help
`

// config is the user configuration file.
type config struct {
	Width  int
	Height int

	Axis struct {
		Top    string
		Right  string
		Bottom string
		Left   string
	}

	Points []struct {
		Name string
		X    int
		Y    int
	}
}

var (
	helpFlag   bool
	configFlag string

	// rectMargin is the margin for the compass rectangle.
	rectMargin = 50

	// fillNoneStrokeBlack is a default calm line style.
	fillNoneStrokeBlack = "fill:none;stroke:black"

	// axisFontStyle is the default axis font style
	axisFontStyle = "font-size: 13pt;font-weight:bold"

	// pointFontStyle is the default point font style
	pointFontStyle = "font-size: 11pt;font-style:italic"

	// lineOverflow is the amount of overflow permitted for each compass line.
	lineOverflow = 10
)

// handleCliFlags parses CLI flags.
func handleCliFlags() {
	flag.BoolVar(&helpFlag, "h", false, "output help")
	flag.StringVar(&configFlag, "c", "ppppcg.yml", "config file")
	flag.Parse()
}

// loadYAMLConfig loads a mppppcg YAML user config.
func loadYAMLConfig() (config, error) {
	var cfg config

	configPath, err := filepath.Abs(configFlag)
	if err != nil {
		return config{}, fmt.Errorf("loadYAMLConfig: unable to convert %s to an absolute path: %w", configFlag, err)
	}

	conf, err := os.ReadFile(configPath)
	if err != nil {
		return config{}, fmt.Errorf("loadYAMLConfig: unable to read %s: %w", configFlag, err)
	}

	err = yaml.UnmarshalStrict(conf, &cfg)
	if err != nil {
		return config{}, fmt.Errorf("loadYAMLConfig: unable to unmarshal %s: %w", string(conf), err)
	}

	return cfg, nil
}

// main is the CLI entrypoint.
func main() {
	handleCliFlags()

	if helpFlag {
		fmt.Print(help)
		os.Exit(0)
	}

	cfg, err := loadYAMLConfig()
	if err != nil {
		log.Fatal(err)
	}

	canvas := svg.New(os.Stdout)
	canvas.Start(cfg.Width, cfg.Height)

	canvas.CenterRect(
		cfg.Width/2,
		cfg.Height/2,
		cfg.Width-rectMargin,
		cfg.Width-rectMargin,
		fillNoneStrokeBlack,
	)

	canvas.Line(
		cfg.Width/2,
		lineOverflow,
		cfg.Width/2,
		cfg.Width-lineOverflow,
		fillNoneStrokeBlack,
	)

	canvas.Line(
		lineOverflow,
		cfg.Width/2,
		cfg.Width-lineOverflow,
		cfg.Width/2,
		fillNoneStrokeBlack,
	)

	canvas.TranslateRotate(
		lineOverflow,
		cfg.Width/2,
		270,
	)
	canvas.Text(
		-15,
		35,
		cfg.Axis.Left,
		axisFontStyle,
	)
	canvas.Gend()

	canvas.Text(
		cfg.Width/2+10,
		lineOverflow+10,
		cfg.Axis.Top,
		axisFontStyle,
	)

	canvas.TranslateRotate(
		cfg.Width-lineOverflow,
		(cfg.Width/2)-lineOverflow+5,
		90,
	)
	canvas.Text(
		35,
		-(cfg.Width/2)-3,
		cfg.Axis.Right,
		axisFontStyle,
	)
	canvas.Gend()

	canvas.Text(
		(cfg.Width/2)+lineOverflow,
		cfg.Width-lineOverflow,
		cfg.Axis.Bottom,
		axisFontStyle,
	)

	for _, p := range cfg.Points {
		canvas.Text(p.X, p.Y, p.Name, pointFontStyle)
	}

	canvas.End()
}
