package main

import (
	"flag"
	"fmt"
	"go/build"
	"io"
	"log"
	"os"
	"path/filepath"

	"gitee.com/graverchain/rpcx/tool/xgen/parser"
)

var (
	processPkg    = flag.Bool("pkg", false, "process the whole package instead of just the given file")
	specifiedName = flag.String("o", "", "specify the filename of the output")
	buildTags     = flag.String("tags", "", "build tags to add to generated file")
	registry      = flag.String("r", "", "registry type. support etcd, consul, zookeeper, mdns")
)

func main() {
	flag.Parse()

	files := flag.Args()

	if *processPkg {
		if len(flag.Args()) == 0 {
			log.Fatalf("not set packages")
		}
		var pkgFiles []string
		for _, f := range files {
			pkgFiles = append(pkgFiles, filepath.Join(filepath.Join(build.Default.GOPATH, "src", f)))
		}
		files = pkgFiles
	}

	if len(files) == 0 {
		flag.Usage()
		os.Exit(1)
	}

	var parsers []*parser.Parser

	for _, fname := range files {
		fInfo, err := os.Stat(fname)
		if err != nil {
			panic(err)
		}

		if !filepath.IsAbs(fname) {
			fname, err = filepath.Abs(fname)
			if err != nil {
				panic(err)
			}
		}

		relDir, err := filepath.Rel(filepath.Join(build.Default.GOPATH, "src"), fname)
		if err != nil {
			fmt.Printf("provided directory not under GOPATH (%s): %v",
				build.Default.GOPATH, err)
			return
		}

		p := &parser.Parser{PkgFullName: relDir, StructNames: make(map[string]bool)}
		if err := p.Parse(fname, fInfo.IsDir()); err != nil {
			fmt.Printf("Error parsing %v: %v", fname, err)
			return
		}

		parsers = append(parsers, p)
		_ = generate(parsers)
	}

}

func generate(parsers []*parser.Parser) error {
	var w io.Writer
	if *specifiedName == "" {
		w = os.Stdout
	} else {
		f, err := os.Create(*specifiedName)
		if err != nil {
			return err
		}
		defer f.Close()
		w = f
	}

	if *buildTags != "" {
		fmt.Fprintln(w, "// +build ", *buildTags)
		fmt.Fprintln(w)
	}
	fmt.Fprintln(w, "// AUTOGENERATED FILE: rpcx server stub code")
	fmt.Fprintln(w, "// compilable during generation.")
	fmt.Fprintln(w)
	fmt.Fprintln(w, "package main")

	fmt.Fprintln(w)
	fmt.Fprintln(w, "import (")
	fmt.Fprintln(w, `  "flag"`)
	fmt.Fprintln(w, `  "time"`)
	fmt.Fprintln(w)
	fmt.Fprintln(w, `  metrics "github.com/rcrowley/go-metrics"`)
	fmt.Fprintln(w, `  "gitee.com/graverchain/rpcx/server"`)
	fmt.Fprintln(w, `  "gitee.com/graverchain/rpcx/serverplugin"`)

	var importedPackages = make(map[string]bool)
	for _, p := range parsers {
		if !importedPackages[p.PkgFullName] {
			fmt.Fprintln(w, `  "`+p.PkgFullName+`"`)
			importedPackages[p.PkgFullName] = true
		}
	}
	fmt.Fprintln(w, ")")
	fmt.Fprintln(w)

	var mainFn = `
var (
	addr     = flag.String("addr", "localhost:8972", "server address")`
	if *registry == "etcd" || *registry == "consul" || *registry == "zookeeper" {
		mainFn = mainFn + `
	rAddr = flag.String("rAddr", "localhost:2379", "register address")`
	}

	mainFn = mainFn + `
	basePath = flag.String("base", "/rpcx", "prefix path")
)
	
func main() {
	flag.Parse()

	_ = time.Second
	_ = metrics.UseNilMetrics
	_ = serverplugin.GetFunctionName

	s := server.NewServer()
	addRegistryPlugin(s)

	registerServices(s)

	s.Serve("tcp", *addr)
}`
	fmt.Fprintln(w, mainFn)

	fmt.Fprintln(w, `func registerServices(s *server.Server) {`)
	for _, p := range parsers {
		for n := range p.StructNames {
			fmt.Fprintln(w, `	s.Register(new(`+p.PkgName+"."+n+`), "")`)
		}
	}

	fmt.Fprintln(w, `}`)

	useRegistry := false

	fmt.Fprintln(w, `func addRegistryPlugin(s *server.Server) {`)
	switch *registry {
	case "etcd":
		fmt.Fprintln(w, `	// add registery
	r := &serverplugin.EtcdRegisterPlugin{
		ServiceAddress: "tcp@" + *addr,
		EtcdServers:    []string{*rAddr},
		BasePath:       *basePath,
		Metrics:        metrics.NewRegistry(),
		UpdateInterval: time.Minute,
	}`)
		useRegistry = true
	case "consul":
		fmt.Fprintln(w, `	// add registery
	r := &serverplugin.ConsulRegisterPlugin{
		ServiceAddress: "tcp@" + *addr,
		ConsulServers:  []string{*rAddr},
		BasePath:       *basePath,
		Metrics:        metrics.NewRegistry(),
		UpdateInterval: time.Minute,
	}`)
		useRegistry = true
	case "zookeeper":
		fmt.Fprintln(w, `	// add registery
	r := &serverplugin.ZooKeeperRegisterPlugin{
		ServiceAddress:   "tcp@" + *addr,
		ZooKeeperServers: []string{*rAddr},
		BasePath:         *basePath,
		Metrics:          metrics.NewRegistry(),
		UpdateInterval:   time.Minute,
	}`)
		useRegistry = true
	case "mdns":
		fmt.Fprintln(w, `
			r := serverplugin.NewMDNSRegisterPlugin("tcp@"+*addr, 8972, metrics.NewRegistry(), time.Minute, "")`)
		useRegistry = true
	default:
		fmt.Fprintln(w, `
		`)
	}

	if useRegistry {
		fmt.Fprintln(w, `
	err := r.Start()
	if err != nil {
		//log.Fatal(err)
	}
	s.Plugins.Add(r)`)
	}

	fmt.Fprintln(w, `}`)
	return nil
}