package main

import (
	"log"
	"strconv"
	"strings"
	"time"

	"github.com/g3n/engine/app"
	"github.com/g3n/engine/camera"
	"github.com/g3n/engine/core"
	"github.com/g3n/engine/geometry"
	"github.com/g3n/engine/gls"
	"github.com/g3n/engine/graphic"
	"github.com/g3n/engine/gui"
	"github.com/g3n/engine/light"
	"github.com/g3n/engine/material"
	"github.com/g3n/engine/math32"
	"github.com/g3n/engine/renderer"
	"github.com/g3n/engine/util/helper"
	"github.com/g3n/engine/window"
)

var (
	scene            *core.Node
	tableConnections *gui.Table
)

func main() {

	// Create application and scene
	a := app.App(1920, 1080, "Leaky Pool")

	scene = core.NewNode()

	// Set the scene to be managed by the gui manager
	gui.Manager().Set(scene)

	// Create perspective camera
	cam := camera.New(1)
	cam.SetPosition(0, 0, 80)
	scene.Add(cam)

	// Set up orbit control for the camera
	camera.NewOrbitControl(cam)

	// Set up callback to update viewport and camera aspect ratio when the window is resized
	onResize := func(evname string, ev interface{}) {
		// Get framebuffer size and update viewport accordingly
		width, height := a.GetSize()
		a.Gls().Viewport(0, 0, int32(width), int32(height))
		// Update the camera's aspect ratio
		cam.SetAspect(float32(width) / float32(height))
	}
	a.Subscribe(window.OnWindowSize, onResize)
	onResize("", nil)

	// Create a blue torus and add it to the scene
	geom := geometry.NewTorus(1, .4, 12, 32, math32.Pi*2)

	mat := material.NewStandard(math32.NewColor("DarkBlue"))
	mesh := graphic.NewMesh(geom, mat)

	scene.Add(mesh)

	// Create and add lights to the scene
	scene.Add(light.NewAmbient(&math32.Color{1.0, 1.0, 1.0}, 0.8))
	pointLight := light.NewPoint(&math32.Color{1, 1, 1}, 5.0)
	pointLight.SetPosition(1, 0, 2)
	scene.Add(pointLight)

	// Create and add an axis helper to the scene
	scene.Add(helper.NewAxes(0.5))

	// Set background color to gray
	//a.Gls().ClearColor(0.5, 0.5, 0.5, 1.0)
	a.Gls().ClearColor(0, 0, 0, 1.0)

	///////
	//GUI//
	///////

	// Connections Table
	var err error
	tableConnections, err = gui.NewTable(200, 200, []gui.TableColumn{
		{Id: "1", Header: "Local Port", Width: 75, Expand: 0},
		{Id: "2", Header: "Status", Width: 125, Expand: 0},
	})
	if err != nil {
		log.Fatalf("failed to create tableConnections: %v", err)
	}
	tableConnections.SetBorders(1, 1, 1, 1)
	tableConnections.SetPosition(0, 35)
	tableConnections.SetMargins(10, 10, 10, 10)

	editSend := gui.NewEdit(150, "data to transmit...")
	editSend.SetPosition(tableConnections.Position().X+tableConnections.Width()+10, tableConnections.Position().Y+15)
	scene.Add(editSend)

	buttonSendRelease := gui.NewButton("Send and Release")
	buttonSendRelease.SetPosition(editSend.Position().X+editSend.Width()+10, editSend.Position().Y-2)
	buttonSendRelease.Subscribe(gui.OnClick, func(name string, ev interface{}) {
		// Get the selected connection in the table
		// Get the editSend text
		// Call the "client program" function to convert to integer and transmit, then release
		/*	if len(tableConnections.SelectedRows()) != 1 {

					return
				}


				row := tableConnections.Rows(0, -1)[tableConnections.SelectedRows()[0]]
				if row["2"] != "Leased Out" {
					return
				}


			var conn *net.TCPConn

			for i := range dials {
				if dials[i].Connection == nil {
					continue
				}

					if row["1"] != strings.Split(dials[i].Connection.LocalAddr().String(), ":")[1] {
						continue
					}

				conn = dials[i].Connection
				break
			}
			if conn == nil {
				log.Printf("could not match selected table entry to dialed connection")
				return
			}

		*/
		clientProgram(editSend.Text())

	})
	scene.Add(buttonSendRelease)

	labelServer := gui.NewLabel("Server: ")
	labelServer.SetPosition(10, 8)
	scene.Add(labelServer)

	editServer := gui.NewEdit(150, "10.69.71.106:6699")
	editServer.SetText("10.69.71.106:6699")
	editServer.SetPosition(labelServer.Position().X+labelServer.Width()+10, 10)
	scene.Add(editServer)

	labelPoolSize := gui.NewLabel("Pool Size: ")
	labelPoolSize.SetPosition(editServer.Position().X+editServer.Width()+10, 8)
	scene.Add(labelPoolSize)

	editPoolSize := gui.NewEdit(30, "10")
	editPoolSize.SetText("10")
	editPoolSize.SetPosition(labelPoolSize.Position().X+labelPoolSize.Width()+10, 10)
	scene.Add(editPoolSize)

	buttonPoolSize := gui.NewButton("Set")
	buttonPoolSize.SetPosition(editPoolSize.Position().X+editPoolSize.Width()+10, 8)
	buttonPoolSize.Subscribe(gui.OnClick, func(name string, ev interface{}) {
		poolSize, err := strconv.Atoi(editPoolSize.Text())
		if err != nil {
			log.Printf("failed to convert poolSize '%d' to integer: %v", poolSize, err)
			return
		}
		setConnPoolSize(poolSize)
	})
	scene.Add(buttonPoolSize)

	buttonStartPool := gui.NewButton("Start Pool")
	buttonStartPool.SetPosition(buttonPoolSize.Position().X+buttonPoolSize.Width()+10, 8)
	buttonStartPool.Subscribe(gui.OnClick, func(name string, ev interface{}) {
		go connPoolWatchdog(editServer.Text())     // goroutine that keeps the pool full of healthy TCP connections
		buttonStartPool.Label.SetText("Stop Pool") // stop not implemented - need to restart program

	})
	scene.Add(buttonStartPool)

	scene.Add(tableConnections)

	// Run the application
	a.Run(func(renderer *renderer.Renderer, deltaTime time.Duration) {
		a.Gls().Clear(gls.DEPTH_BUFFER_BIT | gls.STENCIL_BUFFER_BIT | gls.COLOR_BUFFER_BIT)

		renderer.Render(scene, cam)
	})
}

// updates the scene based on the status of the dial
// Disconnected, Connected, Leased Out

// Disconnected:
// Connected: dials[i].Mat.SetColor(math32.NewColor("LimeGreen"))
// Leased Out:

func updateSceneConnections() {
	rows := make([]map[string]interface{}, 0, len(dials))
	for i := range dials {
		row := make(map[string]interface{})
		if dials[i].Connection != nil {
			row["1"] = strings.Split(dials[i].Connection.LocalAddr().String(), ":")[1]
		} else {
			row["1"] = "N/A"
		}
		row["2"] = dials[i].Status
		switch dials[i].Status {
		case "Disconnected":
			dials[i].Mat.SetColor(math32.NewColor("Grey"))
		case "Connected":
			dials[i].Mat.SetColor(math32.NewColor("LimeGreen"))

		case "Leased Out":
			dials[i].Mat.SetColor(math32.NewColor("Blue"))

		default:
			log.Printf("connection has unknown status!  Should be one of Disconnected, Connected or Leased Out")
		}

		rows = append(rows, row)
	}

	tableConnections.SetRows(rows)
}
