// Copyright 2017 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

//go:build ignore

package main

import (
	"flag"
	"log"
	"strconv"
	"strings"

	"golang.org/x/text/internal/cldrtree"
	"golang.org/x/text/internal/gen"
	"golang.org/x/text/language"
	"golang.org/x/text/unicode/cldr"
)

var (
	draft = flag.String("draft",
		"contributed",
		`Minimal draft requirements (approved, contributed, provisional, unconfirmed).`)
)

// TODO:
// - Compile format patterns.
// - Compress the large amount of redundancy in metazones.
// - Split trees (with shared buckets) with data that is enough for default
//   formatting of Go Time values and tables that are needed for larger
//   variants.
// - zone to metaZone mappings (in supplemental)
// - Add more enum values and also some key maps for some of the elements.

func main() {
	gen.Init()

	r := gen.OpenCLDRCoreZip()
	defer r.Close()

	d := &cldr.Decoder{}
	d.SetDirFilter("supplemental", "main")
	d.SetSectionFilter("dates")
	data, err := d.DecodeZip(r)
	if err != nil {
		log.Fatalf("DecodeZip: %v", err)
	}

	dates := cldrtree.New("dates")
	buildCLDRTree(data, dates)

	w := gen.NewCodeWriter()
	if err := dates.Gen(w); err != nil {
		log.Fatal(err)
	}
	gen.WriteCLDRVersion(w)
	w.WriteGoFile("tables.go", "date")

	w = gen.NewCodeWriter()
	if err := dates.GenTestData(w); err != nil {
		log.Fatal(err)
	}
	w.WriteGoFile("data_test.go", "date")
}

