package main

import (
	"fmt"
	"os"
	"sort"
	"strings"
	"text/template"

	"github.com/jwalton/gchalk/pkg/ansistyles"
)

var functionsTemplate = template.Must(template.New("functions").Parse(`
// {{.capStyle}} returns a string {{.description}}.
func {{.capStyle}}(str ...string) string {
	return rootBuilder.With{{.capStyle}}().applyStyle(str...)
}

// With{{.capStyle}} returns a Builder that generates strings {{.description}},
// and further styles can be applied via chaining.
func With{{.capStyle}}() *Builder {
	return rootBuilder.With{{.capStyle}}()
}

// {{.capStyle}} returns a string {{.description}}, in addition to other styles from this builder.
func (builder *Builder) {{.capStyle}}(str ...string) string {
	return builder.With{{.capStyle}}().applyStyle(str...)
}

// With{{.capStyle}} returns a Builder that generates strings {{.description}},
// in addition to other styles from this builder, and further styles can be applied via chaining.
func (builder *Builder) With{{.capStyle}}() *Builder {
	builder.shared.mutex.Lock()
	defer builder.shared.mutex.Unlock()

	if builder.{{.style}} == nil {
		builder.{{.style}} = createBuilder(builder, ansistyles.{{.capStyle}}.Open, ansistyles.{{.capStyle}}.Close)
	}
	return builder.{{.style}}
}
`))

func sortedKeys(collection map[string]ansistyles.CSPair) []string {
	strings := make([]string, len(collection))
	i := 0
	for style := range collection {
		strings[i] = style
		i++
	}
	sort.Strings(strings)
	return strings
}

func sortedStyles() []string {
	styles := make([]string, 0)

	styles = append(styles, sortedKeys(ansistyles.Color)...)
	styles = append(styles, sortedKeys(ansistyles.BgColor)...)
	styles = append(styles, sortedKeys(ansistyles.Modifier)...)

	return styles
}

func main() {
	fmt.Print(`package gchalk

// This file was generated.  Don't edit it directly.

import (
	"github.com/jwalton/gchalk/pkg/ansistyles"
)

`)
	if err := generateStyles(); err != nil {
		fmt.Println(err)
		os.Exit(1)
	}
	generateWithStyle()
}

func generateStyles() error {
	for _, style := range sortedKeys(ansistyles.Color) {
		args := map[string]string{
			"style":       style,
			"capStyle":    strings.Title(style),
			"description": fmt.Sprintf("where the color is %s", style),
		}
		if err := functionsTemplate.Execute(os.Stdout, args); err != nil {
			return err
		}
	}

	for _, style := range sortedKeys(ansistyles.BgColor) {
		baseStyle := style[2:]
		args := map[string]string{
			"style":       style,
			"capStyle":    strings.Title(style),
			"description": fmt.Sprintf("where the background color is %s", baseStyle),
		}
		if err := functionsTemplate.Execute(os.Stdout, args); err != nil {
			return err
		}
	}

	for _, style := range sortedKeys(ansistyles.Modifier) {
		args := map[string]string{
			"style":       style,
			"capStyle":    strings.Title(style),
			"description": fmt.Sprintf("with the %s modifier", style),
		}
		if err := functionsTemplate.Execute(os.Stdout, args); err != nil {
			return err
		}
	}

	return nil
}

func generateWithStyle() {
	fmt.Println("func (builder *Builder) getBuilderForStyle(style string) *Builder {")
	fmt.Println("	switch {")

	for _, style := range sortedStyles() {
		fmt.Printf("	case strEqualsIgnoreCase(style, \"%s\"):\n", style)
		fmt.Printf("		return builder.With%s()\n", strings.Title(style))
	}
	fmt.Println("	default:")
	fmt.Println("		return nil")

	fmt.Println("	}")
	fmt.Println("}")
}
