package main

import (
	"encoding/csv"
	"errors"
	"fmt"
	"io/ioutil"
	"os"
)

const (
	langSource     = "datagen/region/region_codes.csv"
	outputFileName = "region_code_info.go"
)

type reg struct {
	code string
	name string
}

func decodeRegion() *[]reg {

	// read the csv file
	csvfile, err := os.Open(langSource)
	if err != nil {
		fmt.Println(err)
		os.Exit(1)
	}
	defer csvfile.Close()

	// decode csv file
	reader := csv.NewReader(csvfile)
	// there are no empty fields
	reader.FieldsPerRecord = 2

	rawCSVdata, err := reader.ReadAll()
	if err != nil {
		fmt.Println(err)
		os.Exit(1)
	}

	// move raw CSV data to struct
	var row reg
	var rows []reg

	for _, v := range rawCSVdata {
		row.code = v[0]
		row.name = v[1]
		rows = append(rows, row)
	}
	return &rows
}

func generateSwitch(d []reg) (string, error) {
	out := `// generated by "go generate"; DO NOT EDIT

package langreg

import (
	"errors"
	"fmt"
)

// RegionCodeInfo returns the English regional name that
// corresponds to the ISO 3166-1 alpha-2 region codes.
// Region codes should always be uppercase, and this is enforced.
// E.g. "US" is valid, but "us" is not.
func RegionCodeInfo(s string) (region string, err error) {

	// codes have to be two characters long
	if len(s) != 2 {
		return "",
			errors.New("ISO 3166-1 alpha-2 region codes must be 2 characters long")
	}
  `

	c1 := d[0].code[0]
	out += fmt.Sprintf("\nswitch s[0] {\n\ncase %q:\nswitch s[1]{\n", c1)
	for _, r := range d {
		// check that the code is exactly 2 characters long
		if !isCodeValid(r.code) {
			return "", fmt.Errorf("The code %q is not 2 characters long", r.code)
		}
		// new first letter of code
		if r.code[0] != c1 {
			// new first char
			c1 = r.code[0]
			out += fmt.Sprintf("}\n\ncase %q:\nswitch s[1]{\n", c1)
		}
		out += fmt.Sprintf("case %q:\nreturn %q, nil\n", r.code[1], r.name)
	}

	out += `}
	}
	return "",
		fmt.Errorf("%q is not a valid ISO 3166-1 alpha-2 region code", s)
}`

	return out, nil
}

func isCodeValid(s string) bool {
	if len(s) != 2 {
		return false
	}
	if s[0] < 'A' || s[0] > 'Z' || s[1] < 'A' || s[1] > 'Z' {
		return false
	}
	return true
}

func writeFile(s, fileName string) error {

	b := []byte(s)
	err := ioutil.WriteFile(fileName, b, 0644)
	if err != nil {
		return errors.New("Couldn't write the file")
	}
	return nil
}

func main() {
	rows := decodeRegion()
	res, _ := generateSwitch(*rows)
	err := writeFile(res, outputFileName)
	if err != nil {
		fmt.Println(err)
	} else {
		fmt.Println("region_code_info.go generated")
	}
}