func buildCLDRTree(data *cldr.CLDR, dates *cldrtree.Builder) {
	context := cldrtree.Enum("context")
	widthMap := func(s string) string {
		// Align era with width values.
		if r, ok := map[string]string{
			"eraAbbr":   "abbreviated",
			"eraNarrow": "narrow",
			"eraNames":  "wide",
		}[s]; ok {
			s = r
		}
		// Prefix width to disambiguate with some overlapping length values.
		return "width" + strings.Title(s)
	}
	width := cldrtree.EnumFunc("width", widthMap, "abbreviated", "narrow", "wide")
	length := cldrtree.Enum("length", "short", "long")
	month := cldrtree.Enum("month", "leap7")
	relTime := cldrtree.EnumFunc("relTime", func(s string) string {
		x, err := strconv.ParseInt(s, 10, 8)
		if err != nil {
			log.Fatal("Invalid number:", err)
		}
		return []string{
			"before2",
			"before1",
			"current",
			"after1",
			"after2",
			"after3",
		}[x+2]
	})
	// Disambiguate keys like 'months' and 'sun'.
	cycleType := cldrtree.EnumFunc("cycleType", func(s string) string {
		return s + "CycleType"
	})
	field := cldrtree.EnumFunc("field", func(s string) string {
		return s + "Field"
	})
	timeType := cldrtree.EnumFunc("timeType", func(s string) string {
		if s == "" {
			return "genericTime"
		}
		return s + "Time"
	}, "generic")

	zoneType := []cldrtree.Option{cldrtree.SharedType(), timeType}
	metaZoneType := []cldrtree.Option{cldrtree.SharedType(), timeType}

	for _, lang := range data.Locales() {
		tag := language.Make(lang)
		ldml := data.RawLDML(lang)
		if ldml.Dates == nil {
			continue
		}
		x := dates.Locale(tag)
		if x := x.Index(ldml.Dates.Calendars); x != nil {
			for _, cal := range ldml.Dates.Calendars.Calendar {
				x := x.IndexFromType(cal)
				if x := x.Index(cal.Months); x != nil {
					for _, mc := range cal.Months.MonthContext {
						x := x.IndexFromType(mc, context)
						for _, mw := range mc.MonthWidth {
							x := x.IndexFromType(mw, width)
							for _, m := range mw.Month {
								x.SetValue(m.Yeartype+m.Type, m, month)
							}
						}
					}
				}
				if x := x.Index(cal.MonthPatterns); x != nil {
					for _, mc := range cal.MonthPatterns.MonthPatternContext {
						x := x.IndexFromType(mc, context)
						for _, mw := range mc.MonthPatternWidth {
							// Value is always leap, so no need to create a
							// subindex.
							for _, m := range mw.MonthPattern {
								x.SetValue(mw.Type, m, width)
							}
						}
					}
				}
				if x := x.Index(cal.CyclicNameSets); x != nil {
					for _, cns := range cal.CyclicNameSets.CyclicNameSet {
						x := x.IndexFromType(cns, cycleType)
						for _, cc := range cns.CyclicNameContext {
							x := x.IndexFromType(cc, context)
							for _, cw := range cc.CyclicNameWidth {
								x := x.IndexFromType(cw, width)
								for _, c := range cw.CyclicName {
									x.SetValue(c.Type, c)
								}
							}
						}
					}
				}
				if x := x.Index(cal.Days); x != nil {
					for _, dc := range cal.Days.DayContext {
						x := x.IndexFromType(dc, context)
						for _, dw := range dc.DayWidth {
							x := x.IndexFromType(dw, width)
							for _, d := range dw.Day {
								x.SetValue(d.Type, d)
							}
						}
					}
				}
				if x := x.Index(cal.Quarters); x != nil {
					for _, qc := range cal.Quarters.QuarterContext {
						x := x.IndexFromType(qc, context)
						for _, qw := range qc.QuarterWidth {
							x := x.IndexFromType(qw, width)
							for _, q := range qw.Quarter {
								x.SetValue(q.Type, q)
							}
						}
					}
				}
				if x := x.Index(cal.DayPeriods); x != nil {
					for _, dc := range cal.DayPeriods.DayPeriodContext {
						x := x.IndexFromType(dc, context)
						for _, dw := range dc.DayPeriodWidth {
							x := x.IndexFromType(dw, width)
							for _, d := range dw.DayPeriod {
								x.IndexFromType(d).SetValue(d.Alt, d)
							}
						}
					}
				}
				if x := x.Index(cal.Eras); x != nil {
					opts := []cldrtree.Option{width, cldrtree.SharedType()}
					if x := x.Index(cal.Eras.EraNames, opts...); x != nil {
						for _, e := range cal.Eras.EraNames.Era {
							x.IndexFromAlt(e).SetValue(e.Type, e)
						}
					}
					if x := x.Index(cal.Eras.EraAbbr, opts...); x != nil {
						for _, e := range cal.Eras.EraAbbr.Era {
							x.IndexFromAlt(e).SetValue(e.Type, e)
						}
					}
					if x := x.Index(cal.Eras.EraNarrow, opts...); x != nil {
						for _, e := range cal.Eras.EraNarrow.Era {
							x.IndexFromAlt(e).SetValue(e.Type, e)
						}
					}
				}
				if x := x.Index(cal.DateFormats); x != nil {
					for _, dfl := range cal.DateFormats.DateFormatLength {
						x := x.IndexFromType(dfl, length)
						for _, df := range dfl.DateFormat {
							for _, p := range df.Pattern {
								x.SetValue(p.Alt, p)
							}
						}
					}
				}
				if x := x.Index(cal.TimeFormats); x != nil {
					for _, tfl := range cal.TimeFormats.TimeFormatLength {
						x := x.IndexFromType(tfl, length)
						for _, tf := range tfl.TimeFormat {
							for _, p := range tf.Pattern {
								x.SetValue(p.Alt, p)
							}
						}
					}
				}
				if x := x.Index(cal.DateTimeFormats); x != nil {
					for _, dtfl := range cal.DateTimeFormats.DateTimeFormatLength {
						x := x.IndexFromType(dtfl, length)
						for _, dtf := range dtfl.DateTimeFormat {
							for _, p := range dtf.Pattern {
								x.SetValue(p.Alt, p)
							}
						}
					}
					// TODO:
					// - appendItems
					// - intervalFormats
				}
			}
		}
		// TODO: this is a lot of data and is probably relatively little used.
		// Store this somewhere else.
		if x := x.Index(ldml.Dates.Fields); x != nil {
			for _, f := range ldml.Dates.Fields.Field {
				x := x.IndexFromType(f, field)
				for _, d := range f.DisplayName {
					x.Index(d).SetValue(d.Alt, d)
				}
				for _, r := range f.Relative {
					x.Index(r).SetValue(r.Type, r, relTime)
				}
				for _, rt := range f.RelativeTime {
					x := x.Index(rt).IndexFromType(rt)
					for _, p := range rt.RelativeTimePattern {
						x.SetValue(p.Count, p)
					}
				}
				for _, rp := range f.RelativePeriod {
					x.Index(rp).SetValue(rp.Alt, rp)
				}
			}
		}
		if x := x.Index(ldml.Dates.TimeZoneNames); x != nil {
			format := x.IndexWithName("zoneFormat")
			for _, h := range ldml.Dates.TimeZoneNames.HourFormat {
				format.SetValue(h.Element(), h)
			}
			for _, g := range ldml.Dates.TimeZoneNames.GmtFormat {
				format.SetValue(g.Element(), g)
			}
			for _, g := range ldml.Dates.TimeZoneNames.GmtZeroFormat {
				format.SetValue(g.Element(), g)
			}
			for _, r := range ldml.Dates.TimeZoneNames.RegionFormat {
				x.Index(r).SetValue(r.Type, r, timeType)
			}

			set := func(x *cldrtree.Index, e []*cldr.Common, zone string) {
				for _, n := range e {
					x.Index(n, zoneType...).SetValue(zone, n)
				}
			}
			zoneWidth := []cldrtree.Option{length, cldrtree.SharedType()}
			zs := x.IndexWithName("zone")
			for _, z := range ldml.Dates.TimeZoneNames.Zone {
				for _, l := range z.Long {
					x := zs.Index(l, zoneWidth...)
					set(x, l.Generic, z.Type)
					set(x, l.Standard, z.Type)
					set(x, l.Daylight, z.Type)
				}
				for _, s := range z.Short {
					x := zs.Index(s, zoneWidth...)
					set(x, s.Generic, z.Type)
					set(x, s.Standard, z.Type)
					set(x, s.Daylight, z.Type)
				}
			}
			set = func(x *cldrtree.Index, e []*cldr.Common, zone string) {
				for _, n := range e {
					x.Index(n, metaZoneType...).SetValue(zone, n)
				}
			}
			zoneWidth = []cldrtree.Option{length, cldrtree.SharedType()}
			zs = x.IndexWithName("metaZone")
			for _, z := range ldml.Dates.TimeZoneNames.Metazone {
				for _, l := range z.Long {
					x := zs.Index(l, zoneWidth...)
					set(x, l.Generic, z.Type)
					set(x, l.Standard, z.Type)
					set(x, l.Daylight, z.Type)
				}
				for _, s := range z.Short {
					x := zs.Index(s, zoneWidth...)
					set(x, s.Generic, z.Type)
					set(x, s.Standard, z.Type)
					set(x, s.Daylight, z.Type)
				}
			}
		}
	}
}
