package main

import "log"
import "fmt"
import "os"
import "time"
import "os/user"
import "runtime"
import "runtime/debug"

import "github.com/gookit/config"
import "github.com/gobuffalo/packr"

// will try to get this hosts FQDN
import "github.com/Showmax/go-fqdn"

// import "github.com/golang/protobuf/proto"
import pb "git.wit.com/wit/witProtobuf"

import "git.wit.com/wit/gui"

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

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

// reminder to use this for JSON
// https://github.com/tidwall/gjson
//   value := gjson.Get(json, "name.last")
//   println(value.String())
//   value := gjson.Get(json, friends.#[last=="Murphy"].first)

// 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

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

	filename := config.String("configfile")
	if (filename == "") {
		log.Println("NOT SAVING CONFIG FILE")
	} else {
		log.Println("SAVING CONFIG FILE AS:", filename)
		f, err := os.Create(filename + ".yaml")
		if err == nil {
			config.DumpTo(f, "yaml")
		}

		f, err = os.Create(filename)
		if err == nil {
			config.DumpTo(f, "json")
		}
	}

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

	os.Exit(0)
}

var packrBox packr.Box

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")
	defaultConfig, _ := packrBox.FindString("cloud.json")

	// This will parse the command line and config file
	parseConfig(defaultConfig)

	// only test the socket code if no GUI
	if (config.String("nogui") == "true") {
		log.Println("Need to re-implement this")
		onExit(nil)
	}

	initChannel()
	go processEvents()

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

	user, err := user.Current()
	if err != nil {
		onExit(err)
	}

	gui.Data.Width		= config.Int("width")
	gui.Data.Height		= config.Int("height")

	// TODO: figure out the hostname the right way
	hostname := fqdn.Get()
	log.Println("fqdn.Get() = ", hostname)
	gui.Data.Hostname	= hostname
	gui.Data.IPv6		= "2604:bbc0:3:3:0:10:0:1004"

	gui.Data.Version	= "v0.6"
	gui.Data.GitCommit	= GITCOMMIT
	gui.Data.GoVersion	= GOVERSION
	gui.Data.Buildtime	= BUILDTIME
	gui.Data.MouseClick     = buttonClick
	gui.Data.HomeDir	= user.HomeDir

	// Set output debugging level
	gui.Data.Debug		= config.Bool("debugging")
	gui.Data.DebugTable	= config.Bool("debugtable")
	log.Println("gui.Data.Debug = ", gui.Data.Debug)
	log.Println("gui.Data.DebugTable = ", gui.Data.DebugTable)

	// Current User
	log.Println("Hi " + user.Name + " (id: " + user.Uid + ")")
	log.Println("Username: " + user.Username)
	log.Println("Home Dir: " + user.HomeDir)

	// 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.GoMainWindow()
}

