package main

//import (
//	"bufio"
//	"encoding/json"
//	"fmt"
//	"net"
//	"net/http"
//	"os"
//	"strings"
//	"time"
//
//	"code.fbi.h-da.de/danet/gosdn/api/go/gosdn/southbound"
//	"code.fbi.h-da.de/danet/gosdn/api/go/gosdn/transport"
//	"code.fbi.h-da.de/danet/gosdn/controller/api"
//	"code.fbi.h-da.de/danet/gosdn/controller/interfaces/networkdomain"
//	"github.com/google/uuid"
//	dto "github.com/prometheus/client_model/go"
//	"github.com/prometheus/common/expfmt"
//	"github.com/prometheus/prom2json"
//
//	log "github.com/sirupsen/logrus"
//
//	"github.com/spf13/viper"
//)
//
//const plugin = southbound.Type_TYPE_PLUGIN
//const containerised = southbound.Type_TYPE_CONTAINERISED
//
//type experiment struct {
//	execMode   southbound.Type
//	iterations int
//}
//
//type config struct {
//	gnmiTarget string `yaml:"gnmi-target"`
//	ceosTarget string `yaml:"ceos-target"`
//	controller string `yaml:"controller"`
//}
//
//type result struct{}
//
//var sbiID uuid.UUID
//var pndID uuid.UUID
//
//var pnd networkdomain.NetworkDomain
//
//var targets = []string{
//	"clab-thesis-gosdn:8080",
//	"clab-thesis-orchestrator:9338",
//}
//
//var experiments = []experiment{
//	{
//		execMode:   plugin,
//		iterations: 1,
//	},
//	{
//		execMode:   containerised,
//		iterations: 1,
//	},
//	{
//		execMode:   plugin,
//		iterations: 10,
//	},
//	{
//		execMode:   containerised,
//		iterations: 10,
//	},
//	{
//		execMode:   plugin,
//		iterations: 20,
//	},
//	{
//		execMode:   containerised,
//		iterations: 20,
//	},
//	{
//		execMode:   plugin,
//		iterations: 40,
//	},
//	{
//		execMode:   containerised,
//		iterations: 40,
//	},
//	{
//		execMode:   plugin,
//		iterations: 60,
//	},
//	{
//		execMode:   containerised,
//		iterations: 60,
//	},
//	{
//		execMode:   plugin,
//		iterations: 80,
//	},
//	{
//		execMode:   containerised,
//		iterations: 80,
//	},
//}
//
//func newCollector(targets []string, path string) *metricsCollector {
//	f, err := os.Create(path)
//	if err != nil {
//		log.Fatal(err)
//	}
//	return &metricsCollector{
//		targets:  targets,
//		f:        f,
//		ticker:   time.NewTicker(1 * time.Second),
//		mfChan:   make(chan *dto.MetricFamily, 1024),
//		stopChan: make(chan bool),
//	}
//}
//
//type metricsCollector struct {
//	targets  []string
//	f        *os.File
//	stopChan chan bool
//	ticker   *time.Ticker
//	mfChan   chan *dto.MetricFamily
//	results  []*prom2json.Family
//}
//
//func (mc *metricsCollector) start() {
//	go func() {
//		for {
//			select {
//			case <-mc.ticker.C:
//				for _, target := range mc.targets {
//					if err := mc.collect(target); err != nil {
//						log.Error(err)
//					}
//				}
//			case <-mc.stopChan:
//				close(mc.mfChan)
//				return
//			}
//		}
//	}()
//	go func() {
//		for mf := range mc.mfChan {
//			mc.results = append(mc.results, prom2json.NewFamily(mf))
//		}
//	}()
//}
//
//func (mc *metricsCollector) stop() {
//	mc.stopChan <- true
//	writers := make(map[string]*fileWriter)
//	for _, result := range mc.results {
//		writer, ok := writers[result.Name]
//		if !ok {
//			path := fmt.Sprintf("/out/prom-%v-%v.csv", time.Now().UTC().Format(time.RFC3339), result.Name)
//			writer = newFileWriter(path)
//			writers[result.Name] = writer
//		}
//		switch result.Type {
//		case "GAUGE", "COUNTER":
//			if err := writeMetric(writer, result); err != nil {
//				log.Error(err)
//			}
//		case "HISTOGRAM":
//			if err := writeHistogram(writer, result); err != nil {
//				log.Error(err)
//			}
//		default:
//		}
//	}
//	for _, v := range writers {
//		v.close()
//	}
//	jsonText, err := json.Marshal(mc.results)
//	if err != nil {
//		log.Error()
//	}
//
//	n, err := mc.f.Write(jsonText)
//	if err != nil {
//		log.Error(err)
//	}
//	log.WithField("n", n).Info("wrote prom json")
//}
//
//func (mc *metricsCollector) collect(target string) error {
//	resp, err := http.Get("http://" + target + "/metrics")
//	if err != nil {
//		return err
//	}
//	defer resp.Body.Close()
//	var parser expfmt.TextParser
//	mfs, err := parser.TextToMetricFamilies(resp.Body)
//	if err != nil {
//		return err
//	}
//	for k, v := range mfs {
//		if strings.Contains(k, "go_memstats") ||
//			strings.Contains(k, "code_generations_total") ||
//			strings.Contains(k, "duration_seconds") ||
//			strings.Contains(k, "errors") ||
//			strings.Contains(k, "grpc_requests_total") {
//			mc.mfChan <- v
//		}
//	}
//	return nil
//}
//
//func writeMetric(writer *fileWriter, result *prom2json.Family) error {
//	b := strings.Builder{}
//	b.WriteString(result.Name)
//	b.WriteRune(';')
//	b.WriteString(result.Type)
//	for _, m := range result.Metrics {
//		b.WriteRune(';')
//		metric, ok := m.(prom2json.Metric)
//		if !ok {
//			return fmt.Errorf("invalid type assertion")
//		}
//		b.WriteString(metric.TimestampMs)
//		b.WriteRune(';')
//		for k, v := range metric.Labels {
//			b.WriteString(k)
//			b.WriteRune(';')
//			b.WriteString(v)
//			b.WriteRune(';')
//		}
//		b.WriteString(metric.Value)
//	}
//	b.WriteRune('\n')
//	writer.write(b.String())
//	return nil
//}
//
//func writeHistogram(writer *fileWriter, result *prom2json.Family) error {
//	b := strings.Builder{}
//	b.WriteString(result.Name)
//	b.WriteRune(';')
//	b.WriteString(result.Type)
//	for _, m := range result.Metrics {
//		b.WriteRune(';')
//		hist, ok := m.(prom2json.Histogram)
//		if !ok {
//			return fmt.Errorf("invalid type assertion")
//		}
//		b.WriteString(hist.TimestampMs)
//		b.WriteRune(';')
//		for k, v := range hist.Labels {
//			b.WriteString(k)
//			b.WriteRune(';')
//			b.WriteString(v)
//			b.WriteRune(';')
//		}
//		for k, v := range hist.Buckets {
//			b.WriteString(k)
//			b.WriteRune(';')
//			b.WriteString(v)
//			b.WriteRune(';')
//		}
//		b.WriteString(hist.Count)
//		b.WriteRune(';')
//		b.WriteString(hist.Count)
//	}
//	b.WriteRune('\n')
//	writer.write(b.String())
//	return nil
//}
//
//func newFileWriter(path string) *fileWriter {
//	f, err := os.Create(path)
//	if err != nil {
//		log.Fatal(err)
//	}
//	return &fileWriter{
//		f: f,
//		w: bufio.NewWriter(f),
//	}
//}
//
//type fileWriter struct {
//	f *os.File
//	w *bufio.Writer
//}
//
//func (fw *fileWriter) write(out string) {
//	_, err := fw.w.WriteString(out)
//	if err != nil {
//		log.Error(err)
//	}
//}
//
//func (fw *fileWriter) close() {
//	fw.w.Flush()
//	fw.f.Close()
//}
//
//func main() {
//	c, err := readConfig()
//	if err != nil {
//		log.Fatal(err)
//	}
//
//	log.Info("sleeping 10s")
//	time.Sleep(10 * time.Second)
//	if err := api.Init(c.controller); err != nil {
//		log.Fatal(err)
//	}
//
//	pndID = uuid.MustParse(viper.GetString("CLI_PND"))
//	sbiID = uuid.MustParse(viper.GetString("CLI_SBI"))
//
//	pnd, err = api.NewAdapter(pndID.String(), c.controller)
//	if err != nil {
//		log.Fatal(err)
//	}
//
//	addr, err := net.ResolveTCPAddr("tcp", c.ceosTarget)
//	if err != nil {
//		log.Error(err)
//	}
//	var connected bool
//	var conn net.Conn
//	for !connected {
//		conn, err = net.DialTCP("tcp", nil, addr)
//		if err != nil {
//			log.Warn("waiting for cEOS. Retry in 10s...")
//			time.Sleep(10 * time.Second)
//			continue
//		}
//		connected = true
//	}
//	if err := conn.Close(); err != nil {
//		log.Error()
//	}
//
//	for i, exp := range experiments {
//		log.WithFields(log.Fields{
//			"iterations": exp.iterations,
//			"exec":       exp.execMode,
//		}).Infof("starting experiment %v of %v\n", (i + 1), len(experiments))
//		err := executeExperiment(exp, c)
//		if err != nil {
//			log.Error(err)
//		}
//		log.Info("wait 10s for clean up")
//		time.Sleep(10 * time.Second)
//	}
//}
//
//func readConfig() (*config, error) {
//	viper.SetConfigFile("./experiment.yaml")
//	if err := viper.ReadInConfig(); err != nil {
//		return nil, err
//	}
//	fmt.Println("Using config file:", viper.ConfigFileUsed())
//	return &config{
//		gnmiTarget: viper.GetString("gnmi-target"),
//		ceosTarget: viper.GetString("ceos-target"),
//		controller: viper.GetString("controller"),
//	}, nil
//}
//
//func add(opts *transport.TransportOption, writer *fileWriter) {
//	var errs int
//	start := time.Now()
//	if err := pnd.AddDevice("", opts, sbiID); err != nil {
//		log.Error(err)
//		errs++
//	}
//
//	duration := time.Since(start)
//	writer.write(fmt.Sprintf("%v;add;%v;%v;%v;%v\n", time.Now().UnixNano(), opts.Address, opts.Type.String(), duration, errs))
//}
//
//func get(ouid string, opts *transport.TransportOption, writer *fileWriter) {
//	var errs int
//	start := time.Now()
//	_, err := pnd.Request(uuid.MustParse(ouid), "/system/config/hostname")
//	if err != nil {
//		log.Error(err)
//		errs++
//	}
//
//	duration := time.Since(start)
//	writer.write(fmt.Sprintf("%v;get;%v;%v;%v\n", time.Now().UnixNano(), opts.Type.String(), duration, errs))
//}
//
//func delete(ouid string, opts *transport.TransportOption, writer *fileWriter) {
//	var errs int
//	start := time.Now()
//	if err := pnd.RemoveDevice(uuid.MustParse(ouid)); err != nil {
//		log.Error(err)
//		errs++
//	}
//
//	duration := time.Since(start)
//	writer.write(fmt.Sprintf("%v;delete;%v;%v;%v\n", time.Now().UnixNano(), opts.Type.String(), duration, errs))
//}
//
//func executeExperiment(params experiment, c *config) error {
//	expName := fmt.Sprintf("/out/results-%v-%v.csv", params.execMode, params.iterations)
//	writer := newFileWriter(expName)
//	coll := newCollector(targets, fmt.Sprintf("/out/metrics-%v-%v.json", params.execMode, params.iterations))
//	coll.start()
//	defer coll.stop()
//	start := time.Now()
//
//	opts := &transport.TransportOption{
//		Address:  c.ceosTarget,
//		Username: "admin",
//		Password: "admin",
//		Tls:      false,
//		TransportOption: &transport.TransportOption_GnmiTransportOption{
//			GnmiTransportOption: &transport.GnmiTransportOption{},
//		},
//		Type: params.execMode,
//	}
//
//	for i := 0; i < params.iterations; i++ {
//		add(opts, writer)
//	}
//
//	resp, err := api.GetIds(c.controller)
//	if err != nil {
//		return err
//	}
//
//	ondList := resp[0].Ond
//	for _, ond := range ondList {
//		get(ond.Id, opts, writer)
//	}
//
//	for _, ond := range ondList {
//		delete(ond.Id, opts, writer)
//	}
//	duration := time.Since(start)
//	writer.write(fmt.Sprintf("%v;duration;sequential;%v;%v\n", time.Now().UnixNano(), params.iterations, duration))
//	writer.close()
//	log.WithFields(log.Fields{
//		"duration":   duration,
//		"iterations": params.iterations,
//		"exec":       params.execMode,
//	}).Info("experiment ended")
//	return nil
//}
