package main

import (
	"bufio"
	"fmt"
	"html/template"
	"log"
	"os"
	"path/filepath"
	"strings"
	"unicode/utf8"
)

type data struct {
	Items []dataItem
}

type dataItem struct {
	Data [][]byte
	Kind string
	Name string
}

var tmpl = `// Code generated by astisub. DO NOT EDIT.
package astisub

var (
{{ range $val := .Items }}	{{ $val.Kind }}{{ $val.Name }} = &{{ $val.Kind }}{ {{FormatBytes $val.Data}} }
{{ end }}
	// Chapter: 15.2 | Page: 109 | Link: http://www.etsi.org/deliver/etsi_i_ets/300700_300799/300706/01_60/ets_300706e01p.pdf
	// It is indexed by triplet1 then by national option subset code
	teletextCharsets = map[uint8]map[uint8]struct {
		g0       *teletextCharset
		g2       *teletextCharset
		national *teletextNationalSubset
	}{
		0: {
			0: {g0: teletextCharsetG0Latin, g2: teletextCharsetG2Latin, national: teletextNationalSubsetEnglish},
			1: {g0: teletextCharsetG0Latin, g2: teletextCharsetG2Latin, national: teletextNationalSubsetFrench},
			2: {g0: teletextCharsetG0Latin, g2: teletextCharsetG2Latin, national: teletextNationalSubsetSwedishFinnishHungarian},
			3: {g0: teletextCharsetG0Latin, g2: teletextCharsetG2Latin, national: teletextNationalSubsetCzechSlovak},
			4: {g0: teletextCharsetG0Latin, g2: teletextCharsetG2Latin, national: teletextNationalSubsetGerman},
			5: {g0: teletextCharsetG0Latin, g2: teletextCharsetG2Latin, national: teletextNationalSubsetPortugueseSpanish},
			6: {g0: teletextCharsetG0Latin, g2: teletextCharsetG2Latin, national: teletextNationalSubsetItalian},
			7: {g0: teletextCharsetG0Latin, g2: teletextCharsetG2Latin},
		},
		1: {
			0: {g0: teletextCharsetG0Latin, g2: teletextCharsetG2Latin, national: teletextNationalSubsetPolish},
			1: {g0: teletextCharsetG0Latin, g2: teletextCharsetG2Latin, national: teletextNationalSubsetFrench},
			2: {g0: teletextCharsetG0Latin, g2: teletextCharsetG2Latin, national: teletextNationalSubsetSwedishFinnishHungarian},
			3: {g0: teletextCharsetG0Latin, g2: teletextCharsetG2Latin, national: teletextNationalSubsetCzechSlovak},
			4: {g0: teletextCharsetG0Latin, g2: teletextCharsetG2Latin, national: teletextNationalSubsetGerman},
			5: {g0: teletextCharsetG0Latin, g2: teletextCharsetG2Latin},
			6: {g0: teletextCharsetG0Latin, g2: teletextCharsetG2Latin, national: teletextNationalSubsetItalian},
			7: {g0: teletextCharsetG0Latin, g2: teletextCharsetG2Latin},
		},
		2: {
			0: {g0: teletextCharsetG0Latin, g2: teletextCharsetG2Latin, national: teletextNationalSubsetEnglish},
			1: {g0: teletextCharsetG0Latin, g2: teletextCharsetG2Latin, national: teletextNationalSubsetFrench},
			2: {g0: teletextCharsetG0Latin, g2: teletextCharsetG2Latin, national: teletextNationalSubsetSwedishFinnishHungarian},
			3: {g0: teletextCharsetG0Latin, g2: teletextCharsetG2Latin, national: teletextNationalSubsetCzechSlovak},
			4: {g0: teletextCharsetG0Latin, g2: teletextCharsetG2Latin, national: teletextNationalSubsetGerman},
			5: {g0: teletextCharsetG0Latin, g2: teletextCharsetG2Latin, national: teletextNationalSubsetPortugueseSpanish},
			6: {g0: teletextCharsetG0Latin, g2: teletextCharsetG2Latin, national: teletextNationalSubsetItalian},
			7: {g0: teletextCharsetG0Latin, g2: teletextCharsetG2Latin},
		},
		3: {
			0: {g0: teletextCharsetG0Latin, g2: teletextCharsetG2Latin},
			1: {g0: teletextCharsetG0Latin, g2: teletextCharsetG2Latin},
			2: {g0: teletextCharsetG0Latin, g2: teletextCharsetG2Latin},
			3: {g0: teletextCharsetG0Latin, g2: teletextCharsetG2Latin},
			4: {g0: teletextCharsetG0Latin, g2: teletextCharsetG2Latin},
			5: {g0: teletextCharsetG0Latin, g2: teletextCharsetG2Latin, national: teletextNationalSubsetSerbianCroatianSlovenian},
			6: {g0: teletextCharsetG0Latin, g2: teletextCharsetG2Latin},
			7: {g0: teletextCharsetG0Latin, g2: teletextCharsetG2Latin, national: teletextNationalSubsetRomanian},
		},
		4: {
			0: {g0: teletextCharsetG0CyrillicOption1, g2: teletextCharsetG2Cyrillic},
			1: {g0: teletextCharsetG0CyrillicOption2, g2: teletextCharsetG2Cyrillic},
			2: {g0: teletextCharsetG0Latin, g2: teletextCharsetG2Latin, national: teletextNationalSubsetEstonian},
			3: {g0: teletextCharsetG0Latin, g2: teletextCharsetG2Latin, national: teletextNationalSubsetCzechSlovak},
			4: {g0: teletextCharsetG0Latin, g2: teletextCharsetG2Latin, national: teletextNationalSubsetGerman},
			5: {g0: teletextCharsetG0CyrillicOption3, g2: teletextCharsetG2Cyrillic},
			6: {g0: teletextCharsetG0Latin, g2: teletextCharsetG2Latin, national: teletextNationalSubsetLettishLithuanian},
		},
		6: {
			3: {g0: teletextCharsetG0Latin, g2: teletextCharsetG2Latin, national: teletextNationalSubsetTurkish},
			7: {g0: teletextCharsetG0Greek, g2: teletextCharsetG2Greek},
		},
		8: {
			0: {g0: teletextCharsetG0Latin, g2: teletextCharsetG2Arabic, national: teletextNationalSubsetEnglish},
			1: {g0: teletextCharsetG0Latin, g2: teletextCharsetG2Arabic, national: teletextNationalSubsetFrench},
			7: {g0: teletextCharsetG0Arabic, g2: teletextCharsetG2Arabic},
		},
		10: {
			5: {g0: teletextCharsetG0Hebrew, g2: teletextCharsetG2Arabic},
			7: {g0: teletextCharsetG0Arabic, g2: teletextCharsetG2Arabic},
		},
	}
	teletextNationalSubsetCharactersPositionInG0 = [13]uint8{0x03, 0x04, 0x20, 0x3b, 0x3c, 0x3d, 0x3e, 0x3f, 0x40, 0x5b, 0x5c, 0x5d, 0x5e}
)
`