func buttonClick(b *gui.ButtonMap) {
	log.Println("buttonClick() b =", b)

	if (b == nil) {
		log.Println("main() BACK IN CONTROL PANEL CODE (button is nil)")
	} else {
		log.Println("main() BACK IN CONTROL PANEL CODE (button is ", b.Name, ")")
		log.Println("\tb.Name", b.Name)
		log.Println("\tb.Note", b.Note)
		log.Println("\tb.AccNick", b.AccNick)
		if (b.Note == "BACK") {
			gui.Data.State = "splash"
		}
		if (b.Note == "QUIT") {
			onExit(nil)
		}
		if (b.Note == "CONFIG") {
			log.Println("TRY TO LOAD DEFAULT CONFIG")
			defaultConfig, _ := packrBox.FindString("test.json")
			config.LoadData(defaultConfig)
			log.Println("defaultConfig =", defaultConfig)
			for account, _ := range config.StringMap("accounts") {
                                log.Println("gui.State = splash BUT THERE IS AN ACCOUNT = ", account)
                                log.Println("gui.State = splash BUT THERE IS AN ACCOUNT = ", account)
                                log.Println("SETTING gui.State =", gui.Data.State)
			}
			gui.Data.State = "done"
		}
		if (b.Note == "DEBUG") {
			log.Println("debug.PrintStack() (SHOULD BE JUST THIS goroutine)")
			debug.PrintStack()

			log.Println("ATTEMPT FULL STACK DUMP")
			buf := make([]byte, 1<<16)
			runtime.Stack(buf, true)
			log.Printf("%s", buf)
			log.Println("FINISHED FULL STACK DUMP")
		}
		if (b.Note == "ADD") {
			log.Println("\tSHOULD ADD ACCOUNT HERE")
			if (gui.Data.AccNick != "") {
				log.Println("\tADDING ACCOUNT HERE")
				log.Println("\tADDING ACCOUNT HERE")
				log.Println("\tADDING ACCOUNT HERE")
				log.Println("\tData.AccNick = ", gui.Data.AccNick)
				log.Println("\tData.AccUser = ", gui.Data.AccUser)
				log.Println("\tData.AccPass = ", gui.Data.AccPass)
				config.Set("accounts." + gui.Data.AccNick + ".username",  gui.Data.AccUser)
				config.Set("accounts." + gui.Data.AccNick + ".password",  gui.Data.AccPass)
				config.Set("accounts." + gui.Data.AccNick + ".hostname",  "v000185.testing.com.customers.wprod.wit.com")
			}
		}
		if (b.Note == "LOGIN") {
			log.Println("\tTRIGGER LOGIN ACCOUNT")
			gui.Data.State   = "SEND LOGIN"
			gui.Data.AccNick = b.AccNick
			count := 0
			for {
				log.Println("Sleep() in buttonClick() gui.Data.State =", gui.Data.State)
				time.Sleep(200 * time.Millisecond)
				if (gui.Data.State == "NEW PROTOBUF") {
					if (currentMessage == nil) {
						gui.SocketError()
						gui.Data.State = "done"
					} else {
						log.Println("LOGIN currentMessage =", currentMessage)
						if (currentMessage.Type == pb.Event_OK) {
							log.Println("LOGIN WAS OK!")
							log.Println("LOGIN WAS OK!")
							log.Println("LOGIN WAS OK!")
							msg := "On account " + gui.Data.AccNick + "\n"
							gui.ErrorWindow("Login OK", msg)
						}
						if (currentMessage.Type == pb.Event_FAIL) {
							log.Println("LOGIN FAILED")
							log.Println("LOGIN FAILED")
							log.Println("LOGIN FAILED")
							msg := "On account " + gui.Data.AccNick + "\n"
							msg += "pb.Comment = " + currentMessage.Comment + "\n"
							msg += "pb.Id = " + fmt.Sprintf("%d", currentMessage.Id) + "\n"
							msg += "pb.Email = " + currentMessage.Email + "\n"
							msg += "pb.Username = " + currentMessage.Username + "\n"
							gui.ErrorWindow("Login Failed", msg)
						}
						currentMessage = nil
						gui.Data.State = "done"
					}
					return
				}
				count += 1
				if (count > 10) {
					log.Println("ERROR: waited too long for a resposne")
					currentMessage = nil
					gui.Data.State = "done"
					return
				}
			}
		}
		if (b.Note == "SHOW") {
			log.Println("\tTRIGGER DISPLAY ACCOUNT")
			gui.Data.State   = "SEND WEBSOCKET"
			gui.Data.AccNick = b.AccNick
			count := 0
			for {
				log.Println("Sleep() in buttonClick() gui.Data.State =", gui.Data.State)
				time.Sleep(200 * time.Millisecond)
				if (gui.Data.State == "NEW PROTOBUF") {
					if (currentMessage == nil) {
						gui.SocketError()
						gui.Data.State = "done"
					} else {
						count := countVMS(currentMessage)
						log.Println("SHOW VMS currentMessage =", currentMessage)
						log.Println("SHOW VMS count =", count)
						if (count != 0) {
							name := "Virtual Machines (" + b.AccNick + ")"
							mh := gui.AddVmsTab(name, count)
							ReadReceivedData(currentMessage, mh)
						}
						currentMessage = nil
						gui.Data.State = "done"
					}
					return
				}
				count += 1
				if (count > 10) {
					log.Println("ERROR: waited too long for a resposne")
					currentMessage = nil
					gui.Data.State = "done"
					return
				}
			}
		}
	}
	if (gui.Data.State == "splash") {
		gui.ShowAccountQuestionTab()
		gui.Data.State = "account1"
		return
	} else if (gui.Data.State == "account1") {
		gui.ShowAccountTab()
		gui.Data.State = "main"
	} else if (gui.Data.State == "main") {
		gui.ShowMainTab()
		gui.Data.State = "done"
	} else if (gui.Data.State == "QUIT") {
		onExit(nil)
	}
}

// this watches the GUI primarily to process protobuf's
func watchGUI() {
	count := 0

	for {
		if (count > 10) {
			log.Println("Sleep() in watchGUI() gui.Data.State =", gui.Data.State)
			count = 0
		}
		count += 1
		time.Sleep(200 * time.Millisecond)

		if (gui.Data.State == "SEND WEBSOCKET") {
			log.Println("\tTRIGGERING WEBSOCKET HERE on AccNick =", gui.Data.AccNick)
			log.Println("\tTRIGGERING WEBSOCKET HERE on AccNick =", gui.Data.AccNick)
			log.Println("\tTRIGGERING WEBSOCKET HERE on AccNick =", gui.Data.AccNick)

			event := pb.MakeGetEvent()
			event.Token = config.String("accounts." + gui.Data.AccNick + ".token")
			log.Println("\tTRIGGERING WEBSOCKET HERE with event.Token =", event.Token)

			gorillaSendProtobuf(event)
			gui.Data.State = "READ PROTOBUF"
		}
		if (gui.Data.State == "SEND LOGIN") {
			log.Println("\tTRIGGERING LOGIN HERE on AccNick =", gui.Data.AccNick)
			log.Println("\tTRIGGERING LOGIN HERE on AccNick =", gui.Data.AccNick)
			log.Println("\tTRIGGERING LOGIN HERE on AccNick =", gui.Data.AccNick)

			event := pb.MakeLoginEvent()
			event.Username = config.String("accounts." + gui.Data.AccNick + ".username")
			event.Password = config.String("accounts." + gui.Data.AccNick + ".password")
			event.Token    = config.String("accounts." + gui.Data.AccNick + ".token")
			log.Println("\tTRIGGERING LOGIN HERE with event.Token =", event.Token)

			gorillaSendProtobuf(event)
			gui.Data.State = "READ PROTOBUF"
		}
		if (gui.Data.State == "kill") {
			log.Println("gui.State = kill")
			log.Println("gui.State = kill")
			log.Println("gui.State = kill")
			os.Exit(0)
		}
		if (gui.Data.State == "splash") {
			for account, _ := range config.StringMap("accounts") {
				log.Println("gui.State = splash BUT THERE IS AN ACCOUNT = ", account)
				log.Println("gui.State = splash BUT THERE IS AN ACCOUNT = ", account)
				log.Println("SETTING gui.State = main")
				gui.Data.State = "main";
			}
		}
	}
}