// This is a helper to transform the HCL.yaml-tmLanguage file (the source of
// record) into both HCL.json-tmLanguage and HCL.tmLanguage (in plist XML
// format).
//
// Run this after making updates to HCL.yaml-tmLanguage to generate the other
// formats.
//
// This file is intended to be run with "go run":
//
//     go run ./build.go
//
// This file is also set up to run itself under "go generate":
//
//     go generate .

package main

//go:generate go run ./build.go

import (
	"encoding/json"
	"fmt"
	"io/ioutil"
	"log"
	"os"

	yaml "gopkg.in/yaml.v2"
	plist "howett.net/plist"

	multierror "github.com/hashicorp/go-multierror"
)

func main() {
	err := realMain()
	if err != nil {
		log.Fatal(err)
	}
	os.Exit(0)
}

func realMain() error {
	var err error
	buildErr := build("HCL")
	if buildErr != nil {
		err = multierror.Append(err, fmt.Errorf("in HCL: %s", buildErr))
	}
	buildErr = build("HCLTemplate")
	if buildErr != nil {
		err = multierror.Append(err, fmt.Errorf("in HCLTemplate: %s", buildErr))
	}
	buildErr = build("HCLExpression")
	if buildErr != nil {
		err = multierror.Append(err, fmt.Errorf("in HCLExpression: %s", buildErr))
	}
	return err
}

func build(basename string) error {
	yamlSrc, err := ioutil.ReadFile(basename + ".yaml-tmLanguage")
	if err != nil {
		return err
	}

	var content interface{}
	err = yaml.Unmarshal(yamlSrc, &content)
	if err != nil {
		return err
	}

	// Normalize the value so it's both JSON- and plist-friendly.
	content = prepare(content)

	jsonSrc, err := json.MarshalIndent(content, "", "  ")
	if err != nil {
		return err
	}

	plistSrc, err := plist.MarshalIndent(content, plist.XMLFormat, "  ")
	if err != nil {
		return err
	}

	err = ioutil.WriteFile(basename+".json-tmLanguage", jsonSrc, os.ModePerm)
	if err != nil {
		return err
	}

	err = ioutil.WriteFile(basename+".tmLanguage", plistSrc, os.ModePerm)
	if err != nil {
		return err
	}

	return nil
}

func prepare(v interface{}) interface{} {
	switch tv := v.(type) {

	case map[interface{}]interface{}:
		var ret map[string]interface{}
		if len(tv) == 0 {
			return ret
		}
		ret = make(map[string]interface{}, len(tv))
		for k, v := range tv {
			ret[k.(string)] = prepare(v)
		}
		return ret

	case []interface{}:
		for i := range tv {
			tv[i] = prepare(tv[i])
		}
		return tv

	default:
		return v
	}
}