func main() {
	dir, err := os.Getwd()
	if err != nil {
		log.Fatal(fmt.Errorf("main: getting working directory failed: %w", err))
	}

	dst, err := os.Create(filepath.Join(dir, "teletext_charsets.go"))
	if err != nil {
		log.Fatal(fmt.Errorf("main: creating dst file failed: %w", err))
	}
	defer dst.Close()

	src, err := os.Open(filepath.Join(dir, "internal", "cmd", "teletext", "chars.txt"))
	if err != nil {
		log.Fatal(fmt.Errorf("main: opening src file failed: %w", err))
	}
	defer src.Close()

	var lines [][]byte
	s := bufio.NewScanner(src)
	for s.Scan() {
		lines = append(lines, s.Bytes())
	}

	if err := s.Err(); err != nil {
		log.Fatal(fmt.Errorf("main: scanning src failed: %w", err))
	}

	data := data{Items: []dataItem{
		{
			Data: extractBytes(lines, 16, 173),
			Kind: "teletextCharset",
			Name: "G0Arabic",
		},
		{
			Data: extractBytes(lines, 16, 59),
			Kind: "teletextCharset",
			Name: "G0CyrillicOption1",
		},
		{
			Data: extractBytes(lines, 16, 78),
			Kind: "teletextCharset",
			Name: "G0CyrillicOption2",
		},
		{
			Data: extractBytes(lines, 16, 97),
			Kind: "teletextCharset",
			Name: "G0CyrillicOption3",
		},
		{
			Data: extractBytes(lines, 16, 135),
			Kind: "teletextCharset",
			Name: "G0Greek",
		},
		{
			Data: extractBytes(lines, 16, 211),
			Kind: "teletextCharset",
			Name: "G0Hebrew",
		},
		{
			Data: extractBytes(lines, 16, 21),
			Kind: "teletextCharset",
			Name: "G0Latin",
		},
		{
			Data: extractBytes(lines, 16, 192),
			Kind: "teletextCharset",
			Name: "G2Arabic",
		},
		{
			Data: extractBytes(lines, 16, 116),
			Kind: "teletextCharset",
			Name: "G2Cyrillic",
		},
		{
			Data: extractBytes(lines, 16, 154),
			Kind: "teletextCharset",
			Name: "G2Greek",
		},
		{
			Data: extractBytes(lines, 16, 40),
			Kind: "teletextCharset",
			Name: "G2Latin",
		},
		{
			Data: extractBytes(lines, 1, 5),
			Kind: "teletextNationalSubset",
			Name: "CzechSlovak",
		},
		{
			Data: extractBytes(lines, 1, 6),
			Kind: "teletextNationalSubset",
			Name: "English",
		},
		{
			Data: extractBytes(lines, 1, 7),
			Kind: "teletextNationalSubset",
			Name: "Estonian",
		},
		{
			Data: extractBytes(lines, 1, 8),
			Kind: "teletextNationalSubset",
			Name: "French",
		},
		{
			Data: extractBytes(lines, 1, 9),
			Kind: "teletextNationalSubset",
			Name: "German",
		},
		{
			Data: extractBytes(lines, 1, 10),
			Kind: "teletextNationalSubset",
			Name: "Italian",
		},
		{
			Data: extractBytes(lines, 1, 11),
			Kind: "teletextNationalSubset",
			Name: "LettishLithuanian",
		},
		{
			Data: extractBytes(lines, 1, 12),
			Kind: "teletextNationalSubset",
			Name: "Polish",
		},
		{
			Data: extractBytes(lines, 1, 13),
			Kind: "teletextNationalSubset",
			Name: "PortugueseSpanish",
		},
		{
			Data: extractBytes(lines, 1, 14),
			Kind: "teletextNationalSubset",
			Name: "Romanian",
		},
		{
			Data: extractBytes(lines, 1, 15),
			Kind: "teletextNationalSubset",
			Name: "SerbianCroatianSlovenian",
		},
		{
			Data: extractBytes(lines, 1, 16),
			Kind: "teletextNationalSubset",
			Name: "SwedishFinnishHungarian",
		},
		{
			Data: extractBytes(lines, 1, 17),
			Kind: "teletextNationalSubset",
			Name: "Turkish",
		},
	}}

	if err = template.Must(template.New("tmpl").
		Funcs(template.FuncMap{
			"FormatBytes": func(bss [][]byte) string {
				var sss []string
				for _, bs := range bss {
					var ss []string
					for _, b := range bs {
						ss = append(ss, fmt.Sprintf("0x%x", b))
					}
					sss = append(sss, fmt.Sprintf("[]byte{%s}", strings.Join(ss, ", ")))
				}
				return strings.Join(sss, ", ")
			},
		}).
		Parse(tmpl)).
		Execute(dst, data); err != nil {
		log.Fatal(fmt.Errorf("main: executing template failed: %w", err))
	}
}

func extractBytes(lines [][]byte, rowCount, rowStart int) [][]byte {
	table := make([][][]byte, rowCount)
	for row := rowStart; row < rowStart+rowCount; row++ {
		i := 0
		for {
			r, n := utf8.DecodeRune(lines[row][i:])
			if r == utf8.RuneError {
				panic("main: decoding rune error")
			}

			table[row-rowStart] = append(table[row-rowStart], lines[row][i:i+n])
			i += n + 1

			if i >= len(lines[row])-1 {
				break
			}
		}
	}

	var bs [][]byte
	for column := 0; column < len(table[0]); column++ {
		for row := 0; row < len(table); row++ {
			bs = append(bs, table[row][column])
		}
	}
	return bs
}
