package client

import (
	"log"
	"os"
	"os/signal"
	"strconv"
	"time"

	"github.com/gorilla/websocket"
	"github.com/inhies/go-bytesize"
	"github.com/mackerelio/go-osstat/memory"
	"github.com/shirou/gopsutil/cpu"
)

func (client *Client) Start() {

	// Setup interrupt handling
	interrupt := make(chan os.Signal, 1)
	signal.Notify(interrupt, os.Interrupt)

	// Connect to the server
	err := client.connectToServer()
	if err != nil {
		log.Fatal("ERROR: Failed connecting to server - ", err)
	}
	defer client.conn.Close()

	// Agent => Manager:
	// - register: register an agent with the manager
	// - update: agents sends CPU and Mem metrics (does this every second)
	// - solution: solution of a task given by the manager
	// - deregister: deregister an agent from the manager

	// Register
	// One-time at the start

	// Fetch the hostname
	hostname, err := os.Hostname()
	if err != nil {
		log.Fatal("ERROR: Failed to register - cannot get hostname - ", err)
	}

	// Fetch the number of logical cores
	cpustats, err := cpu.Counts(true)
	if err != nil {
		log.Fatal("ERROR: Failed to register - unable to fetch number of cores - ", err)
	}

	// Fetch memory stats in bytes
	mem, err := memory.Get()
	if err != nil {
		log.Println("ERROR: Failed to register - fetching memory info - ", err)
	}
	// Use the ByteSize package to allow for memory calculations
	b := bytesize.New(float64(mem.Total))

	// Register this agent with the scheduler
	client.writeToServer("register;" + hostname + ";" + strconv.Itoa(cpustats) + ";" + b.String())

	// Setup updater logic
	// Runs continuesly in the background
	go client.statusUpdater()

	// Start handler for incoming messages
	done := make(chan struct{})
	go client.handleIncoming(done)

	for {
		select {
		case <-done:
			return
		case <-interrupt:
			log.Println("Interrupt received -- Stopping")

			// Dereg the agent before dying off
			client.writeToServer("deregister;" + hostname)

			// Cleanly close the connection by sending a close message and then
			// waiting (with timeout) for the server to close the connection.
			err := client.conn.WriteMessage(websocket.CloseMessage, websocket.FormatCloseMessage(websocket.CloseNormalClosure, ""))
			if err != nil {
				log.Println("ERROR: Close connection - ", err)
				return
			}
			select {
			case <-done:
			case <-time.After(time.Second):
			}
			return
		}
	}

}
