package main

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

const (
	langSource     = "datagen/language/language_codes.csv"
	outputFileName = "language_code_info.go"
)

type lang struct {
	code    string
	nameEn  string
	nameNat string
}

func decodeLang() *[]lang {

	// 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 = 3

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

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

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

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

package langreg

import (
  "errors"
  "fmt"
)

// LangCodeInfo returns the English and native language in its script for a
// given string, and an error if any.  If there are more than one official
// names for the language (either English or native), they are separated by a
// semi-colon (;)
// Language codes should always be lowercase, and this is enforced.
func LangCodeInfo(s string) (english, native string, err error) {

  // codes have to be two characters long
  if len(s) != 2 {
    return "", "",
      errors.New("ISO 639-1 language 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, %q, nil\n", r.code[1], r.nameEn, r.nameNat)
	}

	out += `}
	}
	return "", "",
		fmt.Errorf("%q is not a valid ISO-639-1 language 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 := decodeLang()
	res, _ := generateSwitch(*rows)
	err := writeFile(res, outputFileName)
	if err != nil {
		fmt.Println(err)
	} else {
		fmt.Println("language_code_info.go generated")
	}
}
