package main

import "log"
import "os"
import "time"
// import "reflect"

// this is the king of dns libraries
import "github.com/miekg/dns"

import "git.wit.com/wit/gui"
import pb "git.wit.com/wit/witProtobuf"
import "git.wit.com/jcarr/dnssecsocket"

import "github.com/gobuffalo/packr"
import "github.com/davecgh/go-spew/spew"

var GITCOMMIT	string	// this is passed in as an ldflag
var GOVERSION	string	// this is passed in as an ldflag
var BUILDTIME	string	// this is passed in as an ldflag
var VERSION	string	// this is passed in as an ldflag

var State	string	// used as a State machine

type myButtonInfo struct {
	Account		*pb.Account	// associated with what account?
	Accounts	[]*pb.Account	// associated with what account?
	VM		*pb.Event_VM	// associated with which VM?
	Custom		func (*gui.GuiButton)
	ADD		func (*gui.GuiButton)
	Name		string
	Action		string
}


// use mergo to merge structs
// import "github.com/imdario/mergo"
// mergo.Merge(&dest, src)

// always sorted slice (new project)
// https://github.com/yaa110/sslice

// several smart slice functions (new project. April 2019)
// https://github.com/elliotchance/pie

// look into this for dns if possible
// https://en.wikipedia.org/wiki/DNSCrypt
// https://en.wikipedia.org/wiki/DNS_over_HTTPS

// DNS over TLS plugin for coredns
// uses protobuf's and gRPC in pb/dns.proto
// https://github.com/coredns/coredns/tree/master/plugin/tls
// https://github.com/coredns/coredns/blob/master/pb/dns.proto

// cross platform openvpn that may work for IPv6
// https://github.com/mysteriumnetwork/go-openvpnwe

func onExit(err error) {
	log.Println("Sleep for 1 second")
	time.Sleep(1 * 1000 * 1000 * 1000)

	// save the protobuf.Config as a JSON config file
	saveConfig()

	if (err != nil) {
		panic(err)
	}

	os.Exit(0)
}

var packrBox packr.Box

func lookupAAAA(hostname string) string {
	// lookup the IP address from DNS
	dnsRR := dnssecsocket.Dnstrace(hostname, "AAAA")
	spew.Dump(dnsRR)
	if (dnsRR == nil) {
		return "BROKEN"
	}
	ipaddr := dns.Field(dnsRR, 1)
	log.Println("ipaddr", ipaddr)
	return ipaddr
}

func main() {
	// This puts all the files in that directory in the binary
	// This directory includes the default config file if there is not already one
	packrBox = packr.NewBox("./resources")

	// This will parse the command line and config file
	// This uses a protobuf definition and then Marshal's
	// and unmarshal's that into a JSON config file
	// that is human readable and editable. This is the easy way
	// to pass around a configuration structure throughout a
	// golang application IMHO
	parseConfig()

	for key, foo := range config.Accounts  {
		log.Println("FOUND ACCOUNT = ", key, foo)
	}

	initChannel()
	go processEvents()

	go gorillaDial("v000185.testing.com.customers.wprod.wit.com:9000")
	go gui.WatchGUI()

	// use this to discover what the OS thinks it's hostname is
	// seems to be cross platform (?)
	// Windows: WMIC computersystem where caption='current_pc_name' rename new_pc_name
	hostname := fqdnGet()
	log.Println("fqdnGet() = ", hostname)
	config.Hostname	= hostname

	// this is a recursive dig for the AAAA record
	// TODO: check for dns hijacking
	ipAAAA := lookupAAAA(hostname)
	config.IPv6		= ipAAAA

	gui.Data.MouseClick     = mainMouseClick

	gui.Config.Width	= int(config.Width)
	gui.Config.Height	= int(config.Height)

	// Set output debugging level
	gui.Config.Debug	= config.Debug
	gui.Config.DebugTable	= config.Debugtable
	log.Println("gui.Config.Debug = ", gui.Config.Debug)
	log.Println("gui.Config.DebugTable = ", gui.Config.DebugTable)

	// Get "Real" User under sudo.
	// More Info: https://stackoverflow.com/q/29733575/402585
	log.Println("Real User: " + os.Getenv("SUDO_USER"))

	// make this the main loop in an attempt to figure out the crashes
	// do not change this until the GUI is stable
	gui.StartNewWindow(false, "Cloud Control Panel", showSplashBox)
}

//
// This is GO language concept for 'recover' to keep an app from completely crashing
//
// Doing this can sometimes avoid a panic() on things like:
// panic: runtime error: slice bounds out of range
//
// In debugging mode, always panic() and never try to recover()
//
func r() {
	if (gui.Config.Debug == false) {
		if r := recover(); r != nil {
			log.Println("recover() SOMETHING IS REALLY BROKEN r =", r)
			log.Println("recover() SOMETHING IS REALLY BROKEN r =", r)
			log.Println("recover() SOMETHING IS REALLY BROKEN r =", r)
			panic("something")
		}
	}
}