package main

import (
	"encoding/xml"
	"flag"
	"fmt"
	"io/ioutil"
	"os"
	"text/template"
)

var usage = `%[1]s generates Go code to support CLDR plural rules.

Usage: %[1]s [options]

Options:

`

func main() {
	flag.Usage = func() {
		fmt.Fprintf(os.Stderr, usage, os.Args[0])
		flag.PrintDefaults()
	}
	var in, cout, tout string
	flag.StringVar(&in, "i", "plurals.xml", "the input XML file containing CLDR plural rules")
	flag.StringVar(&cout, "cout", "", "the code output file")
	flag.StringVar(&tout, "tout", "", "the test output file")
	flag.BoolVar(&verbose, "v", false, "verbose output")
	flag.Parse()

	buf, err := ioutil.ReadFile(in)
	if err != nil {
		fatalf("failed to read file: %s", err)
	}

	var data SupplementalData
	if err := xml.Unmarshal(buf, &data); err != nil {
		fatalf("failed to unmarshal xml: %s", err)
	}

	count := 0
	for _, pg := range data.PluralGroups {
		count += len(pg.SplitLocales())
	}
	infof("parsed %d locales", count)

	if cout != "" {
		file := openWritableFile(cout)
		if err := codeTemplate.Execute(file, data); err != nil {
			fatalf("unable to execute code template because %s", err)
		} else {
			infof("generated %s", cout)
		}
	} else {
		infof("not generating code file (use -cout)")
	}

	if tout != "" {
		file := openWritableFile(tout)
		if err := testTemplate.Execute(file, data); err != nil {
			fatalf("unable to execute test template because %s", err)
		} else {
			infof("generated %s", tout)
		}
	} else {
		infof("not generating test file (use -tout)")
	}
}

func openWritableFile(name string) *os.File {
	file, err := os.OpenFile(name, os.O_WRONLY|os.O_CREATE|os.O_TRUNC, 0644)
	if err != nil {
		fatalf("failed to write file %s because %s", name, err)
	}
	return file
}

var codeTemplate = template.Must(template.New("rule").Parse(`// This file is generated by i18n/plural/codegen/generate.sh; DO NOT EDIT

package plural

// DefaultRules returns a map of Rules generated from CLDR language data.
func DefaultRules() Rules {
	rules := Rules{}

{{range .PluralGroups}}
	addPluralRules(rules, {{printf "%#v" .SplitLocales}}, &Rule{
		PluralForms: newPluralFormSet({{range $i, $e := .PluralRules}}{{if $i}}, {{end}}{{$e.CountTitle}}{{end}}),
		PluralFormFunc: func(ops *Operands) Form { {{range .PluralRules}}{{if .GoCondition}}
			// {{.Condition}}
			if {{.GoCondition}} {
				return {{.CountTitle}}
			}{{end}}{{end}}
			return Other
		},
	}){{end}}

	return rules
}
`))

var testTemplate = template.Must(template.New("rule").Parse(`// This file is generated by i18n/plural/codegen/generate.sh; DO NOT EDIT

package plural

import "testing"

{{range .PluralGroups}}
func Test{{.Name}}(t *testing.T) {
	var tests []pluralFormTest
	{{range .PluralRules}}
	{{if .IntegerExamples}}tests = appendIntegerTests(tests, {{.CountTitle}}, {{printf "%#v" .IntegerExamples}}){{end}}
	{{if .DecimalExamples}}tests = appendDecimalTests(tests, {{.CountTitle}}, {{printf "%#v" .DecimalExamples}}){{end}}
	{{end}}
	locales := {{printf "%#v" .SplitLocales}}
	for _, locale := range locales {
	  runTests(t, locale, tests)
  }
}
{{end}}
`))

func infof(format string, args ...interface{}) {
	fmt.Fprintf(os.Stderr, format+"\n", args...)
}

var verbose bool

func verbosef(format string, args ...interface{}) {
	if verbose {
		infof(format, args...)
	}
}

func fatalf(format string, args ...interface{}) {
	infof("fatal: "+format+"\n", args...)
	os.Exit(1)
}